From d1085c42be246d948a22b9123037a6d93bd089ad Mon Sep 17 00:00:00 2001 From: Yanyan Jiang Date: Thu, 13 Jun 2019 18:41:51 +0000 Subject: [PATCH 001/234] native: add pthread-based MPE --- am/arch/native.mk | 2 +- am/src/native/mpe.c | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/am/arch/native.mk b/am/arch/native.mk index 46732ac3..4fac48bf 100644 --- a/am/arch/native.mk +++ b/am/arch/native.mk @@ -14,7 +14,7 @@ ASFLAGS += -fpie -pie image: @echo + LD "->" $(BINARY_REL) - @g++ -pie -o $(BINARY) -Wl,--whole-archive $(LINK_FILES) -Wl,-no-whole-archive -lSDL2 -lGL -lrt + @g++ -pie -o $(BINARY) -Wl,--whole-archive $(LINK_FILES) -Wl,-no-whole-archive -lSDL2 -lGL -lrt -lpthread run: $(BINARY) diff --git a/am/src/native/mpe.c b/am/src/native/mpe.c index 439686ff..c98c18fc 100644 --- a/am/src/native/mpe.c +++ b/am/src/native/mpe.c @@ -1,21 +1,44 @@ #include +#include +#include +#include +#include +#include + +static int ncpu; +static __thread int cpuid; +static atomic_int cpu_cnt = 0; + +void *thread_wrapper(void *entry) { + cpuid = atomic_fetch_add(&cpu_cnt, 1); + ((void (*)())entry)(); + printf("MP entry should not return\n"); + exit(1); + return NULL; +} int _mpe_init(void (*entry)()) { - entry(); - return 1; + char *smp = getenv("smp"); + ncpu = smp ? atoi(smp) : 1; + assert(0 < ncpu && ncpu <= 16); + + pthread_t threads[ncpu]; + + for (int i = 0; i < ncpu; i++) + pthread_create(&threads[i], NULL, thread_wrapper, entry); + for (int i = 0; i < ncpu; i++) + pthread_join(threads[i], NULL); + exit(1); } int _ncpu() { - return 1; + return ncpu; } int _cpu() { - return 0; + return cpuid; } intptr_t _atomic_xchg(volatile intptr_t *addr, intptr_t newval) { - intptr_t result; - asm volatile ("lock xchg %0, %1": - "+m"(*addr), "=a"(result) : "1"(newval) : "cc"); - return result; + return atomic_exchange((atomic_intptr_t *)addr, newval); } From fce13e2491b0e8fe4dadbb5d955c91f3f2568721 Mon Sep 17 00:00:00 2001 From: Yanyan Jiang Date: Fri, 14 Jun 2019 02:25:52 +0000 Subject: [PATCH 002/234] native: fix bug --- am/src/native/platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/am/src/native/platform.c b/am/src/native/platform.c index c721c222..b5ed0896 100644 --- a/am/src/native/platform.c +++ b/am/src/native/platform.c @@ -34,7 +34,7 @@ static void init_platform() { exit(main(args ? args : "")); // call main here! } -static void exit_platform() __attribute__((constructor)); +static void exit_platform() __attribute__((destructor)); static void exit_platform() { int ret = munmap((void *)PMEM_MAP_START, PMEM_MAP_SIZE); assert(ret == 0); From f3dd5def39fb42ebddba52507a4aa1fd7eaa2e1e Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Sun, 8 Sep 2019 16:33:06 +0800 Subject: [PATCH 003/234] apps,litenes,Makefile: add rules for generating ROM source files * The shell commands are evaluated before rule execution. If we run `make` for the first time, the `$SRCS` will not include the ROM source files, which leads to linking error. * Now we add a rule to first manually generate the ROM source files. --- apps/litenes/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/litenes/Makefile b/apps/litenes/Makefile index 6b4993f6..5a9d47ae 100644 --- a/apps/litenes/Makefile +++ b/apps/litenes/Makefile @@ -1,8 +1,9 @@ NAME := litenes SRCS := $(shell find -L ./src/ -name "*.c") ROMS := $(shell find -L ./src/ -name "*.nes") -PREBUILD := src/roms/gen/roms.h +PREBUILD := rom include $(AM_HOME)/Makefile.app -src/roms/gen/roms.h: $(ROMS) - @cd src/roms && python3 build-roms.py \ No newline at end of file +.PHONY: rom +rom: $(ROMS) + @cd src/roms && python3 build-roms.py From 248c128ffc79acfc16a1f78a59e0541443635ecf Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Thu, 28 Nov 2019 16:33:14 +0800 Subject: [PATCH 004/234] apps: add nesemu2, port to IOE and run on native --- apps/nesemu2/Makefile | 6 + apps/nesemu2/src/cartdb/cartdb.c | 502 ++ apps/nesemu2/src/cartdb/cartdb.h | 31 + apps/nesemu2/src/cartdb/expat/ascii.h | 92 + apps/nesemu2/src/cartdb/expat/asciitab.h | 36 + apps/nesemu2/src/cartdb/expat/expat.h | 1050 +++ .../nesemu2/src/cartdb/expat/expat_external.h | 115 + apps/nesemu2/src/cartdb/expat/iasciitab.h | 37 + apps/nesemu2/src/cartdb/expat/intconfig.h | 19 + apps/nesemu2/src/cartdb/expat/internal.h | 73 + apps/nesemu2/src/cartdb/expat/latin1tab.h | 36 + apps/nesemu2/src/cartdb/expat/nametab.h | 150 + apps/nesemu2/src/cartdb/expat/utf8tab.h | 37 + apps/nesemu2/src/cartdb/expat/xmlparse.c | 6393 +++++++++++++++++ apps/nesemu2/src/cartdb/expat/xmlrole.c | 1324 ++++ apps/nesemu2/src/cartdb/expat/xmlrole.h | 114 + apps/nesemu2/src/cartdb/expat/xmltok.c | 1639 +++++ apps/nesemu2/src/cartdb/expat/xmltok.h | 316 + apps/nesemu2/src/cartdb/expat/xmltok_impl.c | 1783 +++++ apps/nesemu2/src/cartdb/expat/xmltok_impl.h | 46 + apps/nesemu2/src/cartdb/expat/xmltok_ns.c | 115 + apps/nesemu2/src/cartdb/parser.c | 313 + apps/nesemu2/src/cartdb/parser.h | 48 + apps/nesemu2/src/emu/commands.c | 113 + apps/nesemu2/src/emu/commands.h | 62 + apps/nesemu2/src/emu/commands/general.c | 101 + apps/nesemu2/src/emu/commands/nes.c | 184 + apps/nesemu2/src/emu/emu.c | 230 + apps/nesemu2/src/emu/emu.h | 39 + apps/nesemu2/src/emu/events.c | 191 + apps/nesemu2/src/emu/events.h | 65 + apps/nesemu2/src/inputdev/inputdev.c | 66 + apps/nesemu2/src/inputdev/inputdev.h | 71 + apps/nesemu2/src/inputdev/joypad0.c | 79 + apps/nesemu2/src/inputdev/joypad1.c | 79 + apps/nesemu2/src/inputdev/null.c | 23 + apps/nesemu2/src/inputdev/powerpad.c | 60 + apps/nesemu2/src/inputdev/zapper.c | 112 + apps/nesemu2/src/mappers/boards/ave/d-1012.c | 84 + .../nesemu2/src/mappers/boards/ave/nina-001.c | 55 + .../nesemu2/src/mappers/boards/ave/nina-006.c | 58 + .../src/mappers/boards/camerica/bf909x.c | 106 + .../src/mappers/boards/camerica/goldenfive.c | 65 + .../src/mappers/boards/cne/decathlon.c | 60 + apps/nesemu2/src/mappers/boards/cne/fsb.c | 77 + apps/nesemu2/src/mappers/boards/cne/shlz.c | 61 + .../src/mappers/boards/homebrew/dripgame.c | 200 + .../src/mappers/boards/homebrew/magicfloor.c | 37 + .../src/mappers/boards/homebrew/tepples.c | 121 + apps/nesemu2/src/mappers/boards/irem/h3001.c | 125 + .../src/mappers/boards/irem/holydiver.c | 37 + apps/nesemu2/src/mappers/boards/irem/tam-s1.c | 43 + apps/nesemu2/src/mappers/boards/jaleco/jf11.c | 41 + apps/nesemu2/src/mappers/boards/jaleco/jf16.c | 37 + .../src/mappers/boards/jaleco/ss88006.c | 130 + .../nesemu2/src/mappers/boards/kaiser/ks202.c | 127 + apps/nesemu2/src/mappers/boards/konami/vrc1.c | 94 + apps/nesemu2/src/mappers/boards/konami/vrc2.c | 144 + apps/nesemu2/src/mappers/boards/konami/vrc3.c | 117 + apps/nesemu2/src/mappers/boards/konami/vrc4.c | 221 + apps/nesemu2/src/mappers/boards/konami/vrc6.c | 184 + apps/nesemu2/src/mappers/boards/konami/vrc7.c | 222 + apps/nesemu2/src/mappers/boards/namcot/163.c | 29 + apps/nesemu2/src/mappers/boards/namcot/3425.c | 44 + apps/nesemu2/src/mappers/boards/namcot/34x3.c | 68 + .../src/mappers/boards/nintendo/axrom.c | 40 + .../src/mappers/boards/nintendo/bxrom.c | 36 + .../src/mappers/boards/nintendo/cnrom.c | 36 + .../src/mappers/boards/nintendo/cnrom_cp.c | 55 + .../src/mappers/boards/nintendo/cprom.c | 38 + .../src/mappers/boards/nintendo/dxrom.c | 29 + .../src/mappers/boards/nintendo/event.c | 88 + .../src/mappers/boards/nintendo/exrom.c | 29 + .../src/mappers/boards/nintendo/fxrom.c | 24 + .../src/mappers/boards/nintendo/gxrom_mxrom.c | 38 + .../src/mappers/boards/nintendo/hxrom.c | 29 + .../src/mappers/boards/nintendo/nrom.c | 44 + .../src/mappers/boards/nintendo/pal_zz.c | 57 + .../src/mappers/boards/nintendo/pxrom.c | 24 + apps/nesemu2/src/mappers/boards/nintendo/qj.c | 55 + .../src/mappers/boards/nintendo/sxrom.c | 38 + .../src/mappers/boards/nintendo/tqrom.c | 46 + .../src/mappers/boards/nintendo/txrom.c | 29 + .../src/mappers/boards/nintendo/un1rom.c | 37 + .../mappers/boards/nintendo/unrom_74hc08.c | 37 + .../src/mappers/boards/nintendo/uxrom.c | 37 + apps/nesemu2/src/mappers/boards/ntdec/asder.c | 98 + .../src/mappers/boards/ntdec/caltron6in1.c | 57 + .../nesemu2/src/mappers/boards/ntdec/tc-112.c | 68 + .../nesemu2/src/mappers/boards/other/active.c | 83 + .../src/mappers/boards/other/bmc/1200in1.c | 57 + .../src/mappers/boards/other/bmc/150in1.c | 45 + .../src/mappers/boards/other/bmc/15in1.c | 55 + .../src/mappers/boards/other/bmc/20in1.c | 60 + .../src/mappers/boards/other/bmc/21in1.c | 35 + .../src/mappers/boards/other/bmc/22in1.c | 58 + .../src/mappers/boards/other/bmc/31in1.c | 42 + .../src/mappers/boards/other/bmc/35in1.c | 36 + .../src/mappers/boards/other/bmc/36in1.c | 37 + .../src/mappers/boards/other/bmc/65in1.c | 73 + .../src/mappers/boards/other/bmc/70in1.c | 128 + .../src/mappers/boards/other/bmc/72in1.c | 76 + .../src/mappers/boards/other/bmc/76in1.c | 75 + .../mappers/boards/other/bmc/ballgames11in1.c | 78 + .../src/mappers/boards/other/bmc/big7in1.c | 65 + .../boards/other/bmc/contra-function-16.c | 71 + .../src/mappers/boards/other/bmc/fk23c.c | 177 + .../boards/other/bmc/goldengame260in1.c | 51 + .../src/mappers/boards/other/bmc/mario7in1.c | 77 + .../src/mappers/boards/other/bmc/reset4in1.c | 46 + .../src/mappers/boards/other/bmc/super42in1.c | 50 + .../mappers/boards/other/bmc/super700in1.c | 45 + .../src/mappers/boards/other/bmc/supergk.c | 67 + .../mappers/boards/other/bmc/superhik4in1.c | 56 + .../mappers/boards/other/bmc/superhikXin1.c | 67 + .../mappers/boards/other/btl/biomiraclea.c | 104 + .../mappers/boards/other/btl/mario1-malee2.c | 58 + .../src/mappers/boards/other/btl/pikachuy2k.c | 85 + .../src/mappers/boards/other/btl/smb2b.c | 91 + .../src/mappers/boards/other/colordreams.c | 35 + .../src/mappers/boards/other/coolboy.c | 73 + .../src/mappers/boards/other/deathrace.c | 40 + apps/nesemu2/src/mappers/boards/other/hes.c | 59 + .../src/mappers/boards/other/hosenkan.c | 62 + .../src/mappers/boards/other/jycompany.c | 429 ++ .../nesemu2/src/mappers/boards/other/kasing.c | 58 + .../src/mappers/boards/other/magicseries.c | 35 + .../src/mappers/boards/other/nanjing.c | 147 + .../src/mappers/boards/other/nesdisksystem.c | 51 + apps/nesemu2/src/mappers/boards/other/nitra.c | 38 + .../src/mappers/boards/other/opencorp.c | 89 + .../src/mappers/boards/other/rumblestation.c | 65 + .../src/mappers/boards/other/unl/bitcorp.c | 41 + .../src/mappers/boards/other/unl/fs304.c | 104 + .../src/mappers/boards/other/unl/h2288.c | 86 + .../src/mappers/boards/other/unl/racermate.c | 83 + .../mappers/boards/other/unl/superlionking.c | 95 + .../nesemu2/src/mappers/boards/other/unl/uy.c | 41 + .../nesemu2/src/mappers/boards/rexsoft/dbz5.c | 77 + .../src/mappers/boards/rexsoft/sl1632.c | 150 + .../src/mappers/boards/sachen/74ls374n.c | 127 + .../nesemu2/src/mappers/boards/sachen/8259x.c | 32 + .../src/mappers/boards/sachen/sa-003x.c | 77 + .../src/mappers/boards/sachen/sa-0161m.c | 61 + .../src/mappers/boards/sachen/sa-7200x.c | 75 + .../nesemu2/src/mappers/boards/sachen/tca01.c | 43 + .../nesemu2/src/mappers/boards/sachen/tcu01.c | 65 + .../src/mappers/boards/subor/studygame.c | 35 + .../src/mappers/boards/sunsoft/sunsoft-1.c | 42 + .../src/mappers/boards/sunsoft/sunsoft-2.c | 45 + .../src/mappers/boards/sunsoft/sunsoft-3.c | 114 + .../src/mappers/boards/sunsoft/sunsoft-4.c | 103 + .../src/mappers/boards/sunsoft/sunsoft-5b.c | 156 + .../src/mappers/boards/taito/tc0190fmc.c | 24 + .../mappers/boards/taito/tc0190fmc_pal16r4.c | 92 + .../nesemu2/src/mappers/boards/taito/x1-005.c | 133 + .../nesemu2/src/mappers/boards/taito/x1-017.c | 132 + .../src/mappers/boards/tengen/mimic-1.c | 29 + .../src/mappers/boards/tengen/rambo-1.c | 183 + apps/nesemu2/src/mappers/boards/txc/22211.c | 84 + .../nesemu2/src/mappers/boards/txc/mxmdhtwo.c | 68 + .../src/mappers/boards/txc/strikewolf.c | 81 + apps/nesemu2/src/mappers/boards/txc/tw.c | 69 + apps/nesemu2/src/mappers/boards/waixing/ffv.c | 97 + apps/nesemu2/src/mappers/boards/waixing/sgz.c | 140 + apps/nesemu2/src/mappers/boards/waixing/sh2.c | 95 + .../src/mappers/boards/waixing/type-d.c | 48 + .../src/mappers/boards/waixing/type-h.c | 42 + apps/nesemu2/src/mappers/boards/waixing/zs.c | 39 + apps/nesemu2/src/mappers/chips/latch.c | 56 + apps/nesemu2/src/mappers/chips/latch.h | 31 + apps/nesemu2/src/mappers/chips/mmc1.c | 190 + apps/nesemu2/src/mappers/chips/mmc1.h | 46 + apps/nesemu2/src/mappers/chips/mmc2.c | 113 + apps/nesemu2/src/mappers/chips/mmc2.h | 35 + apps/nesemu2/src/mappers/chips/mmc3.c | 296 + apps/nesemu2/src/mappers/chips/mmc3.h | 51 + apps/nesemu2/src/mappers/chips/mmc4.c | 113 + apps/nesemu2/src/mappers/chips/mmc4.h | 31 + apps/nesemu2/src/mappers/chips/mmc5.c | 454 ++ apps/nesemu2/src/mappers/chips/mmc5.h | 33 + apps/nesemu2/src/mappers/chips/namcot-108.c | 80 + apps/nesemu2/src/mappers/chips/namcot-108.h | 33 + apps/nesemu2/src/mappers/chips/namcot-163.c | 191 + apps/nesemu2/src/mappers/chips/namcot-163.h | 47 + apps/nesemu2/src/mappers/chips/sachen-8259.c | 139 + apps/nesemu2/src/mappers/chips/sachen-8259.h | 27 + .../src/mappers/chips/taito-tc0190fmc.c | 84 + .../src/mappers/chips/taito-tc0190fmc.h | 31 + apps/nesemu2/src/mappers/fds/calls.h | 137 + apps/nesemu2/src/mappers/fds/calls/disk.c | 297 + apps/nesemu2/src/mappers/fds/calls/diskutil.c | 76 + apps/nesemu2/src/mappers/fds/calls/misc.c | 69 + apps/nesemu2/src/mappers/fds/calls/pads.c | 139 + apps/nesemu2/src/mappers/fds/calls/ppu.c | 76 + apps/nesemu2/src/mappers/fds/calls/util.c | 190 + apps/nesemu2/src/mappers/fds/calls/vector.c | 237 + apps/nesemu2/src/mappers/fds/calls/vram.c | 391 + apps/nesemu2/src/mappers/fds/fds.c | 630 ++ apps/nesemu2/src/mappers/fds/fds.h | 26 + apps/nesemu2/src/mappers/fds/hle.c | 411 ++ apps/nesemu2/src/mappers/fds/hle.h | 42 + apps/nesemu2/src/mappers/ines.c | 182 + apps/nesemu2/src/mappers/ines20.c | 60 + apps/nesemu2/src/mappers/mapperid.h | 268 + apps/nesemu2/src/mappers/mapperinc.h | 37 + apps/nesemu2/src/mappers/mappers.c | 265 + apps/nesemu2/src/mappers/mappers.h | 45 + apps/nesemu2/src/mappers/nsf/nsf.c | 287 + apps/nesemu2/src/mappers/sound/s_DRIP.c | 142 + apps/nesemu2/src/mappers/sound/s_DRIP.h | 12 + apps/nesemu2/src/mappers/sound/s_FDS.c | 325 + apps/nesemu2/src/mappers/sound/s_FDS.h | 19 + apps/nesemu2/src/mappers/sound/s_FME7.c | 148 + apps/nesemu2/src/mappers/sound/s_FME7.h | 18 + apps/nesemu2/src/mappers/sound/s_MMC5.c | 213 + apps/nesemu2/src/mappers/sound/s_MMC5.h | 19 + apps/nesemu2/src/mappers/sound/s_N106.c | 172 + apps/nesemu2/src/mappers/sound/s_N106.h | 19 + apps/nesemu2/src/mappers/sound/s_VRC6.c | 158 + apps/nesemu2/src/mappers/sound/s_VRC6.h | 18 + apps/nesemu2/src/mappers/sound/s_VRC7.c | 1488 ++++ apps/nesemu2/src/mappers/sound/s_VRC7.h | 18 + apps/nesemu2/src/mappers/unif.c | 203 + apps/nesemu2/src/misc/config.c | 366 + apps/nesemu2/src/misc/config.h | 64 + apps/nesemu2/src/misc/crc32.c | 70 + apps/nesemu2/src/misc/crc32.h | 11 + apps/nesemu2/src/misc/history.c | 90 + apps/nesemu2/src/misc/history.h | 42 + apps/nesemu2/src/misc/log.c | 135 + apps/nesemu2/src/misc/log.h | 30 + apps/nesemu2/src/misc/memfile.c | 256 + apps/nesemu2/src/misc/memfile.h | 64 + apps/nesemu2/src/misc/memutil.c | 214 + apps/nesemu2/src/misc/memutil.h | 40 + apps/nesemu2/src/misc/paths.c | 72 + apps/nesemu2/src/misc/paths.h | 33 + apps/nesemu2/src/misc/slre/slre.c | 879 +++ apps/nesemu2/src/misc/slre/slre.h | 94 + apps/nesemu2/src/misc/strutil.c | 52 + apps/nesemu2/src/misc/strutil.h | 27 + apps/nesemu2/src/misc/vars.c | 260 + apps/nesemu2/src/misc/vars.h | 79 + apps/nesemu2/src/nes/apu/apu.c | 249 + apps/nesemu2/src/nes/apu/apu.h | 60 + apps/nesemu2/src/nes/apu/race.h | 28 + apps/nesemu2/src/nes/apu/race.hh | 68 + apps/nesemu2/src/nes/apu/units/dpcm.h | 35 + apps/nesemu2/src/nes/apu/units/dpcm.hh | 144 + apps/nesemu2/src/nes/apu/units/external.h | 34 + apps/nesemu2/src/nes/apu/units/frame.h | 32 + apps/nesemu2/src/nes/apu/units/frame.hh | 111 + apps/nesemu2/src/nes/apu/units/noise.h | 39 + apps/nesemu2/src/nes/apu/units/noise.hh | 116 + apps/nesemu2/src/nes/apu/units/square.h | 51 + apps/nesemu2/src/nes/apu/units/square.hh | 138 + apps/nesemu2/src/nes/apu/units/triangle.h | 37 + apps/nesemu2/src/nes/apu/units/triangle.hh | 109 + apps/nesemu2/src/nes/cart/cart.c | 324 + apps/nesemu2/src/nes/cart/cart.h | 114 + apps/nesemu2/src/nes/cart/doctor.c | 265 + apps/nesemu2/src/nes/cart/doctor.h | 28 + apps/nesemu2/src/nes/cart/fds.c | 140 + apps/nesemu2/src/nes/cart/fds.h | 28 + apps/nesemu2/src/nes/cart/ines.c | 112 + apps/nesemu2/src/nes/cart/ines.h | 28 + apps/nesemu2/src/nes/cart/ines20.c | 115 + apps/nesemu2/src/nes/cart/ines20.h | 28 + apps/nesemu2/src/nes/cart/nsf.c | 162 + apps/nesemu2/src/nes/cart/nsf.h | 45 + apps/nesemu2/src/nes/cart/patch/ips.c | 64 + apps/nesemu2/src/nes/cart/patch/ips.h | 28 + apps/nesemu2/src/nes/cart/patch/patch.c | 98 + apps/nesemu2/src/nes/cart/patch/patch.h | 33 + apps/nesemu2/src/nes/cart/patch/ups.c | 28 + apps/nesemu2/src/nes/cart/patch/ups.h | 28 + apps/nesemu2/src/nes/cart/unif.c | 204 + apps/nesemu2/src/nes/cart/unif.h | 100 + apps/nesemu2/src/nes/cpu/addrmodes.hh | 211 + apps/nesemu2/src/nes/cpu/cpu.c | 274 + apps/nesemu2/src/nes/cpu/cpu.h | 98 + apps/nesemu2/src/nes/cpu/disassemble.c | 177 + apps/nesemu2/src/nes/cpu/execute.hh | 147 + apps/nesemu2/src/nes/cpu/helper.hh | 145 + apps/nesemu2/src/nes/cpu/opcodes/alu.hh | 129 + apps/nesemu2/src/nes/cpu/opcodes/branch.hh | 86 + apps/nesemu2/src/nes/cpu/opcodes/compare.hh | 48 + apps/nesemu2/src/nes/cpu/opcodes/flag.hh | 54 + apps/nesemu2/src/nes/cpu/opcodes/incdec.hh | 59 + apps/nesemu2/src/nes/cpu/opcodes/loadstore.hh | 52 + apps/nesemu2/src/nes/cpu/opcodes/misc.hh | 35 + apps/nesemu2/src/nes/cpu/opcodes/stack.hh | 109 + apps/nesemu2/src/nes/cpu/opcodes/transfer.hh | 54 + .../src/nes/cpu/opcodes/undocumented.hh | 185 + apps/nesemu2/src/nes/genie.c | 283 + apps/nesemu2/src/nes/genie.h | 29 + apps/nesemu2/src/nes/io.c | 117 + apps/nesemu2/src/nes/io.h | 37 + apps/nesemu2/src/nes/memory.c | 205 + apps/nesemu2/src/nes/memory.h | 106 + apps/nesemu2/src/nes/movie.c | 292 + apps/nesemu2/src/nes/movie.h | 59 + apps/nesemu2/src/nes/nes.c | 317 + apps/nesemu2/src/nes/nes.h | 104 + apps/nesemu2/src/nes/ppu/io.c | 258 + apps/nesemu2/src/nes/ppu/ppu.c | 79 + apps/nesemu2/src/nes/ppu/ppu.h | 116 + apps/nesemu2/src/nes/ppu/step.c | 674 ++ apps/nesemu2/src/nes/ppu/step/calc.hh | 102 + apps/nesemu2/src/nes/ppu/step/draw.hh | 110 + apps/nesemu2/src/nes/ppu/step/fetch.hh | 98 + apps/nesemu2/src/nes/ppu/step/misc.hh | 66 + apps/nesemu2/src/nes/ppu/step/scroll.hh | 68 + apps/nesemu2/src/nes/ppu/step/sprite.hh | 252 + apps/nesemu2/src/nes/ppu/tilecache.c | 107 + apps/nesemu2/src/nes/ppu/tilecache.h | 35 + apps/nesemu2/src/nes/region.c | 62 + apps/nesemu2/src/nes/region.h | 53 + apps/nesemu2/src/nes/state/block.c | 83 + apps/nesemu2/src/nes/state/block.h | 43 + apps/nesemu2/src/nes/state/state.c | 191 + apps/nesemu2/src/nes/state/state.h | 166 + apps/nesemu2/src/palette/generator.c | 171 + apps/nesemu2/src/palette/generator.h | 28 + apps/nesemu2/src/palette/palette.c | 139 + apps/nesemu2/src/palette/palette.h | 41 + apps/nesemu2/src/system/common/filters.c | 95 + apps/nesemu2/src/system/common/filters.h | 73 + .../src/system/common/filters/draw/draw.c | 171 + .../src/system/common/filters/draw/draw.h | 33 + .../common/filters/interpolate/interpolate.c | 81 + .../common/filters/interpolate/interpolate.h | 10 + .../common/filters/ntsc/nes_ntsc/nes_ntsc.h | 192 + .../common/filters/ntsc/nes_ntsc/nes_ntsc.inl | 289 + .../filters/ntsc/nes_ntsc/nes_ntsc_config.h | 27 + .../filters/ntsc/nes_ntsc/nes_ntsc_impl.h | 439 ++ .../src/system/common/filters/ntsc/ntsc.c | 143 + .../src/system/common/filters/ntsc/ntsc.h | 33 + .../system/common/filters/scale2x/scale2x.c | 1464 ++++ .../system/common/filters/scale2x/scale2x.h | 68 + .../system/common/filters/scale2x/scale3x.c | 706 ++ .../system/common/filters/scale2x/scale3x.h | 33 + .../system/common/filters/scale2x/scalebit.c | 516 ++ .../system/common/filters/scale2x/scalebit.h | 54 + apps/nesemu2/src/system/input.h | 58 + apps/nesemu2/src/system/linux/stricmp.c | 16 + apps/nesemu2/src/system/main.h | 32 + apps/nesemu2/src/system/sdl/console/console.c | 335 + apps/nesemu2/src/system/sdl/console/console.h | 32 + apps/nesemu2/src/system/sdl/console/font.c | 60 + apps/nesemu2/src/system/sdl/console/font.h | 32 + .../nesemu2/src/system/sdl/console/fontdata.c | 142 + .../nesemu2/src/system/sdl/console/fontdata.h | 29 + .../src/system/sdl/console/linebuffer.c | 139 + .../src/system/sdl/console/linebuffer.h | 32 + apps/nesemu2/src/system/sdl/input.c | 144 + apps/nesemu2/src/system/sdl/main.c | 178 + apps/nesemu2/src/system/sdl/sound.c | 411 ++ apps/nesemu2/src/system/sdl/system.c | 178 + apps/nesemu2/src/system/sdl/video.c | 406 ++ apps/nesemu2/src/system/sound.h | 31 + apps/nesemu2/src/system/system.h | 36 + apps/nesemu2/src/system/video.h | 40 + apps/nesemu2/src/types.h | 49 + apps/nesemu2/src/version.h | 26 + 366 files changed, 53849 insertions(+) create mode 100644 apps/nesemu2/Makefile create mode 100644 apps/nesemu2/src/cartdb/cartdb.c create mode 100644 apps/nesemu2/src/cartdb/cartdb.h create mode 100644 apps/nesemu2/src/cartdb/expat/ascii.h create mode 100644 apps/nesemu2/src/cartdb/expat/asciitab.h create mode 100644 apps/nesemu2/src/cartdb/expat/expat.h create mode 100644 apps/nesemu2/src/cartdb/expat/expat_external.h create mode 100644 apps/nesemu2/src/cartdb/expat/iasciitab.h create mode 100644 apps/nesemu2/src/cartdb/expat/intconfig.h create mode 100644 apps/nesemu2/src/cartdb/expat/internal.h create mode 100644 apps/nesemu2/src/cartdb/expat/latin1tab.h create mode 100644 apps/nesemu2/src/cartdb/expat/nametab.h create mode 100644 apps/nesemu2/src/cartdb/expat/utf8tab.h create mode 100644 apps/nesemu2/src/cartdb/expat/xmlparse.c create mode 100644 apps/nesemu2/src/cartdb/expat/xmlrole.c create mode 100644 apps/nesemu2/src/cartdb/expat/xmlrole.h create mode 100644 apps/nesemu2/src/cartdb/expat/xmltok.c create mode 100644 apps/nesemu2/src/cartdb/expat/xmltok.h create mode 100644 apps/nesemu2/src/cartdb/expat/xmltok_impl.c create mode 100644 apps/nesemu2/src/cartdb/expat/xmltok_impl.h create mode 100644 apps/nesemu2/src/cartdb/expat/xmltok_ns.c create mode 100644 apps/nesemu2/src/cartdb/parser.c create mode 100644 apps/nesemu2/src/cartdb/parser.h create mode 100644 apps/nesemu2/src/emu/commands.c create mode 100644 apps/nesemu2/src/emu/commands.h create mode 100644 apps/nesemu2/src/emu/commands/general.c create mode 100644 apps/nesemu2/src/emu/commands/nes.c create mode 100644 apps/nesemu2/src/emu/emu.c create mode 100644 apps/nesemu2/src/emu/emu.h create mode 100644 apps/nesemu2/src/emu/events.c create mode 100644 apps/nesemu2/src/emu/events.h create mode 100644 apps/nesemu2/src/inputdev/inputdev.c create mode 100644 apps/nesemu2/src/inputdev/inputdev.h create mode 100644 apps/nesemu2/src/inputdev/joypad0.c create mode 100644 apps/nesemu2/src/inputdev/joypad1.c create mode 100644 apps/nesemu2/src/inputdev/null.c create mode 100644 apps/nesemu2/src/inputdev/powerpad.c create mode 100644 apps/nesemu2/src/inputdev/zapper.c create mode 100644 apps/nesemu2/src/mappers/boards/ave/d-1012.c create mode 100644 apps/nesemu2/src/mappers/boards/ave/nina-001.c create mode 100644 apps/nesemu2/src/mappers/boards/ave/nina-006.c create mode 100644 apps/nesemu2/src/mappers/boards/camerica/bf909x.c create mode 100644 apps/nesemu2/src/mappers/boards/camerica/goldenfive.c create mode 100644 apps/nesemu2/src/mappers/boards/cne/decathlon.c create mode 100644 apps/nesemu2/src/mappers/boards/cne/fsb.c create mode 100644 apps/nesemu2/src/mappers/boards/cne/shlz.c create mode 100644 apps/nesemu2/src/mappers/boards/homebrew/dripgame.c create mode 100644 apps/nesemu2/src/mappers/boards/homebrew/magicfloor.c create mode 100644 apps/nesemu2/src/mappers/boards/homebrew/tepples.c create mode 100644 apps/nesemu2/src/mappers/boards/irem/h3001.c create mode 100644 apps/nesemu2/src/mappers/boards/irem/holydiver.c create mode 100644 apps/nesemu2/src/mappers/boards/irem/tam-s1.c create mode 100644 apps/nesemu2/src/mappers/boards/jaleco/jf11.c create mode 100644 apps/nesemu2/src/mappers/boards/jaleco/jf16.c create mode 100644 apps/nesemu2/src/mappers/boards/jaleco/ss88006.c create mode 100644 apps/nesemu2/src/mappers/boards/kaiser/ks202.c create mode 100644 apps/nesemu2/src/mappers/boards/konami/vrc1.c create mode 100644 apps/nesemu2/src/mappers/boards/konami/vrc2.c create mode 100644 apps/nesemu2/src/mappers/boards/konami/vrc3.c create mode 100644 apps/nesemu2/src/mappers/boards/konami/vrc4.c create mode 100644 apps/nesemu2/src/mappers/boards/konami/vrc6.c create mode 100644 apps/nesemu2/src/mappers/boards/konami/vrc7.c create mode 100644 apps/nesemu2/src/mappers/boards/namcot/163.c create mode 100644 apps/nesemu2/src/mappers/boards/namcot/3425.c create mode 100644 apps/nesemu2/src/mappers/boards/namcot/34x3.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/axrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/bxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/cnrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/cnrom_cp.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/cprom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/dxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/event.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/exrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/fxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/gxrom_mxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/hxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/nrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/pal_zz.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/pxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/qj.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/sxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/tqrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/txrom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/un1rom.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/unrom_74hc08.c create mode 100644 apps/nesemu2/src/mappers/boards/nintendo/uxrom.c create mode 100644 apps/nesemu2/src/mappers/boards/ntdec/asder.c create mode 100644 apps/nesemu2/src/mappers/boards/ntdec/caltron6in1.c create mode 100644 apps/nesemu2/src/mappers/boards/ntdec/tc-112.c create mode 100644 apps/nesemu2/src/mappers/boards/other/active.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/1200in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/150in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/15in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/20in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/21in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/22in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/31in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/35in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/36in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/65in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/70in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/72in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/76in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/ballgames11in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/big7in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/contra-function-16.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/fk23c.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/goldengame260in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/mario7in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/reset4in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/super42in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/super700in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/supergk.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/superhik4in1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/bmc/superhikXin1.c create mode 100644 apps/nesemu2/src/mappers/boards/other/btl/biomiraclea.c create mode 100644 apps/nesemu2/src/mappers/boards/other/btl/mario1-malee2.c create mode 100644 apps/nesemu2/src/mappers/boards/other/btl/pikachuy2k.c create mode 100644 apps/nesemu2/src/mappers/boards/other/btl/smb2b.c create mode 100644 apps/nesemu2/src/mappers/boards/other/colordreams.c create mode 100644 apps/nesemu2/src/mappers/boards/other/coolboy.c create mode 100644 apps/nesemu2/src/mappers/boards/other/deathrace.c create mode 100644 apps/nesemu2/src/mappers/boards/other/hes.c create mode 100644 apps/nesemu2/src/mappers/boards/other/hosenkan.c create mode 100644 apps/nesemu2/src/mappers/boards/other/jycompany.c create mode 100644 apps/nesemu2/src/mappers/boards/other/kasing.c create mode 100644 apps/nesemu2/src/mappers/boards/other/magicseries.c create mode 100644 apps/nesemu2/src/mappers/boards/other/nanjing.c create mode 100644 apps/nesemu2/src/mappers/boards/other/nesdisksystem.c create mode 100644 apps/nesemu2/src/mappers/boards/other/nitra.c create mode 100644 apps/nesemu2/src/mappers/boards/other/opencorp.c create mode 100644 apps/nesemu2/src/mappers/boards/other/rumblestation.c create mode 100644 apps/nesemu2/src/mappers/boards/other/unl/bitcorp.c create mode 100644 apps/nesemu2/src/mappers/boards/other/unl/fs304.c create mode 100644 apps/nesemu2/src/mappers/boards/other/unl/h2288.c create mode 100644 apps/nesemu2/src/mappers/boards/other/unl/racermate.c create mode 100644 apps/nesemu2/src/mappers/boards/other/unl/superlionking.c create mode 100644 apps/nesemu2/src/mappers/boards/other/unl/uy.c create mode 100644 apps/nesemu2/src/mappers/boards/rexsoft/dbz5.c create mode 100644 apps/nesemu2/src/mappers/boards/rexsoft/sl1632.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/74ls374n.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/8259x.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/sa-003x.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/sa-0161m.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/sa-7200x.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/tca01.c create mode 100644 apps/nesemu2/src/mappers/boards/sachen/tcu01.c create mode 100644 apps/nesemu2/src/mappers/boards/subor/studygame.c create mode 100644 apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-1.c create mode 100644 apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-2.c create mode 100644 apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-3.c create mode 100644 apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-4.c create mode 100644 apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-5b.c create mode 100644 apps/nesemu2/src/mappers/boards/taito/tc0190fmc.c create mode 100644 apps/nesemu2/src/mappers/boards/taito/tc0190fmc_pal16r4.c create mode 100644 apps/nesemu2/src/mappers/boards/taito/x1-005.c create mode 100644 apps/nesemu2/src/mappers/boards/taito/x1-017.c create mode 100644 apps/nesemu2/src/mappers/boards/tengen/mimic-1.c create mode 100644 apps/nesemu2/src/mappers/boards/tengen/rambo-1.c create mode 100644 apps/nesemu2/src/mappers/boards/txc/22211.c create mode 100644 apps/nesemu2/src/mappers/boards/txc/mxmdhtwo.c create mode 100644 apps/nesemu2/src/mappers/boards/txc/strikewolf.c create mode 100644 apps/nesemu2/src/mappers/boards/txc/tw.c create mode 100644 apps/nesemu2/src/mappers/boards/waixing/ffv.c create mode 100644 apps/nesemu2/src/mappers/boards/waixing/sgz.c create mode 100644 apps/nesemu2/src/mappers/boards/waixing/sh2.c create mode 100644 apps/nesemu2/src/mappers/boards/waixing/type-d.c create mode 100644 apps/nesemu2/src/mappers/boards/waixing/type-h.c create mode 100644 apps/nesemu2/src/mappers/boards/waixing/zs.c create mode 100644 apps/nesemu2/src/mappers/chips/latch.c create mode 100644 apps/nesemu2/src/mappers/chips/latch.h create mode 100644 apps/nesemu2/src/mappers/chips/mmc1.c create mode 100644 apps/nesemu2/src/mappers/chips/mmc1.h create mode 100644 apps/nesemu2/src/mappers/chips/mmc2.c create mode 100644 apps/nesemu2/src/mappers/chips/mmc2.h create mode 100644 apps/nesemu2/src/mappers/chips/mmc3.c create mode 100644 apps/nesemu2/src/mappers/chips/mmc3.h create mode 100644 apps/nesemu2/src/mappers/chips/mmc4.c create mode 100644 apps/nesemu2/src/mappers/chips/mmc4.h create mode 100644 apps/nesemu2/src/mappers/chips/mmc5.c create mode 100644 apps/nesemu2/src/mappers/chips/mmc5.h create mode 100644 apps/nesemu2/src/mappers/chips/namcot-108.c create mode 100644 apps/nesemu2/src/mappers/chips/namcot-108.h create mode 100644 apps/nesemu2/src/mappers/chips/namcot-163.c create mode 100644 apps/nesemu2/src/mappers/chips/namcot-163.h create mode 100644 apps/nesemu2/src/mappers/chips/sachen-8259.c create mode 100644 apps/nesemu2/src/mappers/chips/sachen-8259.h create mode 100644 apps/nesemu2/src/mappers/chips/taito-tc0190fmc.c create mode 100644 apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h create mode 100644 apps/nesemu2/src/mappers/fds/calls.h create mode 100644 apps/nesemu2/src/mappers/fds/calls/disk.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/diskutil.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/misc.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/pads.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/ppu.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/util.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/vector.c create mode 100644 apps/nesemu2/src/mappers/fds/calls/vram.c create mode 100644 apps/nesemu2/src/mappers/fds/fds.c create mode 100644 apps/nesemu2/src/mappers/fds/fds.h create mode 100644 apps/nesemu2/src/mappers/fds/hle.c create mode 100644 apps/nesemu2/src/mappers/fds/hle.h create mode 100644 apps/nesemu2/src/mappers/ines.c create mode 100644 apps/nesemu2/src/mappers/ines20.c create mode 100644 apps/nesemu2/src/mappers/mapperid.h create mode 100644 apps/nesemu2/src/mappers/mapperinc.h create mode 100644 apps/nesemu2/src/mappers/mappers.c create mode 100644 apps/nesemu2/src/mappers/mappers.h create mode 100644 apps/nesemu2/src/mappers/nsf/nsf.c create mode 100644 apps/nesemu2/src/mappers/sound/s_DRIP.c create mode 100644 apps/nesemu2/src/mappers/sound/s_DRIP.h create mode 100644 apps/nesemu2/src/mappers/sound/s_FDS.c create mode 100644 apps/nesemu2/src/mappers/sound/s_FDS.h create mode 100644 apps/nesemu2/src/mappers/sound/s_FME7.c create mode 100644 apps/nesemu2/src/mappers/sound/s_FME7.h create mode 100644 apps/nesemu2/src/mappers/sound/s_MMC5.c create mode 100644 apps/nesemu2/src/mappers/sound/s_MMC5.h create mode 100644 apps/nesemu2/src/mappers/sound/s_N106.c create mode 100644 apps/nesemu2/src/mappers/sound/s_N106.h create mode 100644 apps/nesemu2/src/mappers/sound/s_VRC6.c create mode 100644 apps/nesemu2/src/mappers/sound/s_VRC6.h create mode 100644 apps/nesemu2/src/mappers/sound/s_VRC7.c create mode 100644 apps/nesemu2/src/mappers/sound/s_VRC7.h create mode 100644 apps/nesemu2/src/mappers/unif.c create mode 100644 apps/nesemu2/src/misc/config.c create mode 100644 apps/nesemu2/src/misc/config.h create mode 100644 apps/nesemu2/src/misc/crc32.c create mode 100644 apps/nesemu2/src/misc/crc32.h create mode 100644 apps/nesemu2/src/misc/history.c create mode 100644 apps/nesemu2/src/misc/history.h create mode 100644 apps/nesemu2/src/misc/log.c create mode 100644 apps/nesemu2/src/misc/log.h create mode 100644 apps/nesemu2/src/misc/memfile.c create mode 100644 apps/nesemu2/src/misc/memfile.h create mode 100644 apps/nesemu2/src/misc/memutil.c create mode 100644 apps/nesemu2/src/misc/memutil.h create mode 100644 apps/nesemu2/src/misc/paths.c create mode 100644 apps/nesemu2/src/misc/paths.h create mode 100644 apps/nesemu2/src/misc/slre/slre.c create mode 100644 apps/nesemu2/src/misc/slre/slre.h create mode 100644 apps/nesemu2/src/misc/strutil.c create mode 100644 apps/nesemu2/src/misc/strutil.h create mode 100644 apps/nesemu2/src/misc/vars.c create mode 100644 apps/nesemu2/src/misc/vars.h create mode 100644 apps/nesemu2/src/nes/apu/apu.c create mode 100644 apps/nesemu2/src/nes/apu/apu.h create mode 100644 apps/nesemu2/src/nes/apu/race.h create mode 100644 apps/nesemu2/src/nes/apu/race.hh create mode 100644 apps/nesemu2/src/nes/apu/units/dpcm.h create mode 100644 apps/nesemu2/src/nes/apu/units/dpcm.hh create mode 100644 apps/nesemu2/src/nes/apu/units/external.h create mode 100644 apps/nesemu2/src/nes/apu/units/frame.h create mode 100644 apps/nesemu2/src/nes/apu/units/frame.hh create mode 100644 apps/nesemu2/src/nes/apu/units/noise.h create mode 100644 apps/nesemu2/src/nes/apu/units/noise.hh create mode 100644 apps/nesemu2/src/nes/apu/units/square.h create mode 100644 apps/nesemu2/src/nes/apu/units/square.hh create mode 100644 apps/nesemu2/src/nes/apu/units/triangle.h create mode 100644 apps/nesemu2/src/nes/apu/units/triangle.hh create mode 100644 apps/nesemu2/src/nes/cart/cart.c create mode 100644 apps/nesemu2/src/nes/cart/cart.h create mode 100644 apps/nesemu2/src/nes/cart/doctor.c create mode 100644 apps/nesemu2/src/nes/cart/doctor.h create mode 100644 apps/nesemu2/src/nes/cart/fds.c create mode 100644 apps/nesemu2/src/nes/cart/fds.h create mode 100644 apps/nesemu2/src/nes/cart/ines.c create mode 100644 apps/nesemu2/src/nes/cart/ines.h create mode 100644 apps/nesemu2/src/nes/cart/ines20.c create mode 100644 apps/nesemu2/src/nes/cart/ines20.h create mode 100644 apps/nesemu2/src/nes/cart/nsf.c create mode 100644 apps/nesemu2/src/nes/cart/nsf.h create mode 100644 apps/nesemu2/src/nes/cart/patch/ips.c create mode 100644 apps/nesemu2/src/nes/cart/patch/ips.h create mode 100644 apps/nesemu2/src/nes/cart/patch/patch.c create mode 100644 apps/nesemu2/src/nes/cart/patch/patch.h create mode 100644 apps/nesemu2/src/nes/cart/patch/ups.c create mode 100644 apps/nesemu2/src/nes/cart/patch/ups.h create mode 100644 apps/nesemu2/src/nes/cart/unif.c create mode 100644 apps/nesemu2/src/nes/cart/unif.h create mode 100644 apps/nesemu2/src/nes/cpu/addrmodes.hh create mode 100644 apps/nesemu2/src/nes/cpu/cpu.c create mode 100644 apps/nesemu2/src/nes/cpu/cpu.h create mode 100644 apps/nesemu2/src/nes/cpu/disassemble.c create mode 100644 apps/nesemu2/src/nes/cpu/execute.hh create mode 100644 apps/nesemu2/src/nes/cpu/helper.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/alu.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/branch.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/compare.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/flag.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/incdec.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/loadstore.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/misc.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/stack.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/transfer.hh create mode 100644 apps/nesemu2/src/nes/cpu/opcodes/undocumented.hh create mode 100644 apps/nesemu2/src/nes/genie.c create mode 100644 apps/nesemu2/src/nes/genie.h create mode 100644 apps/nesemu2/src/nes/io.c create mode 100644 apps/nesemu2/src/nes/io.h create mode 100644 apps/nesemu2/src/nes/memory.c create mode 100644 apps/nesemu2/src/nes/memory.h create mode 100644 apps/nesemu2/src/nes/movie.c create mode 100644 apps/nesemu2/src/nes/movie.h create mode 100644 apps/nesemu2/src/nes/nes.c create mode 100644 apps/nesemu2/src/nes/nes.h create mode 100644 apps/nesemu2/src/nes/ppu/io.c create mode 100644 apps/nesemu2/src/nes/ppu/ppu.c create mode 100644 apps/nesemu2/src/nes/ppu/ppu.h create mode 100644 apps/nesemu2/src/nes/ppu/step.c create mode 100644 apps/nesemu2/src/nes/ppu/step/calc.hh create mode 100644 apps/nesemu2/src/nes/ppu/step/draw.hh create mode 100644 apps/nesemu2/src/nes/ppu/step/fetch.hh create mode 100644 apps/nesemu2/src/nes/ppu/step/misc.hh create mode 100644 apps/nesemu2/src/nes/ppu/step/scroll.hh create mode 100644 apps/nesemu2/src/nes/ppu/step/sprite.hh create mode 100644 apps/nesemu2/src/nes/ppu/tilecache.c create mode 100644 apps/nesemu2/src/nes/ppu/tilecache.h create mode 100644 apps/nesemu2/src/nes/region.c create mode 100644 apps/nesemu2/src/nes/region.h create mode 100644 apps/nesemu2/src/nes/state/block.c create mode 100644 apps/nesemu2/src/nes/state/block.h create mode 100644 apps/nesemu2/src/nes/state/state.c create mode 100644 apps/nesemu2/src/nes/state/state.h create mode 100644 apps/nesemu2/src/palette/generator.c create mode 100644 apps/nesemu2/src/palette/generator.h create mode 100644 apps/nesemu2/src/palette/palette.c create mode 100644 apps/nesemu2/src/palette/palette.h create mode 100644 apps/nesemu2/src/system/common/filters.c create mode 100644 apps/nesemu2/src/system/common/filters.h create mode 100644 apps/nesemu2/src/system/common/filters/draw/draw.c create mode 100644 apps/nesemu2/src/system/common/filters/draw/draw.h create mode 100644 apps/nesemu2/src/system/common/filters/interpolate/interpolate.c create mode 100644 apps/nesemu2/src/system/common/filters/interpolate/interpolate.h create mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.h create mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl create mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h create mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h create mode 100644 apps/nesemu2/src/system/common/filters/ntsc/ntsc.c create mode 100644 apps/nesemu2/src/system/common/filters/ntsc/ntsc.h create mode 100644 apps/nesemu2/src/system/common/filters/scale2x/scale2x.c create mode 100644 apps/nesemu2/src/system/common/filters/scale2x/scale2x.h create mode 100644 apps/nesemu2/src/system/common/filters/scale2x/scale3x.c create mode 100644 apps/nesemu2/src/system/common/filters/scale2x/scale3x.h create mode 100644 apps/nesemu2/src/system/common/filters/scale2x/scalebit.c create mode 100644 apps/nesemu2/src/system/common/filters/scale2x/scalebit.h create mode 100644 apps/nesemu2/src/system/input.h create mode 100644 apps/nesemu2/src/system/linux/stricmp.c create mode 100644 apps/nesemu2/src/system/main.h create mode 100644 apps/nesemu2/src/system/sdl/console/console.c create mode 100644 apps/nesemu2/src/system/sdl/console/console.h create mode 100644 apps/nesemu2/src/system/sdl/console/font.c create mode 100644 apps/nesemu2/src/system/sdl/console/font.h create mode 100644 apps/nesemu2/src/system/sdl/console/fontdata.c create mode 100644 apps/nesemu2/src/system/sdl/console/fontdata.h create mode 100644 apps/nesemu2/src/system/sdl/console/linebuffer.c create mode 100644 apps/nesemu2/src/system/sdl/console/linebuffer.h create mode 100644 apps/nesemu2/src/system/sdl/input.c create mode 100644 apps/nesemu2/src/system/sdl/main.c create mode 100644 apps/nesemu2/src/system/sdl/sound.c create mode 100644 apps/nesemu2/src/system/sdl/system.c create mode 100644 apps/nesemu2/src/system/sdl/video.c create mode 100644 apps/nesemu2/src/system/sound.h create mode 100644 apps/nesemu2/src/system/system.h create mode 100644 apps/nesemu2/src/system/video.h create mode 100644 apps/nesemu2/src/types.h create mode 100644 apps/nesemu2/src/version.h diff --git a/apps/nesemu2/Makefile b/apps/nesemu2/Makefile new file mode 100644 index 00000000..69c28a82 --- /dev/null +++ b/apps/nesemu2/Makefile @@ -0,0 +1,6 @@ +NAME := nesemu2 +SRCS := $(shell find -L ./src/ -name "*.c") + +CFLAGS += -DCPU_UNDOC -DQUICK_SPRITES -DSDL -DLINUX -DRELEASE -Isrc + +include $(AM_HOME)/Makefile.app diff --git a/apps/nesemu2/src/cartdb/cartdb.c b/apps/nesemu2/src/cartdb/cartdb.c new file mode 100644 index 00000000..b27da4f2 --- /dev/null +++ b/apps/nesemu2/src/cartdb/cartdb.c @@ -0,0 +1,502 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "cartdb/cartdb.h" +#include "cartdb/parser.h" +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/config.h" +#include "misc/log.h" +#include "misc/crc32.h" +#include "mappers/mappers.h" +#include "mappers/mapperid.h" + +static xml_t *cartxml = 0; + +int cartdb_init() +{ + char filename[1024]; + char *str,*str2,*p; + xml_t *xml; + + config_get_eval_string(filename,"cartdb.filename"); + if(cartxml) { + return(0); + } + + str = strtok(filename,";"); + while(str != 0) { + str2 = mem_strdup(str); + p = str_eatwhitespace(str2); + if((xml = parser_load(p)) == 0) { + log_printf("cartdb_init: error loading xml cart database '%s'\n",p); + } + else { + log_printf("cartdb_init: loaded xml cart database '%s'\n",p); + if(cartxml == 0) + cartxml = xml; + else + parser_merge(cartxml,&xml); + } + mem_free(str2); + str = strtok(0,";"); + } + + if(cartxml == 0) { + log_printf("cartdb_init: no xml databases loaded, continuing without using cartdb\n"); + return(0); + } + + return(0); +} + +void cartdb_kill() +{ + if(cartxml) { + parser_free(cartxml); + parser_verifymemory(); + } + cartxml = 0; +} + +static char *find_attrib(node_t *node,char *name) +{ + attribute_t *attrib; + + if(node == 0) + return(0); + attrib = node->attributes; + while(attrib) { + if(strcmp(attrib->name,name) == 0) { + return(attrib->data); + } + attrib = attrib->next; + } + return(0); +} + +static u32 hexstr2int(char *str) +{ + u32 ret = 0; + size_t len = strlen(str); + u32 i; + char ch; + + for(i=0;i= 'A' && ch <= 'F') + ch = 10 + ch - 'A'; + else if(ch >= '0' && ch <= '9') + ch = ch - '0'; + else { + log_printf("hexstr2int: invalid character '%c'\n",ch); + return((u32)-1); + } + ret |= ch; + } + return(ret); +} + +static node_t *find_cart(u32 crc32) +{ + node_t *gamenode,*cartnode; + char *str; + u32 n; + + for(gamenode = cartxml->root->child; gamenode; gamenode = gamenode->next) { + if(strcmp(gamenode->name,"game") != 0) + continue; + for(cartnode = gamenode->child; cartnode; cartnode = cartnode->next) { + if(strcmp(cartnode->name,"cartridge") != 0) + continue; + if((str = find_attrib(cartnode,"crc")) != 0) { + n = hexstr2int(str); + if(n == crc32) + return(cartnode); + } + } + } + return(0); +} + +static node_t *get_child(char *name,node_t *parentnode) +{ + node_t *node; + + for(node = parentnode->child; node; node = node->next) { + if(strcmp(node->name,name) == 0) + break; + } + return(node); +} + +static node_t *get_sibling(char *name,node_t *brothernode) +{ + node_t *node; + + for(node = brothernode->next; node; node = node->next) { + if(strcmp(node->name,name) == 0) + break; + } + return(node); +} + +enum pins_e { + P_ERROR = 0, + P_NC, + + P_PRG_A0, P_PRG_A1, P_PRG_A2, P_PRG_A3, P_PRG_A4, P_PRG_A5, P_PRG_A6, P_PRG_A7, + P_PRG_A8, P_PRG_A9, P_PRG_A10, P_PRG_A11, P_PRG_A12, P_PRG_A13, P_PRG_A14, P_PRG_A15, + P_PRG_A16, P_PRG_A17, P_PRG_A18, P_PRG_A19, P_PRG_A20, + + P_CHR_A0, P_CHR_A1, P_CHR_A2, P_CHR_A3, P_CHR_A4, P_CHR_A5, P_CHR_A6, P_CHR_A7, + P_CHR_A8, P_CHR_A9, P_CHR_A10, P_CHR_A11, P_CHR_A12, P_CHR_A13, P_CHR_A14, P_CHR_A15, + P_CHR_A16, P_CHR_A17, P_CHR_A18, P_CHR_A19, P_CHR_A20, + +}; + +static u8 get_pin_func(char *str) +{ + char *p,*tmp = strdup(str); + u8 ret = P_ERROR; + + //make lowercase + for(p = tmp;*p;p++) + *p = tolower(*p); + p = str_eatwhitespace(tmp); + + //not connected + if(strncmp(p,"nc",2) == 0) + ret = P_NC; + + //prg pin + else if(strncmp(p,"prg",3) == 0) { + p += 3; + p = str_eatwhitespace(p); + if(*p == 'a') { + p++; + ret = P_PRG_A0 + atoi(p); + } + } + + //chr pin + else if(strncmp(p,"chr",3) == 0) { + p += 3; + p = str_eatwhitespace(p); + if(*p == 'a') { + p++; + ret = P_CHR_A0 + atoi(p); + } + } + + //unknown function + if(ret == P_ERROR) { + log_printf("get_pin_func: unknown pin function '%s'\n",str); + } + + free(tmp); + return(ret); +} + +//get pin map from the xml nodes +static int get_pin_map(node_t *node,u8 *pins,int maxpins) +{ + node_t *pin; + char *num,*func; + u8 pinnum,pinfunc; + + //clear pins array + memset(pins,P_ERROR,maxpins); + + //get first pin node + pin = get_child("pin",node); + while(pin) { + + //get pin number and function attributes + num = find_attrib(pin,"number"); + func = find_attrib(pin,"function"); + + //get and verify the pin number + pinnum = (u8)atoi(num); + if(pinnum >= maxpins) { + log_printf("get_pin_map: pinnum exceeds maxpins\n"); + continue; + } + + //get and verify the pin function + pinfunc = get_pin_func(func); + if(pinfunc != P_ERROR) + pins[pinnum] = pinfunc; + + //get node sibling for continued processing + pin = get_sibling("pin",pin); + } + return(0); +} + +//process chip nodes +static int process_chip(int mapperid,char *board,int mapper,node_t *node) +{ + char *chip; + u8 pins[64]; + + if((chip = find_attrib(node,"type")) == 0) + return(mapperid); + + log_printf("processing child chip node: %s (mapperid = %d)\n", chip,mapperid); + + //SxROM boards, process mmc1 type + if(mapperid == B_NINTENDO_SxROM) { + if(strncmp(chip,"MMC1A",5) == 0) mapperid = B_NINTENDO_SxROM_MMC1A; + else if(strncmp(chip,"MMC1B",5) == 0) mapperid = B_NINTENDO_SxROM_MMC1B; + else if(strncmp(chip,"MMC1C",5) == 0) mapperid = B_NINTENDO_SxROM_MMC1C; + log_printf("process_chip: mmc1 chip type: %s\n",chip); + } + + //vrc2 boards, find wiring configuration + if (mapperid == B_KONAMI_VRC2) { + if (strncmp(chip, "VRC2", 4) == 0 && get_pin_map(node, pins, 64) == 0) { + if (pins[3] == P_PRG_A0 && pins[4] == P_PRG_A1) { + if (pins[21] == P_NC) { + mapperid = B_KONAMI_VRC2A; + log_printf("process_chip: vrc2a detected.\n"); + } + else { + mapperid = B_KONAMI_VRC4B; + log_printf("process_chip: vrc2c detected, using vrc4b.\n"); + } + } + else if (pins[3] == P_PRG_A1 && pins[4] == P_PRG_A0) { + mapperid = B_KONAMI_VRC2B; + log_printf("process_chip: vrc2b detected.\n"); + } + else + log_printf("process_chip: unknown vrc2 pin configuration (pin3 = %d, pin4 = %d)\n", pins[3], pins[4]); + + } + } + + //vrc4 boards + if (mapperid == B_KONAMI_VRC4) { + if (strncmp(chip, "VRC4", 4) == 0 && get_pin_map(node, pins, 64) == 0) { + if (pins[3] == P_PRG_A7 && pins[4] == P_PRG_A6) { + mapperid = B_KONAMI_VRC4C; + log_printf("process_chip: vrc4c detected.\n"); + } + else if (pins[3] == P_PRG_A0 && pins[4] == P_PRG_A1) { + mapperid = B_KONAMI_VRC4B; + log_printf("process_chip: vrc4b detected.\n"); + } + else if (pins[3] == P_PRG_A2 && pins[4] == P_PRG_A1) { + mapperid = B_KONAMI_VRC4A; + log_printf("process_chip: vrc4a detected.\n"); + } + else if (pins[3] == P_PRG_A2 && pins[4] == P_PRG_A3) { + mapperid = B_KONAMI_VRC4B; + log_printf("process_chip: vrc4d detected.\n"); + } + else if (pins[3] == P_PRG_A3 && pins[4] == P_PRG_A2) { + mapperid = B_KONAMI_VRC4E; + log_printf("process_chip: vrc4e detected.\n"); + } + else { + log_printf("process_chip: unknown vrc4 pin configuration (pin3 = %d, pin4 = %d)\n", pins[3], pins[4]); + } + } + } + + //leave these alone! + if(mapper == 93) { + mapperid = B_SUNSOFT_2; + log_printf("process_chip: sunsoft-3R board. using sunsoft-2 mapper.\n"); + } + + //return new mapperid + return(mapperid); +} + +//finds the correct mapper id using the information given +static int determine_mapperid(cart_t *cart,char *type,char *mapper,node_t *boardnode) +{ + int n,ret = B_UNSUPPORTED; + node_t *node; + + //turn mapper string into mapper integer + n = mapper ? atoi(mapper) : -1; + + //see if this board is supported by the unif mappers + if((ret = mapper_get_mapperid_unif(type ? type : "")) == B_UNSUPPORTED) { + + //check if this ines mapper is supported + if((ret = mapper_get_mapperid_ines(n)) == B_UNSUPPORTED) { + + //not supported, use mapperid from the rom loader maybe it is different + ret = cart->mapperid; + } + else { + log_printf("determine_mapperid: ines mapper %d supported. (board '%s')\n",n,type ? type : ""); + + //save ines -> unif conversions to add to the unif section + {{{{{{{ + FILE *fp = fopen("c:\\mingw\\home\\ines2unf.txt","at"); + + if(fp) { + fprintf(fp,"%d = %s\n",n,type ? type : ""); + fclose(fp); + } + }}}}}}} + + } + } + else + log_printf("determine_mapperid: unif board '%s' supported.\n",type); + + //find the chips used and process them + node = get_child("chip",boardnode); + while(node) { + ret = process_chip(ret,type,n,node); + node = get_sibling("chip",node); + } + + return(ret); +} + +static int sizestr2int(char *str) +{ + char *tmp = strdup(str); + char *p = tmp; + int ret = -1; + + //convert to lowercase + while(*p) { + *p = tolower(*p); + p++; + } + + //search for the k for kilobytes + if((p = strchr(tmp,'k')) != 0) { + *p = 0; + ret = atoi(tmp) * 1024; + } + + //search for the m for megabytes + else if((p = strchr(tmp,'m')) != 0) { + *p = 0; + ret = atoi(tmp) * 1024 * 1024; + } + + //must be just bytes (or gigabytes...) + else { + ret = atoi(tmp); + } + + free(tmp); + return(ret); +} + +static int hasbattery(node_t *node) +{ + char *tmp = find_attrib(node,"battery"); + + if(tmp && strcmp(tmp,"1") == 0) + return(1); + return(0); +} + +int cartdb_find(cart_t *cart) +{ + node_t *cartnode,*boardnode,*node; + u32 crc32,wramsize,vramsize,battery; + char *str,*str2,*tmp; + + if(cartxml == 0) + return(1); + + //calculate crc32 of entire image + crc32 = crc32_block(cart->prg.data,cart->prg.size,0); + crc32 = crc32_block(cart->chr.data,cart->chr.size,crc32); + + //initialize the size and battery vars + wramsize = vramsize = 0; + battery = 0; + + //try to find cart node with same crc32 + cartnode = find_cart(crc32); + if(cartnode) { + + log_printf("cartdb_find: cart found in database.\n"); + + //copy game title + if((str = find_attrib(cartnode->parent,"name"))) + strcpy(cart->title,str); + + //see if board node exists + if((boardnode = get_child("board",cartnode))) { + + //get unif board name and ines mapper number + str = find_attrib(boardnode,"type"); + str2 = find_attrib(boardnode,"mapper"); + + //set mapperid with information discovered + cart->mapperid = determine_mapperid(cart,str,str2,boardnode); + + //find the vram size + node = get_child("vram",boardnode); + while(node) { + tmp = find_attrib(node,"size"); + if(tmp) { + battery |= hasbattery(node) << 1; + vramsize += sizestr2int(tmp); + } + node = get_sibling("vram",node); + } + + //find the wram size and battery status + node = get_child("wram",boardnode); + while(node) { + tmp = find_attrib(node,"size"); + if(tmp) { + battery |= hasbattery(node); + wramsize += sizestr2int(tmp); + } + node = get_sibling("wram",node); + } + + //debug messages + if(wramsize) { + cart_setwramsize(cart,wramsize / 1024); + if(battery & 1) + cart->battery |= 1; + } + if(vramsize) { + cart_setvramsize(cart,vramsize / 1024); + if(battery & 2) + cart->battery |= 2; + } + return(0); + } + } + + log_printf("cartdb_find: cart not found in database.\n"); + return(1); +} diff --git a/apps/nesemu2/src/cartdb/cartdb.h b/apps/nesemu2/src/cartdb/cartdb.h new file mode 100644 index 00000000..eed0b0b1 --- /dev/null +++ b/apps/nesemu2/src/cartdb/cartdb.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cartdb_h__ +#define __cartdb_h__ + +#include "types.h" +#include "nes/cart/cart.h" + +int cartdb_init(); +void cartdb_kill(); +int cartdb_find(cart_t *cart); + +#endif diff --git a/apps/nesemu2/src/cartdb/expat/ascii.h b/apps/nesemu2/src/cartdb/expat/ascii.h new file mode 100644 index 00000000..d10530b0 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/ascii.h @@ -0,0 +1,92 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F +#define ASCII_LPAREN 0x28 +#define ASCII_RPAREN 0x29 +#define ASCII_FF 0x0C +#define ASCII_SLASH 0x2F +#define ASCII_HASH 0x23 +#define ASCII_PIPE 0x7C +#define ASCII_COMMA 0x2C diff --git a/apps/nesemu2/src/cartdb/expat/asciitab.h b/apps/nesemu2/src/cartdb/expat/asciitab.h new file mode 100644 index 00000000..79a15c28 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/asciitab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/apps/nesemu2/src/cartdb/expat/expat.h b/apps/nesemu2/src/cartdb/expat/expat.h new file mode 100644 index 00000000..5e0c6e37 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/expat.h @@ -0,0 +1,1050 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_INCLUDED +#define Expat_INCLUDED 1 + +//force building static library +#define XML_STATIC + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler +#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler +#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler +#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg +#endif + +#include +#include "expat_external.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct XML_ParserStruct; +typedef struct XML_ParserStruct *XML_Parser; + +/* Should this be defined using stdbool.h when C99 is available? */ +typedef unsigned char XML_Bool; +#define XML_TRUE ((XML_Bool) 1) +#define XML_FALSE ((XML_Bool) 0) + +/* The XML_Status enum gives the possible return values for several + API functions. The preprocessor #defines are included so this + stanza can be added to code that still needs to support older + versions of Expat 1.95.x: + + #ifndef XML_STATUS_OK + #define XML_STATUS_OK 1 + #define XML_STATUS_ERROR 0 + #endif + + Otherwise, the #define hackery is quite ugly and would have been + dropped. +*/ +enum XML_Status { + XML_STATUS_ERROR = 0, +#define XML_STATUS_ERROR XML_STATUS_ERROR + XML_STATUS_OK = 1, +#define XML_STATUS_OK XML_STATUS_OK + XML_STATUS_SUSPENDED = 2 +#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +}; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE, + XML_ERROR_UNEXPECTED_STATE, + XML_ERROR_ENTITY_DECLARED_IN_PE, + XML_ERROR_FEATURE_REQUIRES_XML_DTD, + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, + /* Added in 1.95.7. */ + XML_ERROR_UNBOUND_PREFIX, + /* Added in 1.95.8. */ + XML_ERROR_UNDECLARING_PREFIX, + XML_ERROR_INCOMPLETE_PE, + XML_ERROR_XML_DECL, + XML_ERROR_TEXT_DECL, + XML_ERROR_PUBLICID, + XML_ERROR_SUSPENDED, + XML_ERROR_NOT_SUSPENDED, + XML_ERROR_ABORTED, + XML_ERROR_FINISHED, + XML_ERROR_SUSPEND_PE, + /* Added in 2.0. */ + XML_ERROR_RESERVED_PREFIX_XML, + XML_ERROR_RESERVED_PREFIX_XMLNS, + XML_ERROR_RESERVED_NAMESPACE_URI +}; + +enum XML_Content_Type { + XML_CTYPE_EMPTY = 1, + XML_CTYPE_ANY, + XML_CTYPE_MIXED, + XML_CTYPE_NAME, + XML_CTYPE_CHOICE, + XML_CTYPE_SEQ +}; + +enum XML_Content_Quant { + XML_CQUANT_NONE, + XML_CQUANT_OPT, + XML_CQUANT_REP, + XML_CQUANT_PLUS +}; + +/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be + XML_CQUANT_NONE, and the other fields will be zero or NULL. + If type == XML_CTYPE_MIXED, then quant will be NONE or REP and + numchildren will contain number of elements that may be mixed in + and children point to an array of XML_Content cells that will be + all of XML_CTYPE_NAME type with no quantification. + + If type == XML_CTYPE_NAME, then the name points to the name, and + the numchildren field will be zero and children will be NULL. The + quant fields indicates any quantifiers placed on the name. + + CHOICE and SEQ will have name NULL, the number of children in + numchildren and children will point, recursively, to an array + of XML_Content cells. + + The EMPTY, ANY, and MIXED types will only occur at top level. +*/ + +typedef struct XML_cp XML_Content; + +struct XML_cp { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char * name; + unsigned int numchildren; + XML_Content * children; +}; + + +/* This is called for an element declaration. See above for + description of the model argument. It's the caller's responsibility + to free model when finished with it. +*/ +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + +XMLPARSEAPI(void) +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl); + +/* The Attlist declaration handler is called for *each* attribute. So + a single Attlist declaration with multiple attributes declared will + generate multiple calls to this handler. The "default" parameter + may be NULL in the case of the "#IMPLIED" or "#REQUIRED" + keyword. The "isrequired" parameter will be true and the default + value will be NULL in the case of "#REQUIRED". If "isrequired" is + true and default is non-NULL, then this is a "#FIXED" default. +*/ +typedef void (XMLCALL *XML_AttlistDeclHandler) ( + void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired); + +XMLPARSEAPI(void) +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl); + +/* The XML declaration handler is called for *both* XML declarations + and text declarations. The way to distinguish is that the version + parameter will be NULL for text declarations. The encoding + parameter may be NULL for XML declarations. The standalone + parameter will be -1, 0, or 1 indicating respectively that there + was no standalone parameter in the declaration, that it was given + as no, or that it was given as yes. +*/ +typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); + +XMLPARSEAPI(void) +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler xmldecl); + + +typedef struct { + void *(*malloc_fcn)(size_t size); + void *(*realloc_fcn)(void *ptr, size_t size); + void (*free_fcn)(void *ptr); +} XML_Memory_Handling_Suite; + +/* Constructs a new parser; encoding is the encoding specified by the + external protocol or NULL if there is none specified. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type + names and attribute names that belong to a namespace will be + expanded; unprefixed attribute names are never expanded; unprefixed + element type names are expanded only if there is a default + namespace. The expanded name is the concatenation of the namespace + URI, the namespace separator character, and the local part of the + name. If the namespace separator is '\0' then the namespace URI + and the local part will be concatenated without any separator. + It is a programming error to use the separator '\0' with namespace + triplets (see XML_SetReturnNSTriplet). +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* Constructs a new parser using the memory management suite referred to + by memsuite. If memsuite is NULL, then use the standard library memory + suite. If namespaceSeparator is non-NULL it creates a parser with + namespace processing as described above. The character pointed at + will serve as the namespace separator. + + All further memory operations used for the created parser will come from + the given suite. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate_MM(const XML_Char *encoding, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + +/* Prepare a parser object to be re-used. This is particularly + valuable when memory allocation overhead is disproportionatly high, + such as when a large number of small documnents need to be parsed. + All handlers are cleared from the parser, except for the + unknownEncodingHandler. The parser's external state is re-initialized + except for the values of ns and ns_triplets. + + Added in Expat 1.95.3. +*/ +XMLPARSEAPI(XML_Bool) +XML_ParserReset(XML_Parser parser, const XML_Char *encoding); + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. +*/ +typedef void (XMLCALL *XML_StartElementHandler) (void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (XMLCALL *XML_EndElementHandler) (void *userData, + const XML_Char *name); + + +/* s is not 0 terminated. */ +typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( + void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (XMLCALL *XML_CommentHandler) (void *userData, + const XML_Char *data); + +typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); +typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); + +/* This is called for any characters in the XML document for which + there is no applicable handler. This includes both characters that + are part of markup which is of a kind that is not reported + (comments, markup declarations), or characters that are part of a + construct which could be reported but for which no handler has been + supplied. The characters are passed exactly as they were in the XML + document except that they will be encoded in UTF-8 or UTF-16. + Line boundaries are not normalized. Note that a byte order mark + character is not passed to the default handler. There are no + guarantees about how characters are divided between calls to the + default handler: for example, a comment might be split between + multiple calls. +*/ +typedef void (XMLCALL *XML_DefaultHandler) (void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration, before + any DTD or internal subset is parsed. +*/ +typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( + void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); + +/* This is called for the start of the DOCTYPE declaration when the + closing > is encountered, but after processing any external + subset. +*/ +typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for entity declarations. The is_parameter_entity + argument will be non-zero if the entity is a parameter entity, zero + otherwise. + + For internal entities (), value will + be non-NULL and systemId, publicID, and notationName will be NULL. + The value string is NOT nul-terminated; the length is provided in + the value_length argument. Since it is legal to have zero-length + values, do not use this argument to test for internal entities. + + For external entities, value will be NULL and systemId will be + non-NULL. The publicId argument will be NULL unless a public + identifier was provided. The notationName argument will have a + non-NULL value only for unparsed entity declarations. + + Note that is_parameter_entity can't be changed to XML_Bool, since + that would break binary compatibility. +*/ +typedef void (XMLCALL *XML_EntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +XMLPARSEAPI(void) +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler); + +/* OBSOLETE -- OBSOLETE -- OBSOLETE + This handler has been superceded by the EntityDeclHandler above. + It is provided here for backward compatibility. + + This is called for a declaration of an unparsed (NDATA) entity. + The base argument is whatever was set by XML_SetBase. The + entityName, systemId and notationName arguments will never be + NULL. The other arguments may be. +*/ +typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. The base argument is + whatever was set by XML_SetBase. The notationName will never be + NULL. The other arguments can be. +*/ +typedef void (XMLCALL *XML_NotationDeclHandler) ( + void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for + each namespace declaration. The call to the start and end element + handlers occur between the calls to the start and end namespace + declaration handlers. For an xmlns attribute, prefix will be + NULL. For an xmlns="" attribute, uri will be NULL. +*/ +typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone, that is, it has an + external subset or a reference to a parameter entity, but does not + have standalone="yes". If this handler returns XML_STATUS_ERROR, + then processing will not continue, and the parser will return a + XML_ERROR_NOT_STANDALONE error. + If parameter entity parsing is enabled, then in addition to the + conditions above this handler will only be called if the referenced + entity was actually read. +*/ +typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); + +/* This is called for a reference to an external parsed general + entity. The referenced entity is not automatically parsed. The + application can parse it immediately or later using + XML_ExternalEntityParserCreate. + + The parser argument is the parser parsing the entity containing the + reference; it can be passed as the parser argument to + XML_ExternalEntityParserCreate. The systemId argument is the + system identifier as specified in the entity declaration; it will + not be NULL. + + The base argument is the system identifier that should be used as + the base for resolving systemId if systemId was relative; this is + set by XML_SetBase; it may be NULL. + + The publicId argument is the public identifier as specified in the + entity declaration, or NULL if none was specified; the whitespace + in the public identifier will have been normalized as required by + the XML spec. + + The context argument specifies the parsing context in the format + expected by the context argument to XML_ExternalEntityParserCreate; + context is valid only until the handler returns, so if the + referenced entity is to be parsed later, it must be copied. + context is NULL only when the entity is a parameter entity. + + The handler should return XML_STATUS_ERROR if processing should not + continue because of a fatal error in the handling of the external + entity. In this case the calling parser will return an + XML_ERROR_EXTERNAL_ENTITY_HANDLING error. + + Note that unlike other handlers the first argument is the parser, + not userData. +*/ +typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( + XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This is called in two situations: + 1) An entity reference is encountered for which no declaration + has been read *and* this is not an error. + 2) An internal entity reference is read, but not expanded, because + XML_SetDefaultHandler has been called. + Note: skipped parameter entities in declarations and skipped general + entities in attribute values cannot be reported, because + the event would be out of sync with the reporting of the + declarations or attribute values +*/ +typedef void (XMLCALL *XML_SkippedEntityHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity); + +/* This structure is filled in by the XML_UnknownEncodingHandler to + provide information to the parser about encodings that are unknown + to the parser. + + The map[b] member gives information about byte sequences whose + first byte is b. + + If map[b] is c where c is >= 0, then b by itself encodes the + Unicode scalar value c. + + If map[b] is -1, then the byte sequence is malformed. + + If map[b] is -n, where n >= 2, then b is the first byte of an + n-byte sequence that encodes a single Unicode scalar value. + + The data member will be passed as the first argument to the convert + function. + + The convert function is used to convert multibyte sequences; s will + point to a n-byte sequence where map[(unsigned char)*s] == -n. The + convert function must return the Unicode scalar value represented + by this byte sequence or -1 if the byte sequence is malformed. + + The convert function may be NULL if the encoding is a single-byte + encoding, that is if map[b] >= -1 for all bytes b. + + When the parser is finished with the encoding, then if release is + not NULL, it will call release passing it the data member; once + release has been called, the convert function will not be called + again. + + Expat places certain restrictions on the encodings that are supported + using this mechanism. + + 1. Every ASCII character that can appear in a well-formed XML document, + other than the characters + + $@\^`{}~ + + must be represented by a single byte, and that byte must be the + same byte that represents that character in ASCII. + + 2. No character may require more than 4 bytes to encode. + + 3. All characters encoded must have Unicode scalar values <= + 0xFFFF, (i.e., characters that would be encoded by surrogates in + UTF-16 are not allowed). Note that this restriction doesn't + apply to the built-in support for UTF-8 and UTF-16. + + 4. No Unicode character may be encoded by more than one distinct + sequence of bytes. +*/ +typedef struct { + int map[256]; + void *data; + int (XMLCALL *convert)(void *data, const char *s); + void (XMLCALL *release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. + + The encodingHandlerData argument is that which was passed as the + second argument to XML_SetUnknownEncodingHandler. + + The name argument gives the name of the encoding as specified in + the encoding declaration. + + If the callback can provide information about the encoding, it must + fill in the XML_Encoding structure, and return XML_STATUS_OK. + Otherwise it must return XML_STATUS_ERROR. + + If info does not describe a suitable encoding, then the parser will + return an XML_UNKNOWN_ENCODING error. +*/ +typedef int (XMLCALL *XML_UnknownEncodingHandler) ( + void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +XMLPARSEAPI(void) +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +XMLPARSEAPI(void) +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler handler); + +XMLPARSEAPI(void) +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler handler); + +XMLPARSEAPI(void) +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +XMLPARSEAPI(void) +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +XMLPARSEAPI(void) +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +XMLPARSEAPI(void) +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +XMLPARSEAPI(void) +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start); + +XMLPARSEAPI(void) +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of + internal entities. These entity references will be passed to the + default handler, or to the skipped entity handler, if one is set. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of + internal entities. The entity reference will not be passed to the + default handler. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +XMLPARSEAPI(void) +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-NULL value for arg is specified here, then it will be + passed as the first argument to the external entity ref handler + instead of the parser object. +*/ +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, + void *arg); + +XMLPARSEAPI(void) +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler); + +XMLPARSEAPI(void) +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end + element, processing instruction or character data. It causes the + corresponding markup to be passed to the default handler. +*/ +XMLPARSEAPI(void) +XML_DefaultCurrent(XML_Parser parser); + +/* If do_nst is non-zero, and namespace processing is in effect, and + a name has a prefix (i.e. an explicit namespace qualifier) then + that name is returned as a triplet in a single string separated by + the separator character specified when the parser was created: URI + + sep + local_name + sep + prefix. + + If do_nst is zero, then namespace information is returned in the + default manner (URI + sep + local_name) whether or not the name + has a prefix. + + Note: Calling XML_SetReturnNSTriplet after XML_Parse or + XML_ParseBuffer has no effect. +*/ + +XMLPARSEAPI(void) +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); + +/* This value is passed as the userData argument to callbacks. */ +XMLPARSEAPI(void) +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or NULL. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument to + XML_ParserCreate. On success XML_SetEncoding returns non-zero, + zero otherwise. + Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer + has no effect and returns XML_STATUS_ERROR. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed as the + first argument to callbacks instead of userData. The userData will + still be accessible using XML_GetUserData. +*/ +XMLPARSEAPI(void) +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* If useDTD == XML_TRUE is passed to this function, then the parser + will assume that there is an external subset, even if none is + specified in the document. In such a case the parser will call the + externalEntityRefHandler with a value of NULL for the systemId + argument (the publicId and context arguments will be NULL as well). + Note: For the purpose of checking WFC: Entity Declared, passing + useDTD == XML_TRUE will make the parser behave as if the document + had a DTD with an external subset. + Note: If this function is called, then this must be done before + the first call to XML_Parse or XML_ParseBuffer, since it will + have no effect after that. Returns + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. + Note: If the document does not have a DOCTYPE declaration at all, + then startDoctypeDeclHandler and endDoctypeDeclHandler will not + be called, despite an external subset being parsed. + Note: If XML_DTD is not defined when Expat is compiled, returns + XML_ERROR_FEATURE_REQUIRES_XML_DTD. +*/ +XMLPARSEAPI(enum XML_Error) +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); + + +/* Sets the base to be used for resolving relative URIs in system + identifiers in declarations. Resolving relative identifiers is + left to the application: this value will be passed through as the + base argument to the XML_ExternalEntityRefHandler, + XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base + argument will be copied. Returns XML_STATUS_ERROR if out of memory, + XML_STATUS_OK otherwise. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetBase(XML_Parser parser, const XML_Char *base); + +XMLPARSEAPI(const XML_Char *) +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call + to the XML_StartElementHandler that were specified in the start-tag + rather than defaulted. Each attribute/value pair counts as 2; thus + this correspondds to an index into the atts array passed to the + XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to + XML_StartElementHandler, or -1 if there is no ID attribute. Each + attribute/value pair counts as 2; thus this correspondds to an + index into the atts array passed to the XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetIdAttributeIndex(XML_Parser parser); + +#ifdef XML_ATTR_INFO +/* Source file byte offsets for the start and end of attribute names and values. + The value indices are exclusive of surrounding quotes; thus in a UTF-8 source + file an attribute value of "blah" will yield: + info->valueEnd - info->valueStart = 4 bytes. +*/ +typedef struct { + XML_Index nameStart; /* Offset to beginning of the attribute name. */ + XML_Index nameEnd; /* Offset after the attribute name's last byte. */ + XML_Index valueStart; /* Offset to beginning of the attribute value. */ + XML_Index valueEnd; /* Offset after the attribute value's last byte. */ +} XML_AttrInfo; + +/* Returns an array of XML_AttrInfo structures for the attribute/value pairs + passed in last call to the XML_StartElementHandler that were specified + in the start-tag rather than defaulted. Each attribute/value pair counts + as 1; thus the number of entries in the array is + XML_GetSpecifiedAttributeCount(parser) / 2. +*/ +XMLPARSEAPI(const XML_AttrInfo *) +XML_GetAttributeInfo(XML_Parser parser); +#endif + +/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is + detected. The last call to XML_Parse must have isFinal true; len + may be zero for this call (or any other). + + Though the return values for these functions has always been + described as a Boolean value, the implementation, at least for the + 1.95.x series, has always returned exactly one of the XML_Status + values. +*/ +XMLPARSEAPI(enum XML_Status) +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +XMLPARSEAPI(void *) +XML_GetBuffer(XML_Parser parser, int len); + +XMLPARSEAPI(enum XML_Status) +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. + Must be called from within a call-back handler, except when aborting + (resumable = 0) an already suspended parser. Some call-backs may + still follow because they would otherwise get lost. Examples: + - endElementHandler() for empty elements when stopped in + startElementHandler(), + - endNameSpaceDeclHandler() when stopped in endElementHandler(), + and possibly others. + + Can be called from most handlers, including DTD related call-backs, + except when parsing an external parameter entity and resumable != 0. + Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. + Possible error codes: + - XML_ERROR_SUSPENDED: when suspending an already suspended parser. + - XML_ERROR_FINISHED: when the parser has already finished. + - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. + + When resumable != 0 (true) then parsing is suspended, that is, + XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. + Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() + return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. + + *Note*: + This will be applied to the current parser instance only, that is, if + there is a parent parser then it will continue parsing when the + externalEntityRefHandler() returns. It is up to the implementation of + the externalEntityRefHandler() to call XML_StopParser() on the parent + parser (recursively), if one wants to stop parsing altogether. + + When suspended, parsing can be resumed by calling XML_ResumeParser(). +*/ +XMLPARSEAPI(enum XML_Status) +XML_StopParser(XML_Parser parser, XML_Bool resumable); + +/* Resumes parsing after it has been suspended with XML_StopParser(). + Must not be called from within a handler call-back. Returns same + status codes as XML_Parse() or XML_ParseBuffer(). + Additional error code XML_ERROR_NOT_SUSPENDED possible. + + *Note*: + This must be called on the most deeply nested child parser instance + first, and on its parent parser only after the child parser has finished, + to be applied recursively until the document entity's parser is restarted. + That is, the parent parser will not resume by itself and it is up to the + application to call XML_ResumeParser() on it at the appropriate moment. +*/ +XMLPARSEAPI(enum XML_Status) +XML_ResumeParser(XML_Parser parser); + +enum XML_Parsing { + XML_INITIALIZED, + XML_PARSING, + XML_FINISHED, + XML_SUSPENDED +}; + +typedef struct { + enum XML_Parsing parsing; + XML_Bool finalBuffer; +} XML_ParsingStatus; + +/* Returns status of parser with respect to being initialized, parsing, + finished, or suspended and processing the final buffer. + XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, + XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED +*/ +XMLPARSEAPI(void) +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); + +/* Creates an XML_Parser object that can parse an external general + entity; context is a '\0'-terminated string specifying the parse + context; encoding is a '\0'-terminated string giving the name of + the externally specified encoding, or NULL if there is no + externally specified encoding. The context string consists of a + sequence of tokens separated by formfeeds (\f); a token consisting + of a name specifies that the general entity of the name is open; a + token of the form prefix=uri specifies the namespace for a + particular prefix; a token of the form =uri specifies the default + namespace. This can be called at any point after the first call to + an ExternalEntityRefHandler so longer as the parser has not yet + been freed. The new parser is completely independent and may + safely be used in a separate thread. The handlers and userData are + initialized from the parser argument. Returns NULL if out of memory. + Otherwise returns a new XML_Parser object. +*/ +XMLPARSEAPI(XML_Parser) +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD + subset). If parsing of parameter entities is enabled, then + references to external parameter entities (including the external + DTD subset) will be passed to the handler set with + XML_SetExternalEntityRefHandler. The context passed will be 0. + + Unlike external general entities, external parameter entities can + only be parsed synchronously. If the external parameter entity is + to be parsed, it must be parsed during the call to the external + entity ref handler: the complete sequence of + XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and + XML_ParserFree calls must be made during this call. After + XML_ExternalEntityParserCreate has been called to create the parser + for the external parameter entity (context must be 0 for this + call), it is illegal to make any calls on the old parser until + XML_ParserFree has been called on the newly created parser. + If the library has been compiled without support for parameter + entity parsing (ie without XML_DTD being defined), then + XML_SetParamEntityParsing will return 0 if parsing of parameter + entities is requested; otherwise it will return non-zero. + Note: If XML_SetParamEntityParsing is called after XML_Parse or + XML_ParseBuffer, then it has no effect and will always return 0. +*/ +XMLPARSEAPI(int) +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +/* Sets the hash salt to use for internal hash calculations. + Helps in preventing DoS attacks based on predicting hash + function behavior. This must be called before parsing is started. + Returns 1 if successful, 0 when called after parsing has started. +*/ +XMLPARSEAPI(int) +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt); + +/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then + XML_GetErrorCode returns information about the error. +*/ +XMLPARSEAPI(enum XML_Error) +XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse + location. They may be called from any callback called to report + some parse event; in this case the location is the location of the + first of the sequence of characters that generated the event. When + called from callbacks generated by declarations in the document + prologue, the location identified isn't as neatly defined, but will + be within the relevant markup. When called outside of the callback + functions, the position indicated will be just past the last parse + event (regardless of whether there was an associated callback). + + They may also be called after returning from a call to XML_Parse + or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then + the location is the location of the character at which the error + was detected; otherwise the location is the location of the last + parse event, as described above. +*/ +XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. + Returns 0 if the event is in an internal entity. +*/ +XMLPARSEAPI(int) +XML_GetCurrentByteCount(XML_Parser parser); + +/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets + the integer pointed to by offset to the offset within this buffer + of the current parse position, and sets the integer pointed to by size + to the size of this buffer (the number of input bytes). Otherwise + returns a NULL pointer. Also returns a NULL pointer if a parse isn't + active. + + NOTE: The character pointer returned should not be used outside + the handler that makes the call. +*/ +XMLPARSEAPI(const char *) +XML_GetInputContext(XML_Parser parser, + int *offset, + int *size); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees the content model passed to the element declaration handler */ +XMLPARSEAPI(void) +XML_FreeContentModel(XML_Parser parser, XML_Content *model); + +/* Exposing the memory handling functions used in Expat */ +XMLPARSEAPI(void *) +XML_MemMalloc(XML_Parser parser, size_t size); + +XMLPARSEAPI(void *) +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); + +XMLPARSEAPI(void) +XML_MemFree(XML_Parser parser, void *ptr); + +/* Frees memory used by the parser. */ +XMLPARSEAPI(void) +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +XMLPARSEAPI(const XML_LChar *) +XML_ErrorString(enum XML_Error code); + +/* Return a string containing the version number of this expat */ +XMLPARSEAPI(const XML_LChar *) +XML_ExpatVersion(void); + +typedef struct { + int major; + int minor; + int micro; +} XML_Expat_Version; + +/* Return an XML_Expat_Version structure containing numeric version + number information for this version of expat. +*/ +XMLPARSEAPI(XML_Expat_Version) +XML_ExpatVersionInfo(void); + +/* Added in Expat 1.95.5. */ +enum XML_FeatureEnum { + XML_FEATURE_END = 0, + XML_FEATURE_UNICODE, + XML_FEATURE_UNICODE_WCHAR_T, + XML_FEATURE_DTD, + XML_FEATURE_CONTEXT_BYTES, + XML_FEATURE_MIN_SIZE, + XML_FEATURE_SIZEOF_XML_CHAR, + XML_FEATURE_SIZEOF_XML_LCHAR, + XML_FEATURE_NS, + XML_FEATURE_LARGE_SIZE, + XML_FEATURE_ATTR_INFO + /* Additional features must be added to the end of this enum. */ +}; + +typedef struct { + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; +} XML_Feature; + +XMLPARSEAPI(const XML_Feature *) +XML_GetFeatureList(void); + + +/* Expat follows the GNU/Linux convention of odd number minor version for + beta/development releases and even number minor version for stable + releases. Micro is bumped with each release, and set to 0 with each + change to major or minor version. +*/ +#define XML_MAJOR_VERSION 2 +#define XML_MINOR_VERSION 1 +#define XML_MICRO_VERSION 0 + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/expat_external.h b/apps/nesemu2/src/cartdb/expat/expat_external.h new file mode 100644 index 00000000..a8553ffc --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/expat_external.h @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_External_INCLUDED +#define Expat_External_INCLUDED 1 + +/* External API definitions */ + +#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) +#define XML_USE_MSC_EXTENSIONS 1 +#endif + +/* Expat tries very hard to make the API boundary very specifically + defined. There are two macros defined to control this boundary; + each of these can be defined before including this header to + achieve some different behavior, but doing so it not recommended or + tested frequently. + + XMLCALL - The calling convention to use for all calls across the + "library boundary." This will default to cdecl, and + try really hard to tell the compiler that's what we + want. + + XMLIMPORT - Whatever magic is needed to note that a function is + to be imported from a dynamically loaded library + (.dll, .so, or .sl, depending on your platform). + + The XMLCALL macro was added in Expat 1.95.7. The only one which is + expected to be directly useful in client code is XMLCALL. + + Note that on at least some Unix versions, the Expat library must be + compiled with the cdecl calling convention as the default since + system headers may assume the cdecl convention. +*/ +#ifndef XMLCALL +#if defined(_MSC_VER) +#define XMLCALL __cdecl +#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER) +#define XMLCALL __attribute__((cdecl)) +#else +/* For any platform which uses this definition and supports more than + one calling convention, we need to extend this definition to + declare the convention used on that platform, if it's possible to + do so. + + If this is the case for your platform, please file a bug report + with information on how to identify your platform via the C + pre-processor and how to specify the same calling convention as the + platform's malloc() implementation. +*/ +#define XMLCALL +#endif +#endif /* not defined XMLCALL */ + + +#if !defined(XML_STATIC) && !defined(XMLIMPORT) +#ifndef XML_BUILDING_EXPAT +/* using Expat from an application */ + +#ifdef XML_USE_MSC_EXTENSIONS +#define XMLIMPORT __declspec(dllimport) +#endif + +#endif +#endif /* not defined XML_STATIC */ + + +/* If we didn't define it above, define it away: */ +#ifndef XMLIMPORT +#define XMLIMPORT +#endif + + +#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_UNICODE +#endif + +#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +#ifdef XML_UNICODE_WCHAR_T +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; +#else +typedef unsigned short XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE_WCHAR_T */ +#else /* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE */ + +#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +typedef __int64 XML_Index; +typedef unsigned __int64 XML_Size; +#else +typedef long long XML_Index; +typedef unsigned long long XML_Size; +#endif +#else +typedef long XML_Index; +typedef unsigned long XML_Size; +#endif /* XML_LARGE_SIZE */ + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_External_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/iasciitab.h b/apps/nesemu2/src/cartdb/expat/iasciitab.h new file mode 100644 index 00000000..24a1d5cc --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/iasciitab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/apps/nesemu2/src/cartdb/expat/intconfig.h b/apps/nesemu2/src/cartdb/expat/intconfig.h new file mode 100644 index 00000000..88092e6a --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/intconfig.h @@ -0,0 +1,19 @@ +#ifndef INTCONFIG_H +#define INTCONFIG_H + +#include +#include + +#define XML_NS 1 +#define XML_DTD 1 +#define XML_CONTEXT_BYTES 1024 + +#ifdef USE_LSB +#define BYTEORDER 1234 +#else +#define BYTEORDER 4321 +#endif + +#define HAVE_MEMMOVE + +#endif diff --git a/apps/nesemu2/src/cartdb/expat/internal.h b/apps/nesemu2/src/cartdb/expat/internal.h new file mode 100644 index 00000000..dd545483 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/internal.h @@ -0,0 +1,73 @@ +/* internal.h + + Internal definitions used by Expat. This is not needed to compile + client code. + + The following calling convention macros are defined for frequently + called functions: + + FASTCALL - Used for those internal functions that have a simple + body and a low number of arguments and local variables. + + PTRCALL - Used for functions called though function pointers. + + PTRFASTCALL - Like PTRCALL, but for low number of arguments. + + inline - Used for selected internal functions for which inlining + may improve performance on some platforms. + + Note: Use of these macros is based on judgement, not hard rules, + and therefore subject to change. +*/ + +#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__) +/* We'll use this version by default only where we know it helps. + + regparm() generates warnings on Solaris boxes. See SF bug #692878. + + Instability reported with egcs on a RedHat Linux 7.3. + Let's comment out: + #define FASTCALL __attribute__((stdcall, regparm(3))) + and let's try this: +*/ +#define FASTCALL __attribute__((regparm(3))) +#define PTRFASTCALL __attribute__((regparm(3))) +#endif + +/* Using __fastcall seems to have an unexpected negative effect under + MS VC++, especially for function pointers, so we won't use it for + now on that platform. It may be reconsidered for a future release + if it can be made more effective. + Likely reason: __fastcall on Windows is like stdcall, therefore + the compiler cannot perform stack optimizations for call clusters. +*/ + +/* Make sure all of these are defined if they aren't already. */ + +#ifndef FASTCALL +#define FASTCALL +#endif + +#ifndef PTRCALL +#define PTRCALL +#endif + +#ifndef PTRFASTCALL +#define PTRFASTCALL +#endif + +#ifndef XML_MIN_SIZE +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif /* __GNUC__ */ +#endif +#endif /* XML_MIN_SIZE */ + +#ifdef __cplusplus +#define inline inline +#else +#ifndef inline +#define inline +#endif +#endif diff --git a/apps/nesemu2/src/cartdb/expat/latin1tab.h b/apps/nesemu2/src/cartdb/expat/latin1tab.h new file mode 100644 index 00000000..53c25d76 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/latin1tab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/apps/nesemu2/src/cartdb/expat/nametab.h b/apps/nesemu2/src/cartdb/expat/nametab.h new file mode 100644 index 00000000..b05e62c7 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/apps/nesemu2/src/cartdb/expat/utf8tab.h b/apps/nesemu2/src/cartdb/expat/utf8tab.h new file mode 100644 index 00000000..7bb3e776 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/utf8tab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/apps/nesemu2/src/cartdb/expat/xmlparse.c b/apps/nesemu2/src/cartdb/expat/xmlparse.c new file mode 100644 index 00000000..7433c35e --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmlparse.c @@ -0,0 +1,6393 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include /* memset(), memcpy() */ +#include +#include /* UINT_MAX */ +#include /* time() */ + +#define XML_BUILDING_EXPAT 1 + +#include "intconfig.h" + +#include "ascii.h" +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +/* Using pointer subtraction to convert to integer type. */ +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +/* Handle the case where memmove() doesn't exist. */ +#ifndef HAVE_MEMMOVE +#ifdef HAVE_BCOPY +#define memmove(d,s,l) bcopy((s),(d),(l)) +#else +#error memmove does not exist on this platform, nor is a substitute available +#endif /* HAVE_BCOPY */ +#endif /* HAVE_MEMMOVE */ + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; + int startTagLevel; + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; +static Processor internalEntityProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr, + XML_Bool haveMore); +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr, + XML_Bool haveMore); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(XML_Parser oldParser, + DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static unsigned long generate_hash_secret_salt(void); +static XML_Bool startParsing(XML_Parser parser); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + XML_Index m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; +#ifdef XML_ATTR_INFO + XML_AttrInfo *m_attInfo; +#endif + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; + XML_ParsingStatus m_parsingStatus; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif + unsigned long m_hash_secret_salt; +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define freeInternalEntities (parser->m_freeInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define attInfo (parser->m_attInfo) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#define ps_parsing (parser->m_parsingStatus.parsing) +#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ +#define hash_secret_salt (parser->m_hash_secret_salt) + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, + ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, + ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, + ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, + ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' +}; + +static unsigned long +generate_hash_secret_salt(void) +{ + unsigned int seed = (unsigned int)(time(NULL) % UINT_MAX); + srand(seed); + return rand(); +} + +static XML_Bool /* only valid for root parser */ +startParsing(XML_Parser parser) +{ + /* hash functions must be initialized before setContext() is called */ + if (hash_secret_salt == 0) + hash_secret_salt = generate_hash_secret_salt(); + if (ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + return setContext(parser, implicitContext); + } + return XML_TRUE; + } + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + return parserCreate(encodingName, memsuite, nameSep, NULL); +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } +#ifdef XML_ATTR_INFO + attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo)); + if (attInfo == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } +#endif + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + freeInternalEntities = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = ASCII_EXCL; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = NULL; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; + ps_parsing = XML_INITIALIZED; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + hash_secret_salt = 0; +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + OPEN_INTERNAL_ENTITY *openEntityList; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + /* move openInternalEntities to freeInternalEntities */ + openEntityList = openInternalEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return XML_TRUE; +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + unsigned long oldhash_secret_salt = hash_secret_salt; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + hash_secret_salt = oldhash_secret_salt; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + TAG *tagList; + OPEN_INTERNAL_ENTITY *entityList; + if (parser == NULL) + return; + /* free tagStack and freeTagList */ + tagList = tagStack; + for (;;) { + TAG *p; + if (tagList == NULL) { + if (freeTagList == NULL) + break; + tagList = freeTagList; + freeTagList = NULL; + } + p = tagList; + tagList = tagList->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + /* free openInternalEntities and freeInternalEntities */ + entityList = openInternalEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (freeInternalEntities == NULL) + break; + entityList = freeInternalEntities; + freeInternalEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(openEntity); + } + + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); +#ifdef XML_ATTR_INFO + FREE((void *)attInfo); +#endif + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +#ifdef XML_ATTR_INFO +const XML_AttrInfo * XMLCALL +XML_GetAttributeInfo(XML_Parser parser) +{ + return attInfo; +} +#endif + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +int XMLCALL +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; + hash_secret_salt = hash_salt; + return 1; +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + if (len == 0) { + ps_finalBuffer = (XML_Bool)isFinal; + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + + /* If data are left over from last buffer, and we now know that these + data are the final chunk of input, then we have to check them again + to detect errors based on that fact. + */ + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode == XML_ERROR_NONE) { + switch (ps_parsing) { + case XML_SUSPENDED: + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return XML_STATUS_SUSPENDED; + case XML_INITIALIZED: + case XML_PARSING: + ps_parsing = XML_FINISHED; + /* fall through */ + default: + return XML_STATUS_OK; + } + } + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + enum XML_Error result; + parseEndByteIndex += len; + positionPtr = s; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return XML_STATUS_OK; + } + /* fall through */ + default: + result = XML_STATUS_OK; + } + } + + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + buffer = temp; + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + } + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + eventPtr = bufferPtr; + eventEndPtr = bufferPtr; + return result; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start; + enum XML_Status result = XML_STATUS_OK; + + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndPtr = bufferEnd; + parseEndByteIndex += len; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; /* should not happen */ + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return NULL; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return NULL; + default: ; + } + + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (int)(bufferEnd - bufferPtr); +#ifdef XML_CONTEXT_BYTES + int keep = (int)(bufferPtr - buffer); + + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (int)(bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = (int)(bufferLim - bufferPtr); + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + eventPtr = eventEndPtr = NULL; + positionPtr = NULL; + } + return bufferEnd; +} + +enum XML_Status XMLCALL +XML_StopParser(XML_Parser parser, XML_Bool resumable) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + if (resumable) { + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_FINISHED; + break; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + if (resumable) { +#ifdef XML_DTD + if (isParamEntity) { + errorCode = XML_ERROR_SUSPEND_PE; + return XML_STATUS_ERROR; + } +#endif + ps_parsing = XML_SUSPENDED; + } + else + ps_parsing = XML_FINISHED; + } + return XML_STATUS_OK; +} + +enum XML_Status XMLCALL +XML_ResumeParser(XML_Parser parser) +{ + enum XML_Status result = XML_STATUS_OK; + + if (ps_parsing != XML_SUSPENDED) { + errorCode = XML_ERROR_NOT_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_PARSING; + + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (ps_finalBuffer) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void XMLCALL +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) +{ + assert(status != NULL); + *status = parser->m_parsingStatus; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return parseEndByteIndex - (parseEndPtr - eventPtr); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return (int)(eventEndPtr - eventPtr); + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = (int)(eventPtr - buffer); + *size = (int)(bufferEnd - buffer); + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar* const message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("XML or text declaration not at start of entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix"), + XML_L("must not undeclare prefix"), + XML_L("incomplete markup in parameter entity"), + XML_L("XML declaration not well-formed"), + XML_L("text declaration not well-formed"), + XML_L("illegal character(s) in public id"), + XML_L("parser suspended"), + XML_L("parser not suspended"), + XML_L("parsing aborted"), + XML_L("parsing finished"), + XML_L("cannot suspend in external parameter entity"), + XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), + XML_L("reserved prefix (xmlns) must not be declared or undeclared"), + XML_L("prefix must not be bound to one of the reserved namespace names") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static const XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif +#ifdef XML_NS + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, +#endif +#ifdef XML_LARGE_SIZE + {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, +#endif +#ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 0, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && !ps_finalBuffer) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + int tok; + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + eventPtr = start; + tok = XmlContentTok(encoding, start, end, &next); + eventEndPtr = next; + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + start = next; + } + } + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 1, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + result = processInternalEntity(parser, entity, XML_FALSE); + if (result != XML_ERROR_NONE) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if (fromPtr == rawNameEnd) { + tag->name.strLen = convLen; + break; + } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)end - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; +#ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; +#endif + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; +#ifdef XML_ATTR_INFO + temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) + return XML_ERROR_NO_MEMORY; + attInfo = temp2; +#endif + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + ATTRIBUTE *currAtt = &atts[i]; +#ifdef XML_ATTR_INFO + XML_AttrInfo *currAttInfo = &attInfo[i]; +#endif + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, + currAtt->name + + XmlNameLength(enc, currAtt->name)); + if (!attId) + return XML_ERROR_NO_MEMORY; +#ifdef XML_ATTR_INFO + currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name); + currAttInfo->nameEnd = currAttInfo->nameStart + + XmlNameLength(enc, currAtt->name); + currAttInfo->valueStart = parseEndByteIndex - + (parseEndPtr - currAtt->valuePtr); + currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd); +#endif + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = hash_secret_salt; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(ASCII_COLON)) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(parser, HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? (j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) { + i += 2; + break; + } + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(ASCII_COLON)) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; /* prefixLen includes null terminator */ + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + /* if namespaceSeparator != '\0' then uri includes it already */ + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + /* we always have a namespace separator between localPart and prefix */ + if (prefixLen) { + uri += i - 1; + *uri = namespaceSeparator; /* replace null terminator */ + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + static const XML_Char xmlNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, + ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, + ASCII_e, '\0' + }; + static const int xmlLen = + (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; + static const XML_Char xmlnsNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, + ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, + ASCII_SLASH, '\0' + }; + static const int xmlnsLen = + (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + + XML_Bool mustBeXML = XML_FALSE; + XML_Bool isXML = XML_TRUE; + XML_Bool isXMLNS = XML_TRUE; + + BINDING *b; + int len; + + /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_UNDECLARING_PREFIX; + + if (prefix->name + && prefix->name[0] == XML_T(ASCII_x) + && prefix->name[1] == XML_T(ASCII_m) + && prefix->name[2] == XML_T(ASCII_l)) { + + /* Not allowed to bind xmlns */ + if (prefix->name[3] == XML_T(ASCII_n) + && prefix->name[4] == XML_T(ASCII_s) + && prefix->name[5] == XML_T('\0')) + return XML_ERROR_RESERVED_PREFIX_XMLNS; + + if (prefix->name[3] == XML_T('\0')) + mustBeXML = XML_TRUE; + } + + for (len = 0; uri[len]; len++) { + if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) + isXML = XML_FALSE; + + if (!mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; + + if (mustBeXML != isXML) + return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML + : XML_ERROR_RESERVED_NAMESPACE_URI; + + if (isXMLNS) + return XML_ERROR_RESERVED_NAMESPACE_URI; + + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + /* if attId == NULL then we are not starting a namespace scope */ + if (attId && startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null if the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) { + if (isGeneralTextEntity) + return XML_ERROR_TEXT_DECL; + else + return XML_ERROR_XML_DECL; + } + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + int tok; + const char *start = s; + const char *next = start; + eventPtr = start; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + eventEndPtr = next; + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + *nextPtr = next; + } + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + eventPtr = start; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr, + XML_Bool haveMore) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = + { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; + static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; + static const XML_Char atypeIDREF[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; + static const XML_Char atypeIDREFS[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; + static const XML_Char atypeENTITY[] = + { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; + static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, + ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; + static const XML_Char atypeNMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; + static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, + ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; + static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, + ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; + static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; + static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; + + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (haveMore && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case -XML_TOK_PROLOG_S: + tok = -tok; + break; + case XML_TOK_NONE: +#ifdef XML_DTD + /* for internal PE NOT referenced between declarations */ + if (enc != encoding && !openInternalEntities->betweenDecl) { + *nextPtr = s; + return XML_ERROR_NONE; + } + /* WFC: PE Between Declarations - must check that PE contains + complete markup, not only for external PEs, but also for + internal PEs if the reference occurs between declarations. + */ + if (isParamEntity || enc != encoding) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_INCOMPLETE_PE; + *nextPtr = s; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + XML_Char *pubId; + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + pubId = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!pubId) + return XML_ERROR_NO_MEMORY; + normalizePublicId(pubId); + poolFinish(&tempPool); + doctypePubid = pubId; + handleDefault = XML_FALSE; + goto alreadyChecked; + } + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + alreadyChecked: + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else if (!doctypeSysid) + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations. */ + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == ASCII_PIPE) + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ASCII_COMMA; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ASCII_COMMA) + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = ASCII_PIPE; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + XML_Bool betweenDecl = + (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processInternalEntity(parser, entity, betweenDecl); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + reportDefault(parser, encoding, s, next); + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + } + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } +} + +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl) +{ + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity; + + if (freeInternalEntities) { + openEntity = freeInternalEntities; + freeInternalEntities = openEntity->next; + } + else { + openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); + if (!openEntity) + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; + entity->processed = 0; + openEntity->next = openInternalEntities; + openInternalEntities = openEntity; + openEntity->entity = entity; + openEntity->startTagLevel = tagLevel; + openEntity->betweenDecl = betweenDecl; + openEntity->internalEventPtr = NULL; + openEntity->internalEventEndPtr = NULL; + textStart = (char *)entity->textPtr; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, tagLevel, internalEncoding, textStart, + textEnd, &next, XML_FALSE); + + if (result == XML_ERROR_NONE) { + if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - textStart); + processor = internalEntityProcessor; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + } + return result; +} + +static enum XML_Error PTRCALL +internalEntityProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; + if (!openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + entity = openEntity->entity; + textStart = ((char *)entity->textPtr) + entity->processed; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, openEntity->startTagLevel, internalEncoding, + textStart, textEnd, &next, XML_FALSE); + + if (result != XML_ERROR_NONE) + return result; + else if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + +#ifdef XML_DTD + if (entity->is_param) { + int tok; + processor = prologProcessor; + tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr, + (XML_Bool)!ps_finalBuffer); + } + else +#endif /* XML_DTD */ + { + processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parentParser ? 1 : 0, encoding, s, end, + nextPtr, (XML_Bool)!ps_finalBuffer); + } +} + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* Cannot report skipped entity here - see comments on + skippedEntityHandler. + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(ASCII_COLON)) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T(ASCII_x) + && name[1] == XML_T(ASCII_m) + && name[2] == XML_T(ASCII_l) + && name[3] == XML_T(ASCII_n) + && name[4] == XML_T(ASCII_s) + && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(ASCII_COLON)) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T(ASCII_FF) + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T(ASCII_EQUALS)) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, NULL, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); + poolInit(&(p->entityValuePool), ms); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); + poolClear(&(p->entityValuePool)); + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(oldParser, + &(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(oldParser, + &(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(XML_Parser parser, KEY s) +{ + unsigned long h = hash_secret_salt; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) { + table->size = 0; + return NULL; + } + memset(table->v, 0, tsize); + i = hash(parser, name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(parser, name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(parser, table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (int)(pool->end - pool->start)*2; + BLOCK *temp = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = (int)(pool->end - pool->start); + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/apps/nesemu2/src/cartdb/expat/xmlrole.c b/apps/nesemu2/src/cartdb/expat/xmlrole.c new file mode 100644 index 00000000..d854f949 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmlrole.c @@ -0,0 +1,1324 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include + +#include "intconfig.h" + +#include "expat_external.h" +#include "internal.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { + ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { + ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { + ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { + ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { + ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { + ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, + '\0' }; +static const char KW_ENTITY[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { + ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { + ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +#ifdef XML_DTD +static const char KW_IGNORE[] = { + ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +#endif +static const char KW_IMPLIED[] = { + ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +#ifdef XML_DTD +static const char KW_INCLUDE[] = { + ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +#endif +static const char KW_NDATA[] = { + ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, + '\0' }; +static const char KW_NOTATION[] = + { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, + '\0' }; +static const char KW_PCDATA[] = { + ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { + ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { + ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, + '\0' }; +static const char KW_SYSTEM[] = { + ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static int FASTCALL common(PROLOG_STATE *state, int tok); + +static int PTRCALL +prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_PI; + case XML_TOK_COMMENT: + state->handler = prolog1; + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_DOCTYPE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_DOCTYPE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_ATTLIST_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_ELEMENT_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NOTATION_NONE; + } + break; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NONE: + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static int PTRCALL +externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_ENTITY_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity10; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity10(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + } + return common(state, tok); +} + +static int PTRCALL +notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NOTATION_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NOTATION_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + { + static const char * const types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +/* default value */ +static int PTRCALL +attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static int PTRCALL +element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static int PTRCALL +element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +condSect1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static int PTRCALL +condSect2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return state->role_none; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return state->role_none; + } + return common(state, tok); +} + +static int PTRCALL +error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + return XML_ROLE_NONE; +} + +static int FASTCALL +common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void +XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; + state->inEntityValue = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/apps/nesemu2/src/cartdb/expat/xmlrole.h b/apps/nesemu2/src/cartdb/expat/xmlrole.h new file mode 100644 index 00000000..0966364f --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmlrole.h @@ -0,0 +1,114 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt +#endif + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NONE, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_INTERNAL_SUBSET, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_NONE, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_COMPLETE, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NONE, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_NONE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NONE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PI, + XML_ROLE_COMMENT, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (PTRCALL *handler) (struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; + int role_none; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; + int inEntityValue; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok.c b/apps/nesemu2/src/cartdb/expat/xmltok.c new file mode 100644 index 00000000..3c3fb697 --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmltok.c @@ -0,0 +1,1639 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include + +#include "intconfig.h" + +#include "expat_external.h" +#include "internal.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits between + the bottom 5 and 6 bits of the bytes. We need 8 bits to index into + pages, 3 bits to add to that index and 5 bits to generate the mask. +*/ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits between + the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index + into pages, 3 bits to add to that index and 5 bits to generate the + mask. +*/ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +/* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode + code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). + Implementation details: + (A & 0x80) == 0 means A < 0x80 + and + (A & 0xC0) == 0xC0 means A > 0xBF +*/ + +#define UTF8_INVALID2(p) \ + ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) + +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || \ + ((*p) == 0xEF && (p)[1] == 0xBF \ + ? \ + (p)[2] > 0xBD \ + : \ + ((p)[2] & 0xC0) == 0xC0) \ + || \ + ((*p) == 0xE0 \ + ? \ + (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ + || \ + ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ + || \ + ((*p) == 0xF0 \ + ? \ + (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) + +static int PTRFASTCALL +isNever(const ENCODING *enc, const char *p) +{ + return 0; +} + +static int PTRFASTCALL +utf8_isName2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isName3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static int PTRFASTCALL +utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +static int PTRFASTCALL +utf8_isInvalid2(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID2((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (PTRFASTCALL *byteType)(const ENCODING *, const char *); + int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int (PTRCALL *charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (PTRFASTCALL *isName2)(const ENCODING *, const char *); + int (PTRFASTCALL *isName3)(const ENCODING *, const char *); + int (PTRFASTCALL *isName4)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); +}; + +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int FASTCALL checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static int PTRFASTCALL +sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +static int PTRFASTCALL +sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +static int PTRCALL +sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static void PTRCALL +utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static void PTRCALL +utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); + from += 2; + break; + case BT_LEAD3: + *to++ = (unsigned short)(((from[0] & 0xf) << 12) + | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + goto after; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } +after: + *fromP = from; + *toP = to; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static void PTRCALL +latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = (char)((c >> 6) | UTF8_cval2); + *(*toP)++ = (char)((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static void PTRCALL +latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +static void PTRCALL +ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +static int PTRFASTCALL +unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static void PTRCALL \ +E ## toUtf8(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static void PTRCALL \ +E ## toUtf16(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#if BYTEORDER != 4321 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#if BYTEORDER != 1234 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +#undef PREFIX + +static int FASTCALL +streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static void PTRCALL +initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static int +toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static int FASTCALL +isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space or there's an S + followed by name=val. +*/ +static int +parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = NULL; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = NULL; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = (char)c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, + ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static int +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = NULL; + const char *name = NULL; + const char *nameEnd = NULL; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (versionEndPtr) + *versionEndPtr = ptr; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static int FASTCALL +checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int FASTCALL +XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (char)(c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = (char)((c >> 6) | UTF8_cval2); + buf[1] = (char)((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = (char)((c >> 12) | UTF8_cval3); + buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[2] = (char)((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = (char)((c >> 18) | UTF8_cval4); + buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); + buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[3] = (char)((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int FASTCALL +XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = (unsigned short)charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (unsigned short)((charNum >> 10) + 0xD800); + buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + CONVERTER convert; + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) + +int +XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static int PTRFASTCALL +unknown_isName(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isInvalid(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static void PTRCALL +unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = uenc->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = uenc->convert(uenc->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static void PTRCALL +unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + while (*fromP != fromLim && *toP != toLim) { + unsigned short c = uenc->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short) + uenc->convert(uenc->userData, *fromP); + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + int i; + struct unknown_encoding *e = (struct unknown_encoding *)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = (unsigned short)c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, + ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, + '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, + '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, + '\0' +}; + +static int FASTCALL +getEncodingIndex(const char *name) +{ + static const char * const encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == NULL) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding + specified at initialization in the isUtf16 member. +*/ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. encodingTable maps from + encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of + the external (protocol) specified encoding; state is + XML_CONTENT_STATE if we're parsing an external text entity, and + XML_PROLOG_STATE otherwise. +*/ + + +static int +initScan(const ENCODING * const *encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. + */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC + || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *nextTokPtr = ptr + 3; + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document + entity can only start with ASCII characters. So the only + way this can fail to be big-endian UTF-16 if it it's an + external parsed general entity that's labelled as + UTF-16LE. + */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. + */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#define XML_TOK_NS_C +#include "xmltok_ns.c" +#undef XML_TOK_NS_C +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#define XML_TOK_NS_C +#include "xmltok_ns.c" +#undef XML_TOK_NS_C + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok.h b/apps/nesemu2/src/cartdb/expat/xmltok.h new file mode 100644 index 00000000..8e50406b --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmltok.h @@ -0,0 +1,316 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be + start of illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. +*/ +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok for a + name with a colon. +*/ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + XML_Size lineNumber; + XML_Size columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +typedef int (PTRCALL *SCANNER)(const ENCODING *, + const char *, + const char *, + const char **); + +struct encoding { + SCANNER scanners[XML_N_STATES]; + SCANNER literalScanners[XML_N_LITERAL_TYPES]; + int (PTRCALL *sameName)(const ENCODING *, + const char *, + const char *); + int (PTRCALL *nameMatchesAscii)(const ENCODING *, + const char *, + const char *, + const char *); + int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); + int (PTRCALL *getAtts)(const ENCODING *enc, + const char *ptr, + int attsMax, + ATTRIBUTE *atts); + int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int (PTRCALL *predefinedEntityName)(const ENCODING *, + const char *, + const char *); + void (PTRCALL *updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (PTRCALL *isPublicId)(const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr); + void (PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* Scan the string starting at ptr until the end of the next complete + token, but do not scan past eptr. Return an integer giving the + type of token. + + Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + + Return XML_TOK_PARTIAL when the string does not contain a complete + token; nextTokPtr will not be set. + + Return XML_TOK_INVALID when the string does not start a valid + token; nextTokPtr will be set to point to the character which made + the token invalid. + + Otherwise the string starts with a valid token; nextTokPtr will be + set to point to the character following the end of that token. + + Each data character counts as a single token, but adjacent data + characters may be returned together. Similarly for characters in + the prolog outside literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on the content + of a literal that has already been returned by XmlTok. +*/ +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncoding(void); +const ENCODING *XmlGetUtf16InternalEncoding(void); +int FASTCALL XmlUtf8Encode(int charNumber, char *buf); +int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlSizeOfUnknownEncoding(void); + + +typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData); + +int XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncodingNS(void); +const ENCODING *XmlGetUtf16InternalEncodingNS(void); +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok_impl.c b/apps/nesemu2/src/cartdb/expat/xmltok_impl.c new file mode 100644 index 00000000..8223c68d --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmltok_impl.c @@ -0,0 +1,1783 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_IMPL_C + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, + const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (ptr + 2*MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static int PTRCALL +PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static int PTRCALL +PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (ptr == end) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) { + *nextTokPtr = end; + /* indicate that this might be part of a CR/LF pair */ + return -XML_TOK_PROLOG_S; + } + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + if (ptr == end) + return XML_TOK_PARTIAL; + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static int PTRCALL +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static int PTRCALL +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) { + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static int PTRCALL +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static int PTRCALL +PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty + element tag. Returns the number of attributes. Pointers to the + first attsMax attributes are stored in atts. +*/ + +static int PTRCALL +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static int PTRFASTCALL +PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); + !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: + case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: + case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static int PTRCALL +PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, + const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static int PTRCALL +PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static int PTRCALL +PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (ptr1 == end1) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static int PTRFASTCALL +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return (int)(ptr - start); + } + } +} + +static const char * PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static void PTRCALL +PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr < end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (XML_Size)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (XML_Size)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES + +#endif /* XML_TOK_IMPL_C */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok_impl.h b/apps/nesemu2/src/cartdb/expat/xmltok_impl.h new file mode 100644 index 00000000..9cbcc12d --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file COPYING for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/apps/nesemu2/src/cartdb/expat/xmltok_ns.c b/apps/nesemu2/src/cartdb/expat/xmltok_ns.c new file mode 100644 index 00000000..94f8506d --- /dev/null +++ b/apps/nesemu2/src/cartdb/expat/xmltok_ns.c @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_NS_C + +const ENCODING * +NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING * +NS(XmlGetUtf16InternalEncoding)(void) +{ +#if BYTEORDER == 1234 + return &ns(internal_little2_encoding).enc; +#elif BYTEORDER == 4321 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return (*(const char *)&n + ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +#endif +} + +static const ENCODING * const NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static int PTRCALL +NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static int PTRCALL +NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int +NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static const ENCODING * +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int +NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + versionEndPtr, + encodingName, + encoding, + standalone); +} + +#endif /* XML_TOK_NS_C */ diff --git a/apps/nesemu2/src/cartdb/parser.c b/apps/nesemu2/src/cartdb/parser.c new file mode 100644 index 00000000..c4d58e26 --- /dev/null +++ b/apps/nesemu2/src/cartdb/parser.c @@ -0,0 +1,313 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "cartdb/parser.h" +#include "misc/log.h" + +#define BUFSIZE 4096 + +static int num_malloc = 0; +static int num_malloc_str = 0; +static int num_malloc_node = 0; +static int num_malloc_attr = 0; +static int num_free = 0; +static int num_free_str = 0; +static int num_free_node = 0; +static int num_free_attr = 0; +size_t num_bytes = 0; + +void *mem_malloc(size_t sz) +{ + num_malloc++; + num_bytes += sz; + return(malloc(sz)); +} + +void *mem_malloc_str(size_t sz) +{ + num_malloc_str++; + return(mem_malloc(sz)); +} + +void *mem_malloc_node(size_t sz) +{ + num_malloc_node++; + return(mem_malloc(sz)); +} + +void *mem_malloc_attr(size_t sz) +{ + num_malloc_attr++; + return(mem_malloc(sz)); +} + +void mem_free(void *ptr) +{ + num_free++; + free(ptr); +} + +void mem_free_str(void *ptr) +{ + num_free_str++; + mem_free(ptr); +} + +void mem_free_node(void *ptr) +{ + num_free_node++; + mem_free(ptr); +} + +void mem_free_attr(void *ptr) +{ + num_free_attr++; + mem_free(ptr); +} + +static char *copystr(const char *str) +{ + char *ret; + size_t sz; + + sz = strlen(str); + ret = mem_malloc_str(sz + 1); + memset(ret,0,sz + 1); + memcpy(ret,str,sz); + return(ret); +} + +static node_t *create_node(const char *name) +{ + node_t *ret = 0; + + ret = (node_t*)mem_malloc_node(sizeof(node_t)); + memset(ret,0,sizeof(node_t)); + ret->name = copystr(name); + return(ret); +} + +static attribute_t *create_attribute(const char *name,const char *data) +{ + attribute_t *ret = 0; + + ret = (attribute_t*)mem_malloc_attr(sizeof(attribute_t)); + memset(ret,0,sizeof(attribute_t)); + ret->name = copystr(name); + ret->data = copystr(data); + return(ret); +} + +static void destroy_node(node_t *node) +{ + mem_free_str(node->name); + mem_free_node(node); +} + +static void destroy_attribute(attribute_t *attr) +{ + mem_free_str(attr->name); + mem_free_str(attr->data); + mem_free_attr(attr); +} + +static node_t *add_child(node_t *parent,const char *name) +{ + node_t *ret = 0; + node_t *node; + + ret = create_node(name); + ret->parent = parent; + if(parent->child == 0) { + parent->child = ret; + } + else { + node = parent->child; + while(node->next) { + node = node->next; + } + node->next = ret; + } + return(ret); +} + +static void free_attributes(attribute_t *attr) +{ + attribute_t *a; + + while(attr) { + a = attr; + attr = attr->next; + destroy_attribute(a); + } +} + +static void free_nodes(node_t *node) +{ + node_t *n; + + while(node) { + if(node->child) + free_nodes(node->child); + n = node; + node = node->next; + free_attributes(n->attributes); + destroy_node(n); + } +} + +static void XMLCALL start(void *data,const char *el,const char **attr) +{ + xml_t *xmlinfo = (xml_t*)data; + int i; + + if(xmlinfo->root == 0) { + xmlinfo->root = create_node(el); + xmlinfo->cur = xmlinfo->root; + } + else { + xmlinfo->cur = add_child(xmlinfo->cur,el); + } + for(i=0;attr[i];i+=2) { + attribute_t *at; + + at = create_attribute(attr[i],attr[i+1]); + at->next = xmlinfo->cur->attributes; + xmlinfo->cur->attributes = at; + } +} + +static void XMLCALL end(void *data,const char *el) +{ + xml_t *xmlinfo = (xml_t*)data; + + xmlinfo->cur = xmlinfo->cur->parent; +} + +xml_t *parser_load(char *filename) +{ + FILE *fp; + char *buf; + int filelen,len; + xml_t *xml = 0; + + //open xml file + if((fp = fopen(filename,"rb")) == 0) { + log_printf("parser_loadxml: error opening file '%s'\n",filename); + return(0); + } + + //get size of xml file + fseek(fp,0,SEEK_END); + filelen = ftell(fp); + fseek(fp,0,SEEK_SET); + + //allocate temporary buffer + buf = mem_malloc(BUFSIZE + 1); + + //initialize xml struct + xml = (xml_t*)mem_malloc(sizeof(xml_t)); + memset(xml,0,sizeof(xml_t)); + xml->parser = XML_ParserCreate(NULL); + xml->root = xml->cur = 0; + + //setup expat + XML_SetElementHandler(xml->parser,start,end); + XML_SetUserData(xml->parser,(void*)xml); + + //help expat parse the xml file + while(filelen) { + if(BUFSIZE > filelen) { + fread(buf,1,filelen,fp); + len = filelen; + } + else { + fread(buf,1,BUFSIZE,fp); + len = BUFSIZE; + } + filelen -= len; + XML_Parse(xml->parser,buf,len,0); + } + XML_Parse(xml->parser,"",0,1); + + //cleanup + XML_ParserFree(xml->parser); + xml->parser = 0; + mem_free(buf); + fclose(fp); + + //we are done + return(xml); +} + +void parser_free(xml_t *xml) +{ + free_nodes(xml->root); + mem_free(xml); +} + +void parser_merge(xml_t *dest,xml_t **src) +{ + node_t *node; + + //ensure we are working with the same types + if(strcmp(dest->root->name,(*src)->root->name) != 0) { + log_printf("parser_merge: different root element names, cannot merge\n"); + return; + } + + //find the last node + node = dest->root->child; + while(node->next) { + node = node->next; + } + + //move nodes to the dest xml struct + node->next = (*src)->root->child; + (*src)->root->child = 0; + + //free the rest of the src xml struct + parser_free(*src); + *src = 0; +} + +#define IS_OK(cc) ((cc) ? "OK" : "Bad!") + +void parser_verifymemory() +{ + int result; + + result = num_malloc_str - num_free_str; + result += num_malloc_node - num_free_node; + result += num_malloc_attr - num_free_attr; + result += num_malloc - num_free; + if(result) { + log_printf("xml memory verification:\n"); + log_printf("------------------------\n"); + log_printf(" str mallocs: %d, frees %d (%s)\n",num_malloc_str,num_free_str,IS_OK(num_malloc_str == num_free_str)); + log_printf(" node mallocs: %d, frees %d (%s)\n",num_malloc_node,num_free_node,IS_OK(num_malloc_node == num_free_node)); + log_printf(" attr mallocs: %d, frees %d (%s)\n",num_malloc_attr,num_free_attr,IS_OK(num_malloc_attr == num_free_attr)); + log_printf(" total mallocs: %d, frees %d (%s)\n",num_malloc,num_free,IS_OK(num_malloc == num_free)); + } + log_printf("total bytes used by xml: %.3fmb\n",(double)num_bytes / 1024.0f / 1024.0f); +} diff --git a/apps/nesemu2/src/cartdb/parser.h b/apps/nesemu2/src/cartdb/parser.h new file mode 100644 index 00000000..6da6e01f --- /dev/null +++ b/apps/nesemu2/src/cartdb/parser.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cartdb__parser_h__ +#define __cartdb__parser_h__ + +#include "cartdb/expat/expat.h" + +typedef struct attribute_s { + struct attribute_s *next; + char *name,*data; +} attribute_t; + +typedef struct node_s { + struct node_s *next; + struct node_s *parent,*child; + attribute_t *attributes; + char *name,*data; +} node_t; + +typedef struct xml_s { + XML_Parser parser; + node_t *root,*cur; +} xml_t; + +xml_t *parser_load(char *filename); +void parser_free(xml_t *xml); +void parser_merge(xml_t *dest,xml_t **src); +void parser_verifymemory(); + +#endif diff --git a/apps/nesemu2/src/emu/commands.c b/apps/nesemu2/src/emu/commands.c new file mode 100644 index 00000000..129e52f0 --- /dev/null +++ b/apps/nesemu2/src/emu/commands.c @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "emu/commands.h" +#include "misc/log.h" +#include "misc/memutil.h" + +#define COMMAND_START static command_t commands[] = { +#define COMMAND(n) {"" #n "", command_ ## n}, +#define COMMAND_END {0,0}}; + +typedef int (*cmdfunc_t)(int argc,char **argv); + +typedef struct command_s { + char *name; + cmdfunc_t func; +} command_t; + +int command_help(int,char**); + +COMMAND_START + COMMAND(help) + COMMAND(mappers) + COMMAND(set) + COMMAND(unset) + COMMAND(quit) + COMMAND(load) + COMMAND(unload) + COMMAND(reset) + COMMAND(hardreset) + COMMAND(readcpu) + COMMAND(writecpu) + COMMAND(readppu) + COMMAND(dump) +COMMAND_END + +COMMAND_FUNC(help) +{ + command_t *c = commands; + + log_printf("available commands:\n "); + for(c=commands;c->name;c++) { + log_printf("%s ",c->name); + } + return(0); +} + +int command_init() +{ + return(0); +} + +void command_kill() +{ + +} + +int command_execute(char *s) +{ + int i,argc = 0; + char *argv[64]; //not more than 64 args! + char *str; + + //eat whitespace in front + while(*s == ' ') { + s++; + } + + //empty strings do nothing + if(strcmp(s,"") == 0) + return(1); + + //make a copy of the string + str = mem_strdup(s); + + //split up the command line (needs improvement) + argv[argc] = strtok(str," \t"); + while(argv[argc] != 0) { + argv[++argc] = strtok(0," \t"); + } + + //try to execute the command + for(i=0;commands[i].name;i++) { + if(strcmp(argv[0],commands[i].name) == 0) { + commands[i].func(argc,argv); + mem_free(str); + return(0); + } + } + + //bad command or file name! + log_printf("invalid command '%s'\n",argv[0]); + mem_free(str); + return(1); +} diff --git a/apps/nesemu2/src/emu/commands.h b/apps/nesemu2/src/emu/commands.h new file mode 100644 index 00000000..d92e63bf --- /dev/null +++ b/apps/nesemu2/src/emu/commands.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __commands_h__ +#define __commands_h__ + +#include "types.h" + +#define CHECK_ARGS(num,msg) \ + if(argc < (num)) { \ + log_printf(msg); \ + return(1); \ + } + +#define CHECK_CART() \ + if(nes->cart == 0) { \ + log_printf("no rom loaded!\n"); \ + return(1); \ + } + +#define COMMAND_FUNC(n) int command_ ## n (int argc,char **argv) +#define COMMAND_DECL(n) COMMAND_FUNC(n); + +//general commands +COMMAND_DECL(mappers); +COMMAND_DECL(set); +COMMAND_DECL(unset); +COMMAND_DECL(quit); + +//nes commands +COMMAND_DECL(load); +COMMAND_DECL(unload); +COMMAND_DECL(reset); +COMMAND_DECL(hardreset); +COMMAND_DECL(readcpu); +COMMAND_DECL(writecpu); +COMMAND_DECL(readppu); +COMMAND_DECL(loadstate); +COMMAND_DECL(savestate); + +COMMAND_DECL(dump); + +int command_execute(char *str); + +#endif diff --git a/apps/nesemu2/src/emu/commands/general.c b/apps/nesemu2/src/emu/commands/general.c new file mode 100644 index 00000000..a8a58a3c --- /dev/null +++ b/apps/nesemu2/src/emu/commands/general.c @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "emu/commands.h" +#include "misc/log.h" +#include "misc/config.h" +#include "mappers/mappers.h" +#include "mappers/mapperid.h" + +//just use include file, ditch this +extern int quit; + +COMMAND_FUNC(mappers) +{ + int i,j,n,n2; + const char *str; + + log_printf("supported ines mappers: "); + for(n=0,i=0;i<256;i++) { + if(mapper_get_mapperid_ines(i) >= 0) { + if(n) + log_printf(", "); + log_printf("%d",i); + n++; + } + } + + log_printf("\n\nsupported ines20 mappers: "); + for(n2=0,i=0;i<(256 * 1);i++) { + for(j=1;j<16;j++) { + if(mapper_get_mapperid_ines20(i,j) >= 0) { + if(n2) + log_printf(", "); + log_printf("%d.%d",i,j); + n2++; + } + } + } + + log_printf("\n\nsupported unif mappers:\n"); + for(i=0;;i++) { + if((str = mapper_get_unif_boardname(i)) == 0) + break; + log_printf(" %s\n",str); + } + log_printf("\n%d ines, %d ines20, %d unif, %d internal boards supported\n",n,n2,i,B_BOARDEND); + return(0); +} + +COMMAND_FUNC(set) +{ + var_t *var; + + //if no arguments was passed, just show the variables + if(argc < 2) { + var = config_get_head(); + while(var) { + log_printf(" %s = %s\n",var->name,var->data); + var = var->next; + } + } + + //we have arguments, validate them and set var + else { + char *name = argv[1]; + char *data = argv[2]; + + var_set_string(name,data); + } + return(0); +} + +COMMAND_FUNC(unset) +{ + CHECK_ARGS(2,"usage: unset \n"); + var_unset(argv[1]); + return(0); +} + +COMMAND_FUNC(quit) +{ + quit++; + return(0); +} diff --git a/apps/nesemu2/src/emu/commands/nes.c b/apps/nesemu2/src/emu/commands/nes.c new file mode 100644 index 00000000..3d0ff90f --- /dev/null +++ b/apps/nesemu2/src/emu/commands/nes.c @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "emu/commands.h" +#include "misc/log.h" +#include "misc/config.h" +#include "nes/nes.h" + +//!!!!!! kludge alert !!!!!! +extern int running; + +static u32 str2int(char *str) +{ + u32 i,ret = 0; + size_t len; + char ch; + int base = 10; + + if(*str == '$') { + base = 16; + str++; + } + else if(str[0] == '0' && tolower(str[1]) == 'x') { + str += 2; + base = 16; + } + + len = strlen(str); + + for(i=0;i= 'A' && ch <= 'F') + ch = 10 + ch - 'A'; + else if(ch >= '0' && ch <= '9') + ch = ch - '0'; + else { + return((u32)-1); + } + ret += ch; + } + return(ret); +} + +COMMAND_FUNC(load) +{ + //need a way to load the functions common to all systems/targets + if(nes_load(argv[1]) == 0) { + nes_reset(1); + running = config_get_bool("nes.pause_on_load") ? 0 : 1; + } + return(0); +} + +COMMAND_FUNC(unload) +{ + CHECK_CART(); + nes_unload(); + running = 0; + return(0); +} + +COMMAND_FUNC(reset) +{ + CHECK_CART(); + nes_reset(0); + return(0); +} + +COMMAND_FUNC(hardreset) +{ + CHECK_CART(); + nes_reset(1); + return(0); +} + +COMMAND_FUNC(readcpu) +{ + u32 addr; + + CHECK_ARGS(2,"usage: readcpu \n"); + CHECK_CART(); + addr = str2int(argv[1]); + if(addr == (u32)-1) { + log_printf("invalid address\n"); + } + else { + log_printf("$%04X = $%02X\n",addr,cpu_read(addr)); + } + return(0); +} + +COMMAND_FUNC(writecpu) +{ + u32 addr,data; + + CHECK_ARGS(3,"usage: writecpu \n"); + CHECK_CART(); + addr = str2int(argv[1]); + data = str2int(argv[2]); + if(addr == (u32)-1) { + log_printf("invalid address\n"); + } + else if(data == (u32)-1) { + log_printf("invalid data\n"); + } + else { + log_printf("$%04X = $%02X\n",addr,data); + cpu_write(addr,(u8)data); + } + return(0); +} + +COMMAND_FUNC(readppu) +{ + u32 addr, size, n; + + CHECK_ARGS(2, "usage: readppu [size]\n"); + CHECK_CART(); + addr = str2int(argv[1]); + if (addr == (u32)-1) { + log_printf("invalid address\n"); + } + else { + if (argc >= 3) { + size = str2int(argv[2]); + for (n = 0; n \n"); + CHECK_CART(); + + //dumping prg + if (stricmp("prg", argv[1]) == 0) { + size = nes->cart->prg.size; + memfile_t *mf = memfile_open(argv[2], "wb"); + + if (mf == 0) { + log_printf("error creating file %s\n", argv[2]); + return(0); + } + memfile_write(nes->cart->prg.data, size, 1, mf); + memfile_close(mf); + log_printf("dumped %d bytes of PRG to file '%s'\n", size, argv[2]); + } + + else + log_printf("please choose valid area and filename to save for dump\n"); + return(0); +} diff --git a/apps/nesemu2/src/emu/emu.c b/apps/nesemu2/src/emu/emu.c new file mode 100644 index 00000000..a039ed59 --- /dev/null +++ b/apps/nesemu2/src/emu/emu.c @@ -0,0 +1,230 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "emu/emu.h" +#include "emu/events.h" +#include "misc/log.h" +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/config.h" +#include "misc/crc32.h" +#include "cartdb/cartdb.h" +#include "system/system.h" +#include "system/video.h" +#include "system/input.h" +#include "system/sound.h" +#include "nes/nes.h" + +#define SUBSYSTEM_START static subsystem_t subsystems[32] = { +#define SUBSYSTEM(n) {50,"" #n "", n ## _init, n ## _kill}, +#define SUBSYSTEM_NOINIT(n) {50,"" #n "", 0, n ## _kill}, +#define SUBSYSTEM_NOKILL(n) {50,"" #n "", n ## _init, 0}, +#define SUBSYSTEM_END {-1,"",0,0}}; + +typedef struct subsystem_s { + int id; + char name[32]; + initfunc_t init; + killfunc_t kill; +} subsystem_t; + +//static subsystem table +SUBSYSTEM_START + SUBSYSTEM(memutil) + SUBSYSTEM(config) + SUBSYSTEM_NOKILL(log) + SUBSYSTEM(cartdb) + SUBSYSTEM(system) + SUBSYSTEM(video) + SUBSYSTEM(input) + SUBSYSTEM(sound) + SUBSYSTEM(palette) + SUBSYSTEM(nes) +SUBSYSTEM_END + +int quit,running; + +int emu_init() +{ + int i; + + //initialize the control variables + quit = 0; + running = 0; + + //generate crc32 table + crc32_gentab(); + + //loop thru the subsystem function pointers and init + for(i=0;subsystems[i].id != -1;i++) { + if(subsystems[i].init) { + log_printf("initing '%s'\n",subsystems[i].name); + if(subsystems[i].init() != 0) { + emu_kill(); + return(1); + } + } + } + return(0); +} + +void emu_kill() +{ + int i; + + //find the end of the pointer list ('i' will equal last one + 1) + for(i=0;subsystems[i].id != -1;i++); + + //loop thru the subsystem function pointers backwards and kill + for(i--;i>=0;i--) { + if(subsystems[i].kill != 0) { + log_printf("killing '%s'\n",subsystems[i].name); + subsystems[i].kill(); + } + } +} + +int emu_exit(int ret) +{ + //kludge alert! need list here or something atexit() style... + log_kill(); + return(ret); +} + +int emu_addsubsystem(char *name,initfunc_t init,killfunc_t kill) +{ + int i; + + log_printf("emu_addsubsystem: adding subsystem '%s'\n",name); + for(i=0;subsystems[i].id != -1;i++); + subsystems[i].id = 50; + strcpy(subsystems[i].name,name); + subsystems[i].init = init; + subsystems[i].kill = kill; + subsystems[i+1].id = -1; + strcpy(subsystems[i+1].name,""); + subsystems[i+1].init = 0; + subsystems[i+1].kill = 0; + return(0); +} + +int emu_mainloop() +{ + u8 *line = (u8*)mem_alloc(512); + int i,p; + u64 t,total,frames; + + //initialize the palette in case the rom isnt loaded first + for(i=0;i<512;i++) { + if(i < 32 && running == 0) + video_updatepalette(i,i); + line[i] = i; + } + + //begin the main loop + log_printf("emu_mainloop: starting main loop...\n"); + total = 0; + frames = 0; + while(quit == 0) { + t = system_gettick(); + system_checkevents(); + input_poll(); + video_startframe(); + if(running && nes->cart) { + nes_frame(); + } + else { + for(i=0;i<240;i++) { + for(p=0;p<256;p++) + video_updatepixel(i,p,line[p]); + } + } + video_endframe(); + total += system_gettick() - t; + frames++; + } + log_printf("fps: %f (%d frames)\n",(double)frames / (double)total * system_getfrequency(),frames); + mem_free(line); + return(0); +} + +int emu_mainloop_test(char *script) +{ + u64 t,total,frames; + char line[1024],*rom = "",*test,*p; + memfile_t *file; + int testrunning = 0; + + log_printf("emu_mainloop_test: starting automated tests from '%s'\n",script); + + if((file = memfile_open(script,"rb")) == 0) { + log_printf("emu_mainloop_test: error opening test script '%s'\n",script); + return(0); + } + + //begin the main loop + total = 0; + frames = 0; + while(quit == 0) { + if(nes->movie.mode & (MOVIE_CRCFAIL | MOVIE_CRCPASS)) { + if(nes->movie.mode & MOVIE_CRCFAIL) + log_printf("emu_mainloop_test: test over. failed. (rom = '%s')\n",rom); + if(nes->movie.mode & MOVIE_CRCPASS) + log_printf("emu_mainloop_test: test over. passed. (rom = '%s')\n",rom); + testrunning = 0; + } + if(testrunning == 0) { + do { + if(memfile_gets(line,1024,file) == 0) { + quit++; + p = 0; + break; + } + p = str_eatwhitespace(line); + log_printf("line: %s",p); + } while(*p == '#'); + if(p == 0) + break; + rom = str_eatwhitespace(strtok(p,";\r\n")); + test = str_eatwhitespace(strtok(0,";\r\n")); + log_printf("rom, test = '%s', '%s'\n",rom,test); + if(emu_event(E_LOADROM,(void*)rom) == 0) { + if(emu_event(E_LOADMOVIE,(void*)test) == 0) { + emu_event(E_PLAYMOVIE,0); + testrunning = 1; + } + } + } + t = system_gettick(); + system_checkevents(); + input_poll(); + video_startframe(); + if(running && nes->cart) { + nes_frame(); + } + video_endframe(); + total += system_gettick() - t; + frames++; + } + log_printf("fps: %f (%d frames)\n",(double)frames / (double)total * system_getfrequency(),frames); + memfile_close(file); + return(0); +} diff --git a/apps/nesemu2/src/emu/emu.h b/apps/nesemu2/src/emu/emu.h new file mode 100644 index 00000000..c6037e1f --- /dev/null +++ b/apps/nesemu2/src/emu/emu.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __emu_h__ +#define __emu_h__ + +#include "types.h" + +typedef int (*initfunc_t)(); +typedef void (*killfunc_t)(); + +//control variables +extern int quit,running; + +int emu_init(); +void emu_kill(); +int emu_exit(int ret); +int emu_addsubsystem(char *name,initfunc_t init,killfunc_t kill); +int emu_mainloop(); +int emu_mainloop_test(char *script); + +#endif diff --git a/apps/nesemu2/src/emu/events.c b/apps/nesemu2/src/emu/events.c new file mode 100644 index 00000000..3b337235 --- /dev/null +++ b/apps/nesemu2/src/emu/events.c @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "emu/events.h" +#include "emu/emu.h" +#include "misc/log.h" +#include "misc/config.h" +#include "misc/paths.h" +#include "misc/memutil.h" +#include "system/video.h" +#include "system/sound.h" +#include "nes/nes.h" +#include "nes/nes.h" +#include "nes/state/state.h" +#include "mappers/mapperid.h" + +static void setfullscreen(int fs) +{ + video_kill(); + sound_pause(); + config_set_bool("video.fullscreen",fs); + sound_play(); + video_init(); + ppu_sync(); +} + +int emu_event(int id,void *data) +{ + int ret = 0; + char dest[1024]; + char *str; + + switch(id) { + + case E_QUIT: + quit++; + break; + + case E_LOADROM: + if((ret = nes_load((char*)data)) == 0) { + nes_reset(1); + running = config_get_bool("video.pause_on_load") ? 0 : 1; + } + else + ret = 1; + break; + + case E_LOADPATCH: + if(nes->cart == 0) + break; + str = mem_strdup(nes->cart->filename); + if((ret = nes_load_patched(str,(char*)data)) == 0) { + nes_reset(1); + } + mem_free(str); + break; + + case E_UNLOAD: + nes_unload(); + break; + + case E_SOFTRESET: + nes_reset(0); + break; + + case E_HARDRESET: + nes_reset(1); + break; + + case E_LOADSTATE: + if(nes->cart == 0) + break; + paths_makestatefilename(nes->cart->filename,dest,1024); + nes_loadstate(dest); + break; + + case E_SAVESTATE: + if (nes == 0 || nes->cart == 0) { + log_printf("emu_event: cannot load state, no rom loaded\n"); + break; + } + paths_makestatefilename(nes->cart->filename,dest,1024); + nes_savestate(dest); + break; + + case E_RECORDMOVIE: + log_printf("emu_event: recording movie from frame %d\n",nes->ppu.frames); + movie_record(); + break; + + case E_PLAYMOVIE: + movie_play(); + log_printf("emu_event: playing movie from frame %d\n",nes->ppu.frames); + break; + + case E_STOPMOVIE: + movie_stop(); + log_printf("emu_event: stopping movie at frame %d\n",nes->ppu.frames); + break; + + case E_LOADMOVIE: + movie_load((char*)data); + break; + + case E_SAVEMOVIE: + movie_save((char*)data); + break; + + case E_FLIPDISK: + if(nes->cart == 0) + break; + if((nes->cart->mapperid & B_TYPEMASK) == B_FDS) { + u8 d[4] = {0,0,0,0}; + + nes->mapper->state(CFG_SAVE,d); + if(d[0] == 0xFF) + d[0] = 0; + else { +// d[0] ^= 1; + d[0] = (d[0] + 1) & 3; + } + nes->mapper->state(CFG_LOAD,d); + log_printf("disk inserted! side = %d\n",d[0]); + } + else + log_printf("cannot flip disk. not fds.\n"); + break; + + case E_DUMPDISK: + if(nes->cart == 0) + break; + if((nes->cart->mapperid & B_TYPEMASK) == B_FDS) { + FILE *fp; + + log_printf("dumping disk as dump.fds\n"); + if((fp = fopen("dump.fds","wb")) != 0) { + fwrite(nes->cart->disk.data,1,nes->cart->disk.size,fp); + fclose(fp); + } + } + break; + + case E_TOGGLERUNNING: + running ^= 1; + break; + + case E_PAUSE: + running = 0; + break; + + case E_UNPAUSE: + running = 1; + break; + + case E_TOGGLEFULLSCREEN: + setfullscreen(config_get_bool("video.fullscreen") ^ 1); + break; + + case E_FULLSCREEN: + setfullscreen(1); + break; + + case E_WINDOWED: + setfullscreen(0); + break; + + //unhandled event + default: + log_printf("emu_event: unhandled event id %d\n",id); + ret = -1; + break; + } + return(ret); +} diff --git a/apps/nesemu2/src/emu/events.h b/apps/nesemu2/src/emu/events.h new file mode 100644 index 00000000..af8e7da8 --- /dev/null +++ b/apps/nesemu2/src/emu/events.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __events_h__ +#define __events_h__ + +#include "types.h" + +//event id's +enum events_e { + E_UNKNOWN = 0, + E_QUIT, + E_LOADROM, + E_LOADPATCH, + E_UNLOAD, + E_SOFTRESET, + E_HARDRESET, + E_SAVESTATE, + E_LOADSTATE, + E_RECORDMOVIE, + E_PLAYMOVIE, + E_STOPMOVIE, + E_LOADMOVIE, + E_SAVEMOVIE, + E_FLIPDISK, + E_DUMPDISK, + E_AUTOTEST, + + //change running state + E_TOGGLERUNNING, + E_PAUSE, + E_UNPAUSE, + + //change from fullscreen/windowed + E_TOGGLEFULLSCREEN, + E_FULLSCREEN, + E_WINDOWED, + + //last valid event id + E_LASTEVENT, + + //clones! + E_RESET = E_SOFTRESET, +}; + +int emu_event(int id,void *data); + +#endif diff --git a/apps/nesemu2/src/inputdev/inputdev.c b/apps/nesemu2/src/inputdev/inputdev.c new file mode 100644 index 00000000..735b8ef3 --- /dev/null +++ b/apps/nesemu2/src/inputdev/inputdev.c @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "inputdev.h" + +#ifdef INPUTDEV + #undef INPUTDEV +#endif + +#define INPUTDEV(n) { \ + extern inputdev_t inputdev##n; \ + if(inputdevid == n) \ + return(&inputdev##n); \ + } + +static inputdev_t *get_inputdev(int inputdevid) +{ + //null device + INPUTDEV(I_NULL); + + //standard input devices + INPUTDEV(I_JOYPAD0); + INPUTDEV(I_JOYPAD1); + INPUTDEV(I_ZAPPER); + INPUTDEV(I_POWERPAD); + + return(0); +} + +static u8 null_read() {return(0);} +static void null_write(u8 data) {} +static void null_update() {} +static int null_movie(int mode) {return(0);} +static void null_state(int mode,u8 *data) {} + +inputdev_t *inputdev_get(int inputdevid) +{ + inputdev_t *ret = get_inputdev(inputdevid); + + if(ret == 0) { + return(get_inputdev(I_NULL)); + } + ret->read = (ret->read == 0) ? null_read : ret->read; + ret->write = (ret->write == 0) ? null_write : ret->write; + ret->update = (ret->update == 0) ? null_update : ret->update; + ret->movie = (ret->movie == 0) ? null_movie : ret->movie; + ret->state = (ret->state == 0) ? null_state : ret->state; + return(ret); +} diff --git a/apps/nesemu2/src/inputdev/inputdev.h b/apps/nesemu2/src/inputdev/inputdev.h new file mode 100644 index 00000000..111874f4 --- /dev/null +++ b/apps/nesemu2/src/inputdev/inputdev.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __inputdev_h__ +#define __inputdev_h__ + +#include "nes/state/state.h" + +//nes input keys for joypads +#define INPUT_A 0x01 +#define INPUT_B 0x02 +#define INPUT_SELECT 0x04 +#define INPUT_START 0x08 +#define INPUT_UP 0x10 +#define INPUT_DOWN 0x20 +#define INPUT_LEFT 0x40 +#define INPUT_RIGHT 0x80 + +#define INPUTDEV(id,read,write,update,movie,state) \ + inputdev_t inputdev##id = {id,read,write,update,movie,state} + +typedef struct inputdev_s { + //device id + int id; + + //read port + u8 (*read)(); + + //write port + void (*write)(u8); + + //update controller info + void (*update)(); + + //update controller info for movie support + int (*movie)(int mode); + + //saving/loading controller state + void (*state)(int mode,u8 *data); +} inputdev_t; + +enum inputdevid_e { + I_NULL, + I_JOYPAD0, + I_JOYPAD1, + I_ZAPPER, + I_POWERPAD, +}; + +inputdev_t *inputdev_get(int id); + +#include "nes/nes.h" + +#endif diff --git a/apps/nesemu2/src/inputdev/joypad0.c b/apps/nesemu2/src/inputdev/joypad0.c new file mode 100644 index 00000000..53af6129 --- /dev/null +++ b/apps/nesemu2/src/inputdev/joypad0.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "inputdev.h" +#include "system/input.h" + +static u32 portdata; +static u8 counter,strobe,buttons; + +static u8 read() +{ + if(strobe) { + portdata = 0xFFFFFF00 | buttons; + counter = 0; + return((u8)portdata & 1); + } + return(((portdata >> counter++) & 1) | 0x40); +} + +static void write(u8 data) +{ + data &= 1; + if(data || strobe) { + strobe = data; + portdata = 0xFFFFFF00 | buttons; + counter = 0; + } +} + +static void update() +{ + buttons = 0; + if(joykeys[joyconfig[0][0]]) buttons |= INPUT_A; + if(joykeys[joyconfig[0][1]]) buttons |= INPUT_B; + if(joykeys[joyconfig[0][2]]) buttons |= INPUT_SELECT; + if(joykeys[joyconfig[0][3]]) buttons |= INPUT_START; + if(joykeys[joyconfig[0][4]]) buttons |= INPUT_UP; + if(joykeys[joyconfig[0][5]]) buttons |= INPUT_DOWN; + if(joykeys[joyconfig[0][6]]) buttons |= INPUT_LEFT; + if(joykeys[joyconfig[0][7]]) buttons |= INPUT_RIGHT; +} + +static int movie(int mode) +{ + if(mode & MOVIE_PLAY) { + buttons = movie_read_u8(); + } + else if(mode & MOVIE_RECORD) { + movie_write_u8(buttons); + } + return(1); +} + +static void state(int mode,u8 *data) +{ + STATE_U32(portdata); + STATE_U8(counter); + STATE_U8(strobe); + STATE_U8(buttons); +} + +INPUTDEV(I_JOYPAD0,read,write,update,movie,state); diff --git a/apps/nesemu2/src/inputdev/joypad1.c b/apps/nesemu2/src/inputdev/joypad1.c new file mode 100644 index 00000000..e95132a8 --- /dev/null +++ b/apps/nesemu2/src/inputdev/joypad1.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "inputdev.h" +#include "system/input.h" + +static u32 portdata; +static u8 counter,strobe,buttons; + +static u8 read() +{ + if(strobe) { + portdata = 0xFFFFFF00 | buttons; + counter = 0; + return((u8)portdata & 1); + } + return(((portdata >> counter++) & 1) | 0x40); +} + +static void write(u8 data) +{ + data &= 1; + if(data || strobe) { + strobe = data; + portdata = 0xFFFFFF00 | buttons; + counter = 0; + } +} + +static void update() +{ + buttons = 0; + if(joykeys[joyconfig[1][0]]) buttons |= INPUT_A; + if(joykeys[joyconfig[1][1]]) buttons |= INPUT_B; + if(joykeys[joyconfig[1][2]]) buttons |= INPUT_SELECT; + if(joykeys[joyconfig[1][3]]) buttons |= INPUT_START; + if(joykeys[joyconfig[1][4]]) buttons |= INPUT_UP; + if(joykeys[joyconfig[1][5]]) buttons |= INPUT_DOWN; + if(joykeys[joyconfig[1][6]]) buttons |= INPUT_LEFT; + if(joykeys[joyconfig[1][7]]) buttons |= INPUT_RIGHT; +} + +static int movie(int mode) +{ + if(mode & MOVIE_PLAY) { + buttons = movie_read_u8(); + } + else if(mode & MOVIE_RECORD) { + movie_write_u8(buttons); + } + return(1); +} + +static void state(int mode,u8 *data) +{ + STATE_U32(portdata); + STATE_U8(counter); + STATE_U8(strobe); + STATE_U8(buttons); +} + +INPUTDEV(I_JOYPAD1,read,write,update,movie,state); diff --git a/apps/nesemu2/src/inputdev/null.c b/apps/nesemu2/src/inputdev/null.c new file mode 100644 index 00000000..d84b745b --- /dev/null +++ b/apps/nesemu2/src/inputdev/null.c @@ -0,0 +1,23 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "inputdev.h" + +INPUTDEV(I_NULL,0,0,0,0,0); diff --git a/apps/nesemu2/src/inputdev/powerpad.c b/apps/nesemu2/src/inputdev/powerpad.c new file mode 100644 index 00000000..5e81fe4b --- /dev/null +++ b/apps/nesemu2/src/inputdev/powerpad.c @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "inputdev.h" +#include "system/input.h" + +static u32 portdata; +static u16 buttons; +static u8 counter,strobe; + +static u8 read() +{ + u8 ret = 0; + + ret |= ((portdata >> (counter + 0)) & 1) << 3; + ret |= ((portdata >> (counter + 8)) & 1) << 4; + counter++; + return(ret | 0x40); +} + +static void write(u8 data) +{ + if(((data & 1) == 0) && (strobe & 1)) { + portdata = 0xFFFF0000 | buttons; + counter = 0; + } + strobe = data; +} + +static void update() +{ + buttons = 0; +} + +static void state(int mode,u8 *data) +{ + STATE_U32(portdata); + STATE_U16(buttons); + STATE_U8(counter); + STATE_U8(strobe); +} + +INPUTDEV(I_POWERPAD,read,write,update,0,state); diff --git a/apps/nesemu2/src/inputdev/zapper.c b/apps/nesemu2/src/inputdev/zapper.c new file mode 100644 index 00000000..6d6955dc --- /dev/null +++ b/apps/nesemu2/src/inputdev/zapper.c @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "inputdev.h" +#include "system/video.h" +#include "system/input.h" + +static u8 portdata; +static u8 counter,buttons,strobe; +static int xpos,ypos; + +extern int video_zapperhit(int x,int y); + +static u8 read() +{ + int x = xpos,y = ypos; + int X,Y; + int hits = 0; + u8 ret = 0; + + //check for button press + if(buttons) + ret = 0x10; + + //see if zapper is even in range + if((x < 0) || (x >= 256) || (y < 0) || (y >= 240)) + ret |= 8; + + //check for zapper light data + else { + for(Y = y - 8; Y < y + 8; Y++) { + if(Y < 0) + Y = 0; + if(Y < (int)nes->ppu.scanline - 32) + continue; + if(Y >(int)nes->ppu.scanline) + break; + for(X = x - 8; X < x + 8; X++) { + if(X < 0) + X = 0; + if(X > 255) + break; + if((Y == (int)nes->ppu.scanline) && (X >= (int)nes->ppu.linecycles)) + break; + if(video_zapperhit(X,Y)) + hits++; + } + } + } + + //if valid hit data, then set bit state + if(hits < 64) + ret |= 8; + + return(ret); +} + +static void write(u8 data) +{ + if(((data & 1) == 0) && (strobe & 1)) { + portdata = buttons; + counter = 0; + } + strobe = data; +} + +static void update() +{ + buttons = input_poll_mouse(&xpos,&ypos); +} + +static int movie(int mode) +{ + if(mode & MOVIE_PLAY) { + buttons = movie_read_u8(); + xpos = (int)movie_read_u8(); + ypos = (int)movie_read_u8(); + } + else if(mode & MOVIE_RECORD) { + movie_write_u8(buttons); + movie_write_u8((u8)xpos); + movie_write_u8((u8)ypos); + } + return(1); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(portdata); + STATE_U8(counter); + STATE_U8(strobe); + STATE_U8(buttons); +} + +INPUTDEV(I_ZAPPER,read,write,update,movie,state); diff --git a/apps/nesemu2/src/mappers/boards/ave/d-1012.c b/apps/nesemu2/src/mappers/boards/ave/d-1012.c new file mode 100644 index 00000000..0de5285e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/ave/d-1012.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[3]; +static readfunc_t oldread; + +static void sync() +{ + if(reg[0] & 0x40) { + mem_setprg32(8,(reg[0] & 0xE) | (reg[1] & 1)); + mem_setchr8(0,((reg[0] & 0xE) << 2) | ((reg[2] >> 4) & 7)); + } + else { + mem_setprg32(8,reg[0] & 0xF); + mem_setchr8(0,((reg[0] & 0xF) << 2) | ((reg[2] >> 4) & 3)); + } + mem_setmirroring((reg[0] >> 7) ^ 1); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xFFF8) { + case 0xFF80: + case 0xFF88: + case 0xFF90: + case 0xFF98: + if((reg[0] & 0x3F) == 0) { + reg[0] = data; + sync(); + } + break; + case 0xFFE8: + case 0xFFF0: + reg[2] = data; + sync(); + break; + } +} + +static u8 read(u32 addr) +{ + u8 data = oldread(addr); + + if(addr >= 0x8000 && (addr & 0xF80) == 0xF80) + write(addr,data); + return(data); +} + +static void reset(int hard) +{ + oldread = cpu_getreadfunc(); + cpu_setreadfunc(read); + mem_setwritefunc(0xF,write); + if(hard) + reg[0] = reg[1] = reg[2] = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,3); + sync(); +} + +MAPPER(B_AVE_D1012,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/ave/nina-001.c b/apps/nesemu2/src/mappers/boards/ave/nina-001.c new file mode 100644 index 00000000..04c891f0 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/ave/nina-001.c @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr[2]; + +static void sync() +{ + mem_setprg32(8,prg); + mem_setchr4(0,chr[0]); + mem_setchr4(4,chr[1]); +} + +static void write(u32 addr,u8 data) +{ + switch(addr) { + case 0x7FFD: prg = data; break; + case 0x7FFE: chr[0] = data; break; + case 0x7FFF: chr[1] = data; break; + } + sync(); +} + +static void reset(int hard) +{ + mem_setwritefunc(7,write); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_ARRAY_U8(chr,2); + sync(); +} + +MAPPER(B_AVE_NINA_001,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/ave/nina-006.c b/apps/nesemu2/src/mappers/boards/ave/nina-006.c new file mode 100644 index 00000000..aa46cf0d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/ave/nina-006.c @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg; +static writefunc_t write4; + +static void sync() +{ + mem_setprg32(8,reg >> 3); + mem_setchr8(0,reg); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + if(addr & 0x4100) { + reg = data; + sync(); + } +} + +static void reset(int hard) +{ + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write); + mem_setwritefunc(5,write); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +MAPPER(B_AVE_NINA_006,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/camerica/bf909x.c b/apps/nesemu2/src/mappers/boards/camerica/bf909x.c new file mode 100644 index 00000000..b380c925 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/camerica/bf909x.c @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,outerprg; +static u8 prgmask,mirroring; +static int type; + +static void sync() +{ + mem_setprg16(0x8,outerprg | prg); + mem_setprg16(0xC,outerprg | (0xFF & prgmask)); + mem_setvram8(0,0); + mem_setmirroring(mirroring); +} + +static void write_mirroring(u32 addr,u8 data) +{ + mirroring = MIRROR_1L + ((data >> 4) & 1); + sync(); +} + +static void write_prgselect(u32 addr,u8 data) +{ + prg = data & prgmask; + sync(); +} + +static void write_outerprgselect(u32 addr,u8 data) +{ + outerprg = data; + sync(); +} + +static void reset(int t,int hard) +{ + type = t; + if(type == B_CAMERICA_BF9093) { + prgmask = 0xF; + } + else if(type == B_CAMERICA_BF9096) { + prgmask = 3; + mem_setwritefunc(0x8,write_outerprgselect); + mem_setwritefunc(0x9,write_outerprgselect); + mem_setwritefunc(0xA,write_outerprgselect); + mem_setwritefunc(0xB,write_outerprgselect); + } + else if(type == B_CAMERICA_BF9097) { + prgmask = 7; + mem_setwritefunc(0x8,write_mirroring); + mem_setwritefunc(0x9,write_mirroring); + } + mem_setwritefunc(0xC,write_prgselect); + mem_setwritefunc(0xD,write_prgselect); + mem_setwritefunc(0xE,write_prgselect); + mem_setwritefunc(0xF,write_prgselect); + mem_setvramsize(8); + prg = outerprg = 0; + mirroring = nes->cart->mirroring; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(outerprg); + STATE_U8(mirroring); + sync(); +} + +static void reset_bf9093(int hard) +{ + reset(B_CAMERICA_BF9093,hard); +} + +static void reset_bf9096(int hard) +{ + reset(B_CAMERICA_BF9096,hard); +} + +static void reset_bf9097(int hard) +{ + reset(B_CAMERICA_BF9097,hard); +} + +MAPPER(B_CAMERICA_BF9093,reset_bf9093,0,0,state); +MAPPER(B_CAMERICA_BF9096,reset_bf9096,0,0,state); +MAPPER(B_CAMERICA_BF9097,reset_bf9097,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/camerica/goldenfive.c b/apps/nesemu2/src/mappers/boards/camerica/goldenfive.c new file mode 100644 index 00000000..a5e1bdef --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/camerica/goldenfive.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,outerprg; + +static void sync() +{ + mem_setprg16(0x8,outerprg | prg); + mem_setprg16(0xC,outerprg | 0xF); + mem_setvram8(0,0); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0xA000) { + if(data & 8) + outerprg = (data << 4) & 0x70; + } + else if(addr >= 0xC000) { + prg = data & 0xF; + } + else + return; + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<16;i++) { + if(i == 0xA || i == 0xB) + continue; + mem_setwritefunc(i,write); + } + mem_setvramsize(8); + prg = outerprg = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + sync(); +} + +MAPPER(B_CAMERICA_GOLDENFIVE,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/cne/decathlon.c b/apps/nesemu2/src/mappers/boards/cne/decathlon.c new file mode 100644 index 00000000..02076f93 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/cne/decathlon.c @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr; + +static void sync() +{ + mem_setprg32(8,prg); + mem_setchr8(0,chr); +} + +static void write(u32 addr,u8 data) +{ + if(addr >= 0x8065 && addr <= 0x80E4) { + addr -= 0x8065; + if(addr < 0x40) + prg = addr & 3; + else + chr = addr & 7; + sync(); + } +} + +static void reset(int hard) +{ + mem_setwritefunc(8,write); + if(hard) { + prg = 0; + chr = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(chr); + sync(); +} + +MAPPER(B_CNE_DECATHLON,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/cne/fsb.c b/apps/nesemu2/src/mappers/boards/cne/fsb.c new file mode 100644 index 00000000..c94e3e08 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/cne/fsb.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[8]; +static u8 *sram6; + +static void sync() +{ + mem_setprg8(0x8,reg[0]); + mem_setprg8(0xA,reg[1]); + mem_setprg8(0xC,reg[2]); + mem_setprg8(0xE,reg[3]); + mem_setchr2(0,reg[4]); + mem_setchr2(2,reg[5]); + mem_setchr2(4,reg[6]); + mem_setchr2(6,reg[7]); +} + +static u8 read6(u32 addr) +{ + if(addr >= 0x6800) + return(sram6[addr & 0xFFF]); + return((u8)(addr >> 8)); +} + +static void write6(u32 addr,u8 data) +{ + if(addr >= 0x6800) + sram6[addr & 0xFFF] = data; + else { + reg[addr & 7] = data; + sync(); + } +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + mem_setwram8(6,0); + sram6 = mem_getwriteptr(6); + mem_unsetcpu8(6); + mem_setreadfunc(6,read6); + mem_setwritefunc(6,write6); + for(i=0;i<8;i++) + reg[i] = 0; + reg[3] = 0xFF; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,8); + sync(); +} + +MAPPER(B_CNE_FSB,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/cne/shlz.c b/apps/nesemu2/src/mappers/boards/cne/shlz.c new file mode 100644 index 00000000..2664fe0b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/cne/shlz.c @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr; +static writefunc_t write4; + +static void sync() +{ + mem_setprg32(8,prg); + mem_setchr8(0,chr); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + prg = data >> 4; + chr = data & 0xF; + sync(); +} + +static void reset(int hard) +{ + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write); + mem_setwritefunc(5,write); + mem_setwramsize(8); + mem_setwram8(6,0); + prg = chr = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(chr); + sync(); +} + +MAPPER(B_CNE_SHLZ,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/homebrew/dripgame.c b/apps/nesemu2/src/mappers/boards/homebrew/dripgame.c new file mode 100644 index 00000000..219d6b30 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/homebrew/dripgame.c @@ -0,0 +1,200 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/sound/s_DRIP.h" + +static apu_external_t drip = { + DRIPsound_Load, + DRIPsound_Unload, + DRIPsound_Reset, + DRIPsound_Get, + 0 +}; + +static const u8 mirrormap[4] = {MIRROR_V,MIRROR_H,MIRROR_1L,MIRROR_1H}; +static const u8 ntmap[4][4] = { + {0,1,0,1}, + {0,0,1,1}, + {0,0,0,0}, + {1,1,1,1} +}; +static const u8 attribs[4] = {0x00,0x55,0xAA,0xFF}; +static u8 control,prg,chr[4]; +static u8 irqenable,irqlatch; +static u16 irqcounter; +static readfunc_t read4,ppuread; +static u8 *exram[2]; +static u32 prevbusaddr,busaddr; + +//extended attributes +static u8 ppu_dripread(u32 addr) +{ + //int nt = (addr >> 10) & 3; + int offset = addr & 0x3FF; + + if(offset >= 0x3C0) { + return(attribs[exram[ntmap[control & 3][(addr >> 10) & 3]][prevbusaddr & 0x3FF]]); + } + return(ppuread(addr)); +} + +static void sync() +{ + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xFF); + mem_setchr2(0,chr[0]); + mem_setchr2(2,chr[1]); + mem_setchr2(4,chr[2]); + mem_setchr2(6,chr[3]); + mem_setmirroring(mirrormap[control & 3]); + if(control & 4) { + ppu_setreadfunc(ppu_dripread); + } + else { + ppu_setreadfunc(0); + } + if(control & 8) + mem_setwram8(6,0); + else + mem_unsetcpu8(6); +} + +static u8 read_4000(u32 addr) +{ + if(addr < 0x4800) + return(read4(addr)); + return('d'); +} + +static u8 read_5000(u32 addr) +{ +/* static u8 hack = 0; + + hack += 3; + if(hack < 20) + return(0x40); + if(hack > 200) + return(0x80); +// log_printf("reading drip sound: $%04X\n",addr); + return(0);*/ + return((u8)DRIPsound_Read(addr)); +} + +static void write_8000(u32 addr,u8 data) +{ +// if(addr >= 0x8010) +// log_printf("dripgame.c: write to $%04x = $%02X\n",addr,data); + addr &= 0xF; + switch(addr) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + DRIPsound_Write(addr,data); + break; + case 0x8: + irqlatch = data; + break; + case 0x9: + irqcounter = ((data & 0x7F) << 8) | irqlatch; + irqenable = data & 0x80; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xA: + control = data & 0xF; + break; + case 0xB: + prg = data & 0xF; + break; + case 0xC: + case 0xD: + case 0xE: + case 0xF: + chr[addr & 3] = data & 0xF; + break; + } + sync(); +} + +static void write_C000(u32 addr,u8 data) +{ +// log_printf("dripgame.c: write to $%04x = $%02X\n",addr,data); + addr &= 0xFFF; + if(addr < 0x400) + exram[0][addr & 0x3FF] = data & 3; + else if(addr < 0x800) + exram[1][addr & 0x3FF] = data & 3; +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8 + 2); + exram[0] = nes->cart->wram.data + 8192; + exram[1] = exram[0] + 0x400; + read4 = mem_getreadfunc(4); + mem_setreadfunc(4,read_4000); + mem_setreadfunc(5,read_5000); + ppuread = ppu_getreadfunc(); + ppu_setreadfunc(ppu_dripread); + for(i=0;i<4;i++) { + mem_setwritefunc(0x8 + i,write_8000); + mem_setwritefunc(0xC + i,write_C000); + } + control = 0; + prg = 0; + for(i=0;i<4;i++) + chr[i] = i; + apu_setexternal(&drip); + sync(); +} + +static void cpucycle() +{ + if(irqenable) { + irqcounter--; + if(irqcounter == 0) + cpu_set_irq(IRQ_MAPPER); + } +} + +static void ppucycle() +{ + prevbusaddr = busaddr; + busaddr = nes->ppu.busaddr; +} + +static void state(int mode,u8 *data) +{ + STATE_U8(control); + STATE_U8(prg); + STATE_ARRAY_U8(chr,4); + STATE_U8(irqenable); + STATE_U8(irqlatch); + STATE_U16(irqcounter); +} + +MAPPER(B_DRIPGAME,reset,ppucycle,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/homebrew/magicfloor.c b/apps/nesemu2/src/mappers/boards/homebrew/magicfloor.c new file mode 100644 index 00000000..1a32d480 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/homebrew/magicfloor.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +//kludge. we use vram instead of nametables because of the tile cache +static void reset(int hard) +{ + int i; + + mem_setvramsize(2); + mem_setprg16(0x8,0); + mem_setprg16(0xC,0); + for(i=0;i<8;i++) { + mem_setvram1(0 + i,0); + mem_setvram1(8 + i,1); + } +} + +MAPPER(B_MAGICFLOOR,reset,0,0,0); diff --git a/apps/nesemu2/src/mappers/boards/homebrew/tepples.c b/apps/nesemu2/src/mappers/boards/homebrew/tepples.c new file mode 100644 index 00000000..f9248359 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/homebrew/tepples.c @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 select, bankmode; +static u8 outerprg,prg,chr; + +static void sync() +{ + int banklo,bankhi,outerbank,bank,shift,mask; + + shift = (bankmode >> 4) & 3; + mask = (1 << shift) - 1; + outerbank = (outerprg & ~mask) | (prg & mask); + bank = ((outerprg & ~mask) << 1) | (prg & ((mask << 1) | 1)); + banklo = bankhi = 0; + //prg bank mode + switch((bankmode >> 2) & 3) { + //32kb mode + case 0: + case 1: + banklo = (outerbank << 1) | 0; + bankhi = (outerbank << 1) | 1; + break; + //swap out C000-FFFF + case 2: + banklo = (outerprg << 1) | 0; + bankhi = bank; + break; + //swap out 8000-BFFF + case 3: + banklo = bank; + bankhi = (outerprg << 1) | 1; + break; + } + mem_setprg16(0x8,banklo); + mem_setprg16(0xC,bankhi); + mem_setvram8(0,chr); + switch(bankmode & 3) { + case 0: mem_setmirroring(MIRROR_1L); break; + case 1: mem_setmirroring(MIRROR_1H); break; + case 2: mem_setmirroring(MIRROR_V); break; + case 3: mem_setmirroring(MIRROR_H); break; + } +} + +static void write_select(u32 addr,u8 data) +{ + select = data; +} + +static void write_register(u32 addr,u8 data) +{ + switch(select) { + case 0x00: + chr = data & 3; + if((bankmode & 2) == 0) + bankmode = (bankmode & 0xFE) | ((data >> 4) & 1); + break; + case 0x01: + prg = data & 0xF; + if((bankmode & 2) == 0) + bankmode = (bankmode & 0xFE) | ((data >> 4) & 1); + break; + case 0x80: + bankmode = data & 0x3F; + break; + case 0x81: + outerprg = data & 0x3F; + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + mem_setvramsize(8 * 4); + mem_setwritefunc(5,write_select); + for(i=8;i<16;i++) + mem_setwritefunc(i,write_register); + if(hard) { + select = 0; + prg = 0xF; + chr = 3; + bankmode = 0x3F; + outerprg = 0x3F; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(select); + STATE_U8(bankmode); + STATE_U8(outerprg); + STATE_U8(prg); + STATE_U8(chr); + sync(); +} + +MAPPER(B_TEPPLES,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/irem/h3001.c b/apps/nesemu2/src/mappers/boards/irem/h3001.c new file mode 100644 index 00000000..ee6afe70 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/irem/h3001.c @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg[3],chr[8],mirror; +static u8 irqenabled; +static u16 irqcounter,irqlatch; + +static void sync() +{ + int i; + + mem_setwram8(6,0); + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); + mem_setmirroring(mirror); +} + +static void write_reg(u32 addr,u8 data) +{ + switch(addr & 0xF007) { + case 0x8000: + prg[0] = data; + break; + case 0x9001: + mirror = (data >> 7) ^ 1; + break; + case 0x9003: + irqenabled = data >> 7; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0x9004: + irqcounter = irqlatch; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0x9005: + irqlatch = (data << 8) | (irqlatch & 0xFF); + break; + case 0x9006: + irqlatch = data | (irqlatch & 0xFF00); + break; + case 0xA000: + prg[1] = data; + break; + case 0xB000: + case 0xB001: + case 0xB002: + case 0xB003: + case 0xB004: + case 0xB005: + case 0xB006: + case 0xB007: + chr[addr & 7] = data; + break; + case 0xC000: + prg[2] = data; + break; + } + sync(); +} + +static void cpucycle() +{ + if(irqenabled == 0) + return; + irqcounter--; + if(irqcounter == 0) { + irqenabled = 0; + cpu_set_irq(IRQ_MAPPER); + } +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + for(i=8;i<0x10;i++) + mem_setwritefunc(i,write_reg); + prg[0] = 0; + prg[1] = 1; + prg[2] = 0xFE; + for(i=0;i<8;i++) + chr[i] = i; + irqcounter = 0; + irqenabled = 0; + irqlatch = 0; + mirror = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,3); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(irqenabled); + STATE_U16(irqcounter); + STATE_U16(irqlatch); + sync(); +} + +MAPPER(B_IREM_H3001,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/irem/holydiver.c b/apps/nesemu2/src/mappers/boards/irem/holydiver.c new file mode 100644 index 00000000..2abc996b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/irem/holydiver.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,latch_data & 7); + mem_setprg16(0xC,0xFF); + mem_setchr8(0,(latch_data >> 4) & 0xF); + mem_setmirroring((latch_data >> 3) & 1); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_IREM_HOLYDIVER,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/irem/tam-s1.c b/apps/nesemu2/src/mappers/boards/irem/tam-s1.c new file mode 100644 index 00000000..6d922022 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/irem/tam-s1.c @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,0xFF); + mem_setprg16(0xC,latch_data); + mem_setvram8(0,0); + switch(latch_data >> 6) { + case 0: mem_setmirroring(MIRROR_1L); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_V); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_IREM_TAM_S1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/jaleco/jf11.c b/apps/nesemu2/src/mappers/boards/jaleco/jf11.c new file mode 100644 index 00000000..5f10dd88 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/jaleco/jf11.c @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,(latch_data & 0xF0) >> 4); + mem_setchr8(0,latch_data & 0xF); +} + +static void reset(int hard) +{ + int i; + + latch_reset(sync,hard); + for(i=8;i<16;i++) + mem_setwritefunc(i,0); + mem_setwritefunc(6,latch_write); + mem_setwritefunc(7,latch_write); +} + +MAPPER(B_JALECO_JF11,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/jaleco/jf16.c b/apps/nesemu2/src/mappers/boards/jaleco/jf16.c new file mode 100644 index 00000000..fe7b34ef --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/jaleco/jf16.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,latch_data & 7); + mem_setprg16(0xC,0xFF); + mem_setchr8(0,(latch_data >> 4) & 0xF); + mem_setmirroring(((latch_data >> 3) & 1) ? MIRROR_1H : MIRROR_1L); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_JALECO_JF16,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/jaleco/ss88006.c b/apps/nesemu2/src/mappers/boards/jaleco/ss88006.c new file mode 100644 index 00000000..7b29a777 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/jaleco/ss88006.c @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg[3],chr[8],mirror,irqcontrol; +static u16 irqlatch,irqcounter,irqmask; + +static void sync() +{ + int i; + + mem_setwram8(6,0); + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); + switch(mirror) { + case 0: mem_setmirroring(MIRROR_H); break; + case 1: mem_setmirroring(MIRROR_V); break; + case 2: mem_setmirroring(MIRROR_1L); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } + if(irqcontrol & 8) irqmask = 0xF; + else if(irqcontrol & 4) irqmask = 0xFF; + else if(irqcontrol & 2) irqmask = 0xFFF; + else irqmask = 0xFFFF; +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xF003) { + case 0x8000: prg[0] = (prg[0] & 0xF0) | ((data << 0) & 0x0F); break; + case 0x8001: prg[0] = (prg[0] & 0x0F) | ((data << 4) & 0xF0); break; + case 0x8002: prg[1] = (prg[1] & 0xF0) | ((data << 0) & 0x0F); break; + case 0x8003: prg[1] = (prg[1] & 0x0F) | ((data << 4) & 0xF0); break; + case 0x9000: prg[2] = (prg[2] & 0xF0) | ((data << 0) & 0x0F); break; + case 0x9001: prg[2] = (prg[2] & 0x0F) | ((data << 4) & 0xF0); break; + case 0x9002: break; + case 0x9003: break; + case 0xA000: chr[0] = (chr[0] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xA001: chr[0] = (chr[0] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xA002: chr[1] = (chr[1] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xA003: chr[1] = (chr[1] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xB000: chr[2] = (chr[2] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xB001: chr[2] = (chr[2] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xB002: chr[3] = (chr[3] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xB003: chr[3] = (chr[3] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xC000: chr[4] = (chr[4] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xC001: chr[4] = (chr[4] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xC002: chr[5] = (chr[5] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xC003: chr[5] = (chr[5] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xD000: chr[6] = (chr[6] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xD001: chr[6] = (chr[6] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xD002: chr[7] = (chr[7] & 0xF0) | ((data << 0) & 0x0F); break; + case 0xD003: chr[7] = (chr[7] & 0x0F) | ((data << 4) & 0xF0); break; + case 0xE000: irqlatch = (irqlatch & 0xFFF0) | ((data & 0xF) << 0); break; + case 0xE001: irqlatch = (irqlatch & 0xFF0F) | ((data & 0xF) << 4); break; + case 0xE002: irqlatch = (irqlatch & 0xF0FF) | ((data & 0xF) << 8); break; + case 0xE003: irqlatch = (irqlatch & 0x0FFF) | ((data & 0xF) << 12); break; + case 0xF000: irqcounter = irqlatch; cpu_clear_irq(IRQ_MAPPER); break; + case 0xF001: irqcontrol = data & 0xF; cpu_clear_irq(IRQ_MAPPER); break; + case 0xF002: mirror = data & 3; break; + case 0xF003: break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + prg[0] = 0; + prg[1] = 1; + prg[2] = 0xFE; + for(i=0;i<8;i++) + chr[i] = i; + mirror = irqcontrol = 0; + irqmask = 0; + irqlatch = irqcounter = 0; + sync(); +} + +static void cpucycle() +{ + if(irqcontrol & 1) { + if((irqcounter & irqmask) == 0) { + cpu_set_irq(IRQ_MAPPER); + irqcounter = 0; + irqcounter += irqmask + 1; + } + irqcounter--; + } +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,3); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(irqcontrol); + STATE_U16(irqcounter); + STATE_U16(irqlatch); + STATE_U16(irqmask); + sync(); +} + +MAPPER(B_JALECO_SS88006,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/kaiser/ks202.c b/apps/nesemu2/src/mappers/boards/kaiser/ks202.c new file mode 100644 index 00000000..693c8bd9 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/kaiser/ks202.c @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 control,prg[4],chr[8],mirror; +static u8 irqenable; +static u16 irqlatch,irqcounter; + +static void sync() +{ + int i; + + mem_setwram8(6,0); + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0x8000: irqlatch = (irqlatch & 0xFFF0) | ((data & 0xF) << 0); break; + case 0x9000: irqlatch = (irqlatch & 0xFF0F) | ((data & 0xF) << 4); break; + case 0xA000: irqlatch = (irqlatch & 0xF0FF) | ((data & 0xF) << 8); break; + case 0xB000: irqlatch = (irqlatch & 0x0FFF) | ((data & 0xF) << 12); break; + case 0xC000: + irqenable = data & 7; + if(irqenable & 2) + irqcounter = irqlatch; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xD000: + if(irqenable & 1) + irqenable |= 2; + else + irqenable &= ~2; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xE000: + control = data & 0xF; + break; + case 0xF000: + switch(control) { + case 1: + case 2: + case 3: + case 4: + prg[control - 1] = (prg[control - 1] & 0x10) | (data & 0xF); + break; + } + switch(addr & 0xFC00) { + case 0xF000: + prg[addr & 3] &= 0xF; + prg[addr & 3] |= (data & 0x10); + break; + case 0xF400: + break; + case 0xF800: + mirror = data & 1; + break; + case 0xFC00: + chr[addr & 7] = data; + break; + } + sync(); + break; + } +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + for(i=0;i<8;i++) { + prg[i & 3] = 0; + chr[i] = 0; + } + mirror = 0; + irqenable = 0; + irqlatch = irqcounter = 0; + } + sync(); +} + +static void cpucycle() +{ + if((irqenable & 2) == 0) + return; + if(irqcounter == 0xFFFF) { + irqcounter = irqlatch; + cpu_set_irq(IRQ_MAPPER); + } + else + irqcounter++; +} + +static void state(int mode,u8 *data) +{ + +} + +MAPPER(B_KAISER_KS202,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/konami/vrc1.c b/apps/nesemu2/src/mappers/boards/konami/vrc1.c new file mode 100644 index 00000000..e4f08257 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/konami/vrc1.c @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg[3],chr[2],mirror; + +static void sync() +{ + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,-1); + mem_setchr4(0,chr[0]); + mem_setchr4(4,chr[1]); + if(mirror) + mem_setmirroring(MIRROR_H); + else + mem_setmirroring(MIRROR_V); +} + +static void write(u32 addr,u8 data) +{ + data &= 0xF; + switch(addr & 0xF000) { + case 0x8000: + prg[0] = data; + break; + case 0x9000: + mirror = data & 1; + chr[0] = (chr[0] & 0xF) | ((data & 2) << 3); + chr[1] = (chr[1] & 0xF) | ((data & 4) << 2); + break; + case 0xA000: + prg[1] = data; + break; + case 0xB000: + break; + case 0xC000: + prg[2] = data; + break; + case 0xD000: + break; + case 0xE000: + chr[0] = (chr[0] & 0x10) | data; + break; + case 0xF000: + chr[1] = (chr[1] & 0x10) | data; + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<0x10;i++) + mem_setwritefunc(i,write); + prg[0] = 0; + prg[1] = 0; + prg[2] = 0; + chr[0] = 0; + chr[1] = 0; + mirror = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,3); + STATE_ARRAY_U8(chr,2); + STATE_U8(mirror); + sync(); +} + +MAPPER(B_KONAMI_VRC1,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/konami/vrc2.c b/apps/nesemu2/src/mappers/boards/konami/vrc2.c new file mode 100644 index 00000000..3e8f0b43 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/konami/vrc2.c @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 vrc2a_map[] = {0,1,2,3,1}; +static u8 vrc2b_map[] = {0,2,1,3,0}; +static u8 vrc2c_map[] = {0,1,2,3,0}; +static u8 *map; +static u8 prg[2],chr[8],mirror,latch; + +static void sync() +{ + int i; + + if(nes->cart->wram.size) + mem_setwram4(7,0); + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg16(0xC,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i] >> map[4]); + switch(mirror) { + case 0: mem_setmirroring(MIRROR_V); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_1L); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } +} + +static u8 read_latch(u32 addr) +{ + return(((addr >> 8) & 0xFE) | latch); +} + +static void write_latch(u32 addr,u8 data) +{ + latch = data & 1; +} + +static void write(u32 addr,u8 data) +{ + data &= 0x1F; + switch(addr & 0xF000) { + case 0x8000: + prg[0] = data; + break; + case 0x9000: + mirror = data & 1; + break; + case 0xA000: + prg[1] = data; + break; + case 0xB000: + switch(map[addr & 3]) { + case 0: chr[0] = (chr[0] & 0xF0) | ((data << 0) & 0x0F); break; + case 1: chr[1] = (chr[1] & 0xF0) | ((data << 0) & 0x0F); break; + case 2: chr[0] = (chr[0] & 0x0F) | ((data << 4) & 0xF0); break; + case 3: chr[1] = (chr[1] & 0x0F) | ((data << 4) & 0xF0); break; + } + break; + case 0xC000: + switch(map[addr & 3]) { + case 0: chr[2] = (chr[2] & 0xF0) | ((data << 0) & 0x0F); break; + case 1: chr[3] = (chr[3] & 0xF0) | ((data << 0) & 0x0F); break; + case 2: chr[2] = (chr[2] & 0x0F) | ((data << 4) & 0xF0); break; + case 3: chr[3] = (chr[3] & 0x0F) | ((data << 4) & 0xF0); break; + } + break; + case 0xD000: + switch(map[addr & 3]) { + case 0: chr[4] = (chr[4] & 0xF0) | ((data << 0) & 0x0F); break; + case 1: chr[5] = (chr[5] & 0xF0) | ((data << 0) & 0x0F); break; + case 2: chr[4] = (chr[4] & 0x0F) | ((data << 4) & 0xF0); break; + case 3: chr[5] = (chr[5] & 0x0F) | ((data << 4) & 0xF0); break; + } + break; + case 0xE000: + switch(map[addr & 3]) { + case 0: chr[6] = (chr[6] & 0xF0) | ((data << 0) & 0x0F); break; + case 1: chr[7] = (chr[7] & 0xF0) | ((data << 0) & 0x0F); break; + case 2: chr[6] = (chr[6] & 0x0F) | ((data << 4) & 0xF0); break; + case 3: chr[7] = (chr[7] & 0x0F) | ((data << 4) & 0xF0); break; + } + break; + } + sync(); +} + +static void reset(int revision) +{ + int i; + + switch(revision) { + default: + case B_KONAMI_VRC2A: map = vrc2a_map; break; + case B_KONAMI_VRC2B: map = vrc2b_map; break; + case B_KONAMI_VRC2C: map = vrc2c_map; break; + } + mem_setreadfunc(6,read_latch); + mem_setwritefunc(6,write_latch); + for(i=8;i<0x10;i++) + mem_setwritefunc(i,write); + for(i=0;i<8;i++) { + prg[i & 1] = 0; + chr[i] = 0; + } + mirror = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(latch); + sync(); +} + +static void reset_vrc2a(int hard) { reset(B_KONAMI_VRC2A); } +static void reset_vrc2b(int hard) { reset(B_KONAMI_VRC2B); } +static void reset_vrc2c(int hard) { reset(B_KONAMI_VRC2C); } + +MAPPER(B_KONAMI_VRC2A,reset_vrc2a,0,0,state); +MAPPER(B_KONAMI_VRC2B,reset_vrc2b,0,0,state); +MAPPER(B_KONAMI_VRC2C,reset_vrc2c,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/konami/vrc3.c b/apps/nesemu2/src/mappers/boards/konami/vrc3.c new file mode 100644 index 00000000..f621fc95 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/konami/vrc3.c @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg; +static u8 irqcontrol; +static u32 irqreload,irqcounter; + +static void sync() +{ + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xFF); + mem_setvram8(0,0); + mem_setwram8(6,0); +} + +static void write(u32 addr,u8 data) +{ + data &= 0xF; + switch(addr & 0xF000) { + case 0x8000: + irqreload = (irqreload & 0xFFF0) | (data << 0); + break; + case 0x9000: + irqreload = (irqreload & 0xFF0F) | (data << 4); + break; + case 0xA000: + irqreload = (irqreload & 0xF0FF) | (data << 8); + break; + case 0xB000: + irqreload = (irqreload & 0x0FFF) | (data << 12); + break; + case 0xC000: + irqcontrol = data & 7; + if(data & 2) + irqcounter = irqreload; + case 0xD000: + cpu_clear_irq(IRQ_MAPPER); + irqcontrol |= (irqcontrol & 1) << 1; + break; + case 0xF000: + prg = data; + sync(); + break; + } +} + +static void reset(int hard) +{ + int i; + + mem_setvramsize(8); + mem_setwramsize(8); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + prg = 0; + irqcontrol = 0; + irqreload = 0; + irqcounter = 0; + sync(); +} + +static void cpucycle() +{ + //see if it is enabled + if((irqcontrol & 2) == 0) + return; + + //16bit mode + if((irqcontrol & 4) == 0) { + if(irqcounter == 0xFFFF) { + irqcounter = irqreload; + cpu_set_irq(IRQ_MAPPER); + } + else + irqcounter++; + } + + //8bit mode + else { + if((irqcounter & 0xFF) == 0xFF) { + irqcounter = (irqcounter & 0xFF00) | (irqreload & 0xFF); + cpu_set_irq(IRQ_MAPPER); + } + else + irqcounter++; + } +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(irqcontrol); + STATE_U32(irqreload); + STATE_U32(irqcounter); + sync(); +} + +MAPPER(B_KONAMI_VRC3,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/konami/vrc4.c b/apps/nesemu2/src/mappers/boards/konami/vrc4.c new file mode 100644 index 00000000..99ec33e3 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/konami/vrc4.c @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 vrc4a_map[] = {0,2,1,3,1}; +static u8 vrc4b_map[] = {0,1,2,3,0}; +static u8 vrc4c_map[] = {0,2,1,3,6}; +static u8 vrc4d_map[] = {0,1,2,3,2}; +static u8 vrc4e_map[] = {0,2,1,3,2}; +static u8 *map; + +static u8 prgflip; + +static u8 prg[2],mirror; +static u8 irqcontrol,irqlatch,irqcounter; +static u16 chr[8]; +static int irqprescaler; + +static void sync() +{ + int i; + + mem_setwram8(0x6,0); + if(prgflip) { + mem_setprg8(0x8,0xFE); + mem_setprg8(0xC,prg[0]); + } + else { + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xC,0xFE); + } + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); + switch(mirror & 3) { + case 0: mem_setmirroring(MIRROR_V); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_1H); break; + case 3: mem_setmirroring(MIRROR_1L); break; + } +} + +static void write(u32 addr,u8 data) +{ +// log_message("vrc4 write: $%04X = $%02X\n",addr,data); + data &= 0x1F; + switch(addr & 0xF000) { + case 0x8000: + prg[0] = data; + break; + case 0x9000: + switch(map[(addr >> map[4]) & 3]) { + case 0: + case 1: mirror = data & 3; break; + case 2: + case 3: prgflip = data & 2; break; + } + break; + case 0xA000: + prg[1] = data; + break; + case 0xB000: + switch(map[(addr >> map[4]) & 3]) { + case 0: chr[0] = (chr[0] & 0x1F0) | ((data << 0) & 0x00F); break; + case 1: chr[1] = (chr[1] & 0x1F0) | ((data << 0) & 0x00F); break; + case 2: chr[0] = (chr[0] & 0x00F) | ((data << 4) & 0x1F0); break; + case 3: chr[1] = (chr[1] & 0x00F) | ((data << 4) & 0x1F0); break; + } + break; + case 0xC000: + switch(map[(addr >> map[4]) & 3]) { + case 0: chr[2] = (chr[2] & 0x1F0) | ((data << 0) & 0x00F); break; + case 1: chr[3] = (chr[3] & 0x1F0) | ((data << 0) & 0x00F); break; + case 2: chr[2] = (chr[2] & 0x00F) | ((data << 4) & 0x1F0); break; + case 3: chr[3] = (chr[3] & 0x00F) | ((data << 4) & 0x1F0); break; + } + break; + case 0xD000: + switch(map[(addr >> map[4]) & 3]) { + case 0: chr[4] = (chr[4] & 0x1F0) | ((data << 0) & 0x00F); break; + case 1: chr[5] = (chr[5] & 0x1F0) | ((data << 0) & 0x00F); break; + case 2: chr[4] = (chr[4] & 0x00F) | ((data << 4) & 0x1F0); break; + case 3: chr[5] = (chr[5] & 0x00F) | ((data << 4) & 0x1F0); break; + } + break; + case 0xE000: + switch(map[(addr >> map[4]) & 3]) { + case 0: chr[6] = (chr[6] & 0x1F0) | ((data << 0) & 0x00F); break; + case 1: chr[7] = (chr[7] & 0x1F0) | ((data << 0) & 0x00F); break; + case 2: chr[6] = (chr[6] & 0x00F) | ((data << 4) & 0x1F0); break; + case 3: chr[7] = (chr[7] & 0x00F) | ((data << 4) & 0x1F0); break; + } + break; + case 0xF000: + switch(map[(addr >> map[4]) & 3]) { + case 0: + irqlatch = (irqlatch & 0xF0) | (data & 0xF); + break; + case 1: + irqcontrol = data; + if(data & 2) { + irqcounter = irqlatch; + irqprescaler = 341; + } + cpu_clear_irq(IRQ_MAPPER); + break; + case 2: + irqlatch = (irqlatch & 0x0F) | ((data & 0xF) << 4); + break; + case 3: + cpu_clear_irq(IRQ_MAPPER); + irqcontrol |= (irqcontrol & 1) << 1; + break; + } + break; + } + sync(); +} + +static void clockirq() +{ + if(irqcounter == 0xFF) { + irqcounter = irqlatch; + cpu_set_irq(IRQ_MAPPER); + } + else + irqcounter++; +} + +static void ppucycle() +{ + if((irqcontrol & 2) == 0) + return; + + if((irqcontrol & 4) == 0) { + irqprescaler -= 2; + if(irqprescaler <= 0) { + irqprescaler += 341; + clockirq(); + } + } +} + +static void cpucycle() +{ + if((irqcontrol & 2) == 0) + return; + + if(irqcontrol & 4) + clockirq(); +} + +static void reset(int revision) +{ + int i; + + switch(revision) { + default: + case B_KONAMI_VRC4A: map = vrc4a_map; break; + case B_KONAMI_VRC4B: map = vrc4b_map; break; + case B_KONAMI_VRC4C: map = vrc4c_map; break; + case B_KONAMI_VRC4D: map = vrc4d_map; break; + case B_KONAMI_VRC4E: map = vrc4e_map; break; + } + mem_setwramsize(8); + for(i=8;i<0x10;i++) + mem_setwritefunc(i,write); + for(i=0;i<8;i++) { + prg[i & 1] = 0; + chr[i] = 0; + } + mirror = 0; + irqcontrol = 0; + irqlatch = 0; + irqcounter = 0; + irqprescaler = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U16(chr,8); + STATE_U8(mirror); + STATE_U8(irqcontrol); + STATE_U8(irqlatch); + STATE_U8(irqcounter); + STATE_INT(irqprescaler); + sync(); +} + +static void reset_vrc4a(int hard) { reset(B_KONAMI_VRC4A); } +static void reset_vrc4b(int hard) { reset(B_KONAMI_VRC4B); } +static void reset_vrc4c(int hard) { reset(B_KONAMI_VRC4C); } +static void reset_vrc4d(int hard) { reset(B_KONAMI_VRC4D); } +static void reset_vrc4e(int hard) { reset(B_KONAMI_VRC4E); } + +MAPPER(B_KONAMI_VRC4A,reset_vrc4a,ppucycle,cpucycle,state); +MAPPER(B_KONAMI_VRC4B,reset_vrc4b,ppucycle,cpucycle,state); +MAPPER(B_KONAMI_VRC4C,reset_vrc4c,ppucycle,cpucycle,state); +MAPPER(B_KONAMI_VRC4D,reset_vrc4d,ppucycle,cpucycle,state); +MAPPER(B_KONAMI_VRC4E,reset_vrc4e,ppucycle,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/konami/vrc6.c b/apps/nesemu2/src/mappers/boards/konami/vrc6.c new file mode 100644 index 00000000..7164239e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/konami/vrc6.c @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/sound/s_VRC6.h" +#include "nes/nes.h" + +static apu_external_t vrc6 = { + VRC6sound_Load, + VRC6sound_Unload, + VRC6sound_Reset, + VRC6sound_Get, + 0 +}; + +static u8 vrc6a_map[] = {0,1,2,3}; +static u8 vrc6b_map[] = {0,2,1,3}; +static u8 *map; +static u8 prg[2],chr[8],mirror; +static u8 irqcontrol,irqlatch,irqcounter; +static int irqprescaler; + +static void sync() +{ + int i; + + mem_setprg16(0x8,prg[0]); + mem_setprg8(0xC,prg[1]); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); + switch(mirror & 0xC) { + case 0x0: mem_setmirroring(MIRROR_V); break; + case 0x4: mem_setmirroring(MIRROR_H); break; + case 0x8: mem_setmirroring(MIRROR_1L); break; + case 0xC: mem_setmirroring(MIRROR_1H); break; + } +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0x8000: + prg[0] = data; + break; + case 0x9000: + break; + case 0xA000: + break; + case 0xB000: + switch(map[addr & 3]) { + case 0: + case 1: + case 2: + break; + case 3: + mirror = data & 0xC; + break; + } + break; + case 0xC000: + prg[1] = data; + break; + case 0xD000: + chr[map[addr & 3]] = data; + break; + case 0xE000: + chr[map[addr & 3] + 4] = data; + break; + case 0xF000: + switch(map[addr & 3]) { + case 0: + irqlatch = data; + break; + case 1: + irqcontrol = data; + if(data & 2) { + irqcounter = irqlatch; + irqprescaler = 341; + } + cpu_clear_irq(IRQ_MAPPER); + break; + case 2: + cpu_clear_irq(IRQ_MAPPER); + irqcontrol |= (irqcontrol & 1) << 1; + break; + } + break; + } + sync(); +} + +static void clockirq() +{ + if(irqcounter >= 0xFF) { + irqcounter = irqlatch; + cpu_set_irq(IRQ_MAPPER); + } + else + irqcounter++; +} + +static void ppucycle() +{ + if((irqcontrol & 2) == 0) + return; + + if((irqcontrol & 4) == 0) { + irqprescaler -= 2; + if(irqprescaler <= 0) { + irqprescaler += 341; + clockirq(); + } + } +} + +static void cpucycle() +{ + if((irqcontrol & 2) == 0) + return; + + if(irqcontrol & 4) + clockirq(); +} + +static void reset(int revision,int hard) +{ + int i; + + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(revision == B_KONAMI_VRC6B) { + map = vrc6b_map; + mem_setwramsize(8); + mem_setwram8(0x6,0); + } + else + map = vrc6a_map; + prg[0] = 0; + prg[1] = (u8)-2; + for(i=0;i<8;i++) + chr[i] = 0; + mirror = 0; + irqlatch = 0; + irqcounter = 0; + irqcontrol = 0; + apu_setexternal(&vrc6); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(irqlatch); + STATE_U8(irqcounter); + STATE_U8(irqcontrol); + STATE_INT(irqprescaler); + sync(); +} + +static void reset_vrc6a(int hard) { reset(B_KONAMI_VRC6A,hard); } +static void reset_vrc6b(int hard) { reset(B_KONAMI_VRC6B,hard); } + +MAPPER(B_KONAMI_VRC6A,reset_vrc6a,ppucycle,cpucycle,state); +MAPPER(B_KONAMI_VRC6B,reset_vrc6b,ppucycle,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/konami/vrc7.c b/apps/nesemu2/src/mappers/boards/konami/vrc7.c new file mode 100644 index 00000000..84a53aba --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/konami/vrc7.c @@ -0,0 +1,222 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/sound/s_VRC7.h" +#include "nes/nes.h" + +static apu_external_t vrc7 = { + VRC7sound_Load, + VRC7sound_Unload, + VRC7sound_Reset, + VRC7sound_Get, + 0 +}; + +static u8 prg[3],chr[8],mirror; +static u8 irqlatch,irqcontrol,irqcounter; +static int irqprescaler; + +static void sync() +{ + int i; + + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,0xFF); + if(nes->cart->chr.size) { + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); + } + else { + for(i=0;i<8;i++) + mem_setvram1(i,chr[i] & 7); + } + switch(mirror) { + case 0: mem_setmirroring(MIRROR_V); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_1L); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } +} + +static void write_8000(u32 addr,u8 data) +{ + if(addr & 0x18) + prg[1] = data; + else + prg[0] = data; + sync(); +} + +static void write_9000(u32 addr,u8 data) +{ + if(addr & 0x18) { + VRC7sound_Write(addr,data); + } + else { + prg[2] = data; + sync(); + } +} + +static void write_A000(u32 addr,u8 data) +{ + if(addr & 0x18) + chr[1] = data; + else + chr[0] = data; + sync(); +} + +static void write_B000(u32 addr,u8 data) +{ + if(addr & 0x18) + chr[3] = data; + else + chr[2] = data; + sync(); +} + +static void write_C000(u32 addr,u8 data) +{ + if(addr & 0x18) + chr[5] = data; + else + chr[4] = data; + sync(); +} + +static void write_D000(u32 addr,u8 data) +{ + if(addr & 0x18) + chr[7] = data; + else + chr[6] = data; + sync(); +} + +static void write_E000(u32 addr,u8 data) +{ + if(addr & 0x18) + irqlatch = data; + else + mirror = data & 3; +} + +static void write_F000(u32 addr,u8 data) +{ + if(addr & 0x18) { + irqcontrol &= ~2; + irqcontrol |= (irqcontrol & 1) << 1; + cpu_clear_irq(IRQ_MAPPER); + } + else { + irqcontrol = data & 7; + if(data & 2) { + irqcounter = irqlatch; + irqprescaler = 341; + } + } +} + +static void clockirq() +{ + if(irqcounter >= 0xFF) { + irqcounter = irqlatch; + cpu_set_irq(IRQ_MAPPER); + } + else + irqcounter++; +} + +static void ppucycle() +{ + if((irqcontrol & 2) == 0) + return; + + if((irqcontrol & 4) == 0) { + irqprescaler -= 2; + if(irqprescaler <= 0) { + irqprescaler += 341; + clockirq(); + } + } +} + +static void cpucycle() +{ + if((irqcontrol & 2) == 0) + return; + + if(irqcontrol & 4) + clockirq(); +} + +static void reset(int revision,int hard) +{ + int i; + + mem_setwritefunc(0x8,write_8000); + mem_setwritefunc(0x9,write_9000); + mem_setwritefunc(0xA,write_A000); + mem_setwritefunc(0xB,write_B000); + mem_setwritefunc(0xC,write_C000); + mem_setwritefunc(0xD,write_D000); + mem_setwritefunc(0xE,write_E000); + mem_setwritefunc(0xF,write_F000); + if(revision == B_KONAMI_VRC7A) { //lagrange point + mem_setvramsize(8); + mem_setvram8(0,0); + } + mem_setwramsize(8); + mem_setwram8(6,0); + prg[0] = 0; + prg[1] = (u8)-2; + prg[2] = (u8)-2; + for(i=0;i<8;i++) + chr[i] = 0; + mirror = 0; + irqlatch = 0; + irqcontrol = 0; + irqcounter = 0; + irqprescaler = 341; + apu_setexternal(&vrc7); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(irqcontrol); + STATE_U8(irqlatch); + STATE_U8(irqcounter); + STATE_INT(irqprescaler); + sync(); +} + +static void reset_vrc7a(int hard) { reset(B_KONAMI_VRC7A,hard); } +static void reset_vrc7b(int hard) { reset(B_KONAMI_VRC7B,hard); } + +MAPPER(B_KONAMI_VRC7A,reset_vrc7a,ppucycle,cpucycle,state); +MAPPER(B_KONAMI_VRC7B,reset_vrc7b,ppucycle,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/namcot/163.c b/apps/nesemu2/src/mappers/boards/namcot/163.c new file mode 100644 index 00000000..6db480f9 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/namcot/163.c @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/namcot-163.h" + +static void reset(int hard) +{ + namcot163_reset(namcot163_sync,hard); +} + +MAPPER(B_NAMCOT_163,reset,0,namcot163_cpucycle,namcot163_state); diff --git a/apps/nesemu2/src/mappers/boards/namcot/3425.c b/apps/nesemu2/src/mappers/boards/namcot/3425.c new file mode 100644 index 00000000..f20c3370 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/namcot/3425.c @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/namcot-108.h" + +static void sync() +{ + u8 *reg = namcot108_getregs(); + + namcot108_sync(); + mem_setnt1(0x8,reg[0] >> 5); + mem_setnt1(0x9,reg[0] >> 5); + mem_setnt1(0xA,reg[1] >> 5); + mem_setnt1(0xB,reg[1] >> 5); + mem_setnt1(0xC,reg[2] >> 5); + mem_setnt1(0xD,reg[3] >> 5); + mem_setnt1(0xE,reg[4] >> 5); + mem_setnt1(0xF,reg[5] >> 5); +} + +static void reset(int hard) +{ + namcot108_reset(sync,hard); +} + +MAPPER(B_NAMCOT_3425,reset,0,0,namcot108_state); diff --git a/apps/nesemu2/src/mappers/boards/namcot/34x3.c b/apps/nesemu2/src/mappers/boards/namcot/34x3.c new file mode 100644 index 00000000..0b375781 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/namcot/34x3.c @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/namcot-108.h" + +static u8 mirror; + +static void sync_34x3() +{ + u8 *reg = namcot108_getregs(); + + mem_setchr2(0,(reg[0] & 0x3F) >> 1); + mem_setchr2(2,(reg[1] & 0x3F) >> 1); + mem_setchr1(4,reg[2] | 0x40); + mem_setchr1(5,reg[3] | 0x40); + mem_setchr1(6,reg[4] | 0x40); + mem_setchr1(7,reg[5] | 0x40); + mem_setprg8(0x8,reg[6]); + mem_setprg8(0xA,reg[7]); + mem_setprg16(0xC,0xFF); +} + +static void sync_3453() +{ + sync_34x3(); + mem_setmirroring(mirror); +} + +static void write_3453(u32 addr,u8 data) +{ + mirror = MIRROR_1L + ((data & 0x40) >> 6); + namcot108_write(addr,data); +} + +static void reset_34x3(int hard) +{ + namcot108_reset(sync_34x3,hard); +} + +static void reset_3453(int hard) +{ + int i; + + namcot108_reset(sync_3453,hard); + for(i=8;i<16;i++) + mem_setwritefunc(i,write_3453); +} + +MAPPER(B_NAMCOT_34x3,reset_34x3,0,0,namcot108_state); +MAPPER(B_NAMCOT_3453,reset_3453,0,0,namcot108_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/axrom.c b/apps/nesemu2/src/mappers/boards/nintendo/axrom.c new file mode 100644 index 00000000..c4ba7dd1 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/axrom.c @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_data & 0xF); + mem_setvram8(0,0); + if(latch_data & 0x10) + mem_setmirroring(MIRROR_1H); + else + mem_setmirroring(MIRROR_1L); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_NINTENDO_AxROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/bxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/bxrom.c new file mode 100644 index 00000000..65f49a51 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/bxrom.c @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_data); + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_NINTENDO_BxROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/cnrom.c b/apps/nesemu2/src/mappers/boards/nintendo/cnrom.c new file mode 100644 index 00000000..b4ed5ca0 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/cnrom.c @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setchr8(0,latch_data & 7); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); + mem_setprg16(0x8,0); + mem_setprg16(0xC,(u32)-1); +} + +MAPPER(B_NINTENDO_CNROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/cnrom_cp.c b/apps/nesemu2/src/mappers/boards/nintendo/cnrom_cp.c new file mode 100644 index 00000000..f0b6a003 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/cnrom_cp.c @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static u8 readchr(u32 addr) +{ + return(0x12); +} + +static void sync() +{ + int i; + + //chr enabled + if((latch_data & 0xF) && latch_data != 0x13) { + mem_setchr8(0,0); + for(i=0;i<8;i++) + nes->ppu.readfuncs[i] = 0; + } + + //chr disabled + else { + mem_unsetppu8(0); + for(i=0;i<8;i++) + nes->ppu.readfuncs[i] = readchr; + } +} + +static void reset(int hard) +{ + latch_reset(sync,hard); + mem_setprg16(0x8,0); + mem_setprg16(0xC,(u32)-1); +} + +MAPPER(B_NINTENDO_CNROM_CP,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/cprom.c b/apps/nesemu2/src/mappers/boards/nintendo/cprom.c new file mode 100644 index 00000000..8d576a4c --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/cprom.c @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setvram4(0,0); + mem_setvram4(4,latch_data & 3); +} + +static void reset(int hard) +{ + mem_setvramsize(16); + latch_reset(sync,hard); + mem_setprg16(0x8,0); + mem_setprg16(0xC,(u32)-1); +} + +MAPPER(B_NINTENDO_CPROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/dxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/dxrom.c new file mode 100644 index 00000000..7b9d819d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/dxrom.c @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/namcot-108.h" + +static void reset(int hard) +{ + namcot108_reset(namcot108_sync,hard); +} + +MAPPER(B_NINTENDO_DxROM,reset,0,0,namcot108_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/event.c b/apps/nesemu2/src/mappers/boards/nintendo/event.c new file mode 100644 index 00000000..2e90b42e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/event.c @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc1.h" + +static u8 prglock,irqenabled,dip; +static u32 irqmax,irqcounter; + +static void sync() +{ + static u8 oldreg1 = 0x10; + u8 reg1 = mmc1_getlowchr(); + + mmc1_syncmirror(); + mmc1_syncsram(); + mem_setvram8(0,0); + if(reg1 & 0x10) { + irqenabled = 0; + irqcounter = 0; + cpu_clear_irq(IRQ_MAPPER); + } + else + irqenabled = 1; + if(prglock) { + mem_setprg32(8,0); + if((oldreg1 & 0x10) == 0 && (reg1 & 0x10) == 0x10) + prglock = 0; + } + else { + if(reg1 & 8) + mmc1_syncprg(7,8); + else + mem_setprg32(8,(reg1 >> 1) & 3); + } + oldreg1 = reg1; +} + +static void reset(int hard) +{ + mem_setwramsize(8); + mem_setvramsize(8); + prglock = 1; + irqcounter = 0; + irqenabled = 0; + dip = 4; + irqmax = 0x20000000 | (dip << 25); + mmc1_reset(C_MMC1B,sync,hard); +} + +static void cpucycle() +{ + if(irqenabled == 0) + return; + irqcounter++; + if(irqcounter >= irqmax) + cpu_set_irq(IRQ_MAPPER); +} + +static void state(int mode,u8 *data) +{ + if(mode >= 2)log_printf("mapper: B_NINTENDO_EVENT: mode = %d, dip = %X\n",mode,*data); + CFG_U8(dip); + STATE_U8(dip); + STATE_U8(prglock); + STATE_U8(irqenabled); + STATE_U32(irqcounter); + mmc1_state(mode,data); +} + +MAPPER(B_NINTENDO_EVENT,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/exrom.c b/apps/nesemu2/src/mappers/boards/nintendo/exrom.c new file mode 100644 index 00000000..92bbdb3b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/exrom.c @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc5.h" + +static void reset(int hard) +{ + mmc5_reset(hard); +} + +MAPPER(B_NINTENDO_ExROM,reset,mmc5_ppucycle,0,mmc5_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/fxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/fxrom.c new file mode 100644 index 00000000..60f286b1 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/fxrom.c @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc4.h" + +MAPPER(B_NINTENDO_FxROM,mmc4_init,0,0,mmc4_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/gxrom_mxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/gxrom_mxrom.c new file mode 100644 index 00000000..d045d747 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/gxrom_mxrom.c @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,(latch_data & 0xF0) >> 4); + if(nes->cart->chr.size) + mem_setchr8(0,latch_data & 0xF); + else + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_NINTENDO_GxROM_MxROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/hxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/hxrom.c new file mode 100644 index 00000000..c44616a3 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/hxrom.c @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void reset(int hard) +{ + mmc3_reset(C_MMC6,mmc3_sync,hard); +} + +MAPPER(B_NINTENDO_HxROM,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/nrom.c b/apps/nesemu2/src/mappers/boards/nintendo/nrom.c new file mode 100644 index 00000000..34d5fd87 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/nrom.c @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static void reset_nrom(int hard) +{ + mem_setprg16(0x8,0); + mem_setprg16(0xC,-1); + if(nes->cart->chr.size) { + mem_setchr8(0,0); + } + else { + mem_setvramsize(8); + mem_setvram8(0,0); + } +} + +static void reset_nrom_sram(int hard) +{ + reset_nrom(hard); + mem_setwramsize(8); + mem_setwram8(6,0); +} + +MAPPER(B_NINTENDO_NROM,reset_nrom,0,0,0); +MAPPER(B_NINTENDO_NROM_SRAM,reset_nrom_sram,0,0,0); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/pal_zz.c b/apps/nesemu2/src/mappers/boards/nintendo/pal_zz.c new file mode 100644 index 00000000..121549c1 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/pal_zz.c @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 andtable[8] = {7,7,7,7,0xF,0xF,0xF,7}; +static u8 ortable[8] = {0,0,0,8,0x10,0x10,0x10,0x18}; +static u8 reg; + +static void sync() +{ + mmc3_syncprg(andtable[reg],ortable[reg]); + mmc3_syncchr(0x7F,(reg & 4) << 5); + mmc3_syncmirror(); +} + +static void write67(u32 addr,u8 data) +{ +// if(mmc3_getsramenabled()) + reg = data & 7; + sync(); +} + +static void reset(int hard) +{ + reg = 0; + mmc3_reset(C_MMC3,sync,hard); + mem_unsetcpu8(6); + mem_setwritefunc(6,write67); + mem_setwritefunc(7,write67); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_NINTENDO_PAL_ZZ,reset,0,mmc3_ppucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/pxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/pxrom.c new file mode 100644 index 00000000..784868aa --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/pxrom.c @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc2.h" + +MAPPER(B_NINTENDO_PxROM,mmc2_reset,0,0,mmc2_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/qj.c b/apps/nesemu2/src/mappers/boards/nintendo/qj.c new file mode 100644 index 00000000..b9a9bd8d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/qj.c @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg; + +static void sync() +{ + mmc3_syncprg(0xF,reg << 4); + mmc3_syncchr(0x7F,reg << 7); + mmc3_syncmirror(); +} + +static void write67(u32 addr,u8 data) +{ + if(mmc3_getsramenabled()) + reg = data; + sync(); +} + +static void reset(int hard) +{ + reg = 0; + mmc3_reset(C_MMC3,sync,hard); + mem_unsetcpu8(6); + mem_setwritefunc(6,write67); + mem_setwritefunc(7,write67); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_NINTENDO_QJ,reset,0,mmc3_ppucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/sxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/sxrom.c new file mode 100644 index 00000000..2df6504d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/sxrom.c @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc1.h" + +static void reset(int type,int hard) +{ + if(nes->cart->chr.size == 0) + mem_setvramsize(8); + mem_setwramsize(nes->cart->wram.size); + mmc1_reset(type,mmc1_sync,hard); +} + +static void reset_a(int hard) { reset(C_MMC1A,hard); } +static void reset_b(int hard) { reset(C_MMC1B,hard); } +static void reset_c(int hard) { reset(C_MMC1C,hard); } + +MAPPER(B_NINTENDO_SxROM_MMC1A,reset_a,0,0,mmc1_state); +MAPPER(B_NINTENDO_SxROM_MMC1B,reset_b,0,0,mmc1_state); +MAPPER(B_NINTENDO_SxROM_MMC1C,reset_c,0,0,mmc1_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/tqrom.c b/apps/nesemu2/src/mappers/boards/nintendo/tqrom.c new file mode 100644 index 00000000..a090fe93 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/tqrom.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void sync() +{ + int i,j; + + mmc3_syncprg(0x3F,0); + for(i=0;i<8;i++) { + j = mmc3_getchrbank(i); + if(j & 0x40) + mem_setvram1(i,j & 7); + else + mem_setchr1(i,j & 0x3F); + } + mmc3_syncsram(); + mmc3_syncmirror(); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + mmc3_reset(C_MMC3A,sync,hard); +} + +MAPPER(B_NINTENDO_TQROM,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/txrom.c b/apps/nesemu2/src/mappers/boards/nintendo/txrom.c new file mode 100644 index 00000000..d73bfb60 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/txrom.c @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void reset(int hard) +{ + mmc3_reset(C_MMC3B,mmc3_sync,hard); +} + +MAPPER(B_NINTENDO_TxROM,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/un1rom.c b/apps/nesemu2/src/mappers/boards/nintendo/un1rom.c new file mode 100644 index 00000000..f7572106 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/un1rom.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,latch_data >> 2); + mem_setprg16(0xC,0xFF); + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_NINTENDO_UN1ROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/unrom_74hc08.c b/apps/nesemu2/src/mappers/boards/nintendo/unrom_74hc08.c new file mode 100644 index 00000000..7761e3e4 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/unrom_74hc08.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,0); + mem_setprg16(0xC,latch_data); + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_NINTENDO_UNROM_74HC08,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/nintendo/uxrom.c b/apps/nesemu2/src/mappers/boards/nintendo/uxrom.c new file mode 100644 index 00000000..d466f30b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/nintendo/uxrom.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,latch_data); + mem_setprg16(0xC,0xFF); + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_NINTENDO_UxROM,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/ntdec/asder.c b/apps/nesemu2/src/mappers/boards/ntdec/asder.c new file mode 100644 index 00000000..fb101e0d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/ntdec/asder.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 command,reg[10]; + +static void sync() +{ + int chrhi = 0; + + if(reg[9] & 2) + chrhi = reg[8]; + mem_setprg8(0x8,reg[0]); + mem_setprg8(0xA,reg[1]); + mem_setprg8(0xC,0xFE); + mem_setprg8(0xE,0xFF); + mem_setchr2(0,(reg[2] | ((chrhi << 6) & 0x100)) >> 1); + mem_setchr2(2,(reg[3] | ((chrhi << 5) & 0x100)) >> 1); + mem_setchr1(4,reg[4] | ((chrhi << 4) & 0x100)); + mem_setchr1(5,reg[5] | ((chrhi << 3) & 0x100)); + mem_setchr1(6,reg[6] | ((chrhi << 2) & 0x100)); + mem_setchr1(7,reg[7] | ((chrhi << 1) & 0x100)); + mem_setmirroring((reg[9] & 1) ^ 1); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xE000) { + case 0x8000: + command = data & 7; + break; + case 0xA000: + switch(command) { + case 0: + case 1: + reg[command] = data; + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + reg[command] = data; + break; + } + sync(); + break; + case 0xC000: + reg[8] = data; + sync(); + break; + case 0xE000: + reg[9] = data; + sync(); + break; + } +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + command = 0; + for(i=0;i<10;i++) + reg[i] = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,10); + sync(); +} + +MAPPER(B_NTDEC_ASDER,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/ntdec/caltron6in1.c b/apps/nesemu2/src/mappers/boards/ntdec/caltron6in1.c new file mode 100644 index 00000000..713a9c68 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/ntdec/caltron6in1.c @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[2]; + +static void sync() +{ + mem_setprg32(8,reg[0] & 7); + mem_setchr8(0,((reg[0] & 0x18) >> 1) | (reg[1] & 3)); + mem_setmirroring(((~reg[0]) >> 5) & 1); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x6800) + reg[0] = addr & 0x3F; + else if((addr >= 0x8000) && (reg[0] & 4)) + reg[1] = data & 3; + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=6;i<16;i++) + mem_setwritefunc(i,write); + reg[0] = reg[1] = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,2); + sync(); +} + +MAPPER(B_NTDEC_CALTRON6IN1,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/ntdec/tc-112.c b/apps/nesemu2/src/mappers/boards/ntdec/tc-112.c new file mode 100644 index 00000000..8557ec03 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/ntdec/tc-112.c @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr[4]; + +static void sync() +{ + mem_setprg8(0x8,prg); + mem_setprg8(0xA,0xFD); + mem_setprg8(0xC,0xFE); + mem_setprg8(0xE,0xFF); + mem_setchr4(0,chr[0] >> 2); + mem_setchr2(4,chr[1] >> 1); + mem_setchr2(6,chr[2] >> 1); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 3) { + case 0: + case 1: + case 2: + chr[addr & 3] = data; + break; + case 3: + prg = data; + break; + } + sync(); +} + +static void reset(int hard) +{ + mem_setwritefunc(6,write); + mem_setwritefunc(7,write); + if(hard) { + prg = chr[0] = chr[1] = chr[2] = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_ARRAY_U8(chr,3); + sync(); +} + +MAPPER(B_NTDEC_TC112,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/active.c b/apps/nesemu2/src/mappers/boards/other/active.c new file mode 100644 index 00000000..29f01421 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/active.c @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static u8 ram[4]; +static readfunc_t read4; +static writefunc_t write4; + +static void sync() +{ + u8 chip,prg,chr; + + chip = (latch_addr >> 11) & 3; + + //kludge for ines format + chip = (chip == 3) ? 2 : chip; + + prg = (latch_addr >> 6) & 0x1F; + prg |= chip << 5; + chr = (latch_data & 3) | ((latch_addr & 0xF) << 2); + if(latch_addr & 0x20) { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + else + mem_setprg32(8,prg >> 1); + mem_setchr8(0,chr); + mem_setmirroring(((latch_addr >> 13) & 1) ^ 1); +} + +static u8 readram(u32 addr) +{ + if(addr < 0x4020) + return(read4(addr)); + return(ram[addr & 3]); +} + +static void writeram(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + ram[addr & 3] = data & 0xF; +} + +static void reset(int hard) +{ + latch_reset(sync,hard); + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4,readram); + mem_setreadfunc(5,readram); + mem_setwritefunc(4,writeram); + mem_setwritefunc(5,writeram); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(ram,4); + latch_state(mode,data); +} + +MAPPER(B_ACTIVE,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/1200in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/1200in1.c new file mode 100644 index 00000000..1b27f0e1 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/1200in1.c @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + int i; + u8 prg; + + prg = ((latch_addr >> 2) & 0x1F) | ((latch_addr & 0x100) >> 3); + if(latch_addr & 1) { + mem_setprg32(8,prg >> 1); + } + else { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + mem_setvram8(0,0); + if(latch_addr & 0x80) { + for(i=0;i<8;i++) + nes->ppu.writepages[i] = 0; + } + else { + if(latch_addr & 0x200) + mem_setprg16(0xC,prg | 7); + else + mem_setprg16(0xC,prg & ~7); + } + mem_setmirroring(((latch_addr >> 1) & 1) ^ 1); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_BMC_1200IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/150in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/150in1.c new file mode 100644 index 00000000..fbb476cc --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/150in1.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + u8 bank = (latch_addr >> 1) & 7; + + log_printf("bmc150in1.c: $%04X = $%02X\n",latch_addr,latch_data); + if((latch_addr & 0xC) == 0xC) { + mem_setprg32(0x8,bank); + } + else { + mem_setprg16(0x8,bank); + mem_setprg16(0xC,bank); + } + mem_setchr8(0,bank); + mem_setmirroring((latch_addr & 1) ^ 1); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_BMC_150IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c new file mode 100644 index 00000000..c378468d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg; + +static void sync() +{ + mmc3_syncprg(0x1F >> (reg >> 1),reg << 4); + mmc3_syncchr(0xFF >> (reg >> 1),reg << 7); + mmc3_syncmirror(); +} + +static void write(u32 addr,u8 data) +{ + reg = data & 3; + printf("205 write $%04X = $%02X\n",addr,data); + sync(); +} + +static void reset(int hard) +{ + mem_setwritefunc(6,write); + mem_setwritefunc(7,write); + reg = 0; + mmc3_reset(C_MMC3,sync,hard); + mem_unsetcpu8(6); //mmc3 doesnt need to set this +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_BMC_15IN1,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/20in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/20in1.c new file mode 100644 index 00000000..194f5959 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/20in1.c @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + u8 prg; + + prg = ((latch_addr >> 8) & 0x3F) | (latch_addr & 0x40); + if(latch_addr & 0x20) { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + else { + mem_setprg32(8,prg >> 1); + } + + prg = latch_addr & 0x1E; + if(latch_addr & 0x20) { + mem_setprg32(8,prg >> 1); + } + else { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + mem_setvram8(0,0); + switch((latch_addr >> 6) & 3) { + case 0: mem_setmirroring(MIRROR_1L); break; + case 1: mem_setmirroring(MIRROR_V); break; + case 2: mem_setmirroring(MIRROR_H); break; + case 3: mem_setmirroring2(0,1,1,1); break; + } +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_BMC_20IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/21in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/21in1.c new file mode 100644 index 00000000..028efecc --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/21in1.c @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_addr & 0xFF); + mem_setchr8(0,latch_addr & 0xFF); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_BMC_21IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/22in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/22in1.c new file mode 100644 index 00000000..e703ba45 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/22in1.c @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static u8 mode = 0; + +static void sync() +{ + //multicart mode + if(mode) { + if(latch_data & 0x20) { + mem_setprg16(0x8,(latch_data & 0x1F) + 8); + mem_setprg16(0x8,(latch_data & 0x1F) + 8); + } + else { + mem_setprg32(8,((latch_data >> 1) & 0xF) + 4); + } + mem_setmirroring((latch_data >> 6) & 1); + } + + //contra mode + else { + mem_setprg16(0x8,latch_data); + mem_setprg16(0xC,7); + mem_setmirroring(MIRROR_V); + } + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + if(hard) + mode = 1; + mode ^= 1; + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_BMC_22IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/31in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/31in1.c new file mode 100644 index 00000000..d1161936 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/31in1.c @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + if(latch_addr & 0x1E) { + mem_setprg16(0x8,latch_addr & 0x1F); + mem_setprg16(0xC,latch_addr & 0x1F); + } + else { + mem_setprg32(8,0); + } + mem_setchr8(0,latch_addr & 0x1F); + mem_setmirroring(((latch_addr >> 5) & 1) ^ 1); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_BMC_31IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/35in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/35in1.c new file mode 100644 index 00000000..fd326d9e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/35in1.c @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,latch_data >> 2); + mem_setprg16(0xC,latch_data >> 2); + mem_setchr8(0,latch_data & 3); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_BMC_35IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/36in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/36in1.c new file mode 100644 index 00000000..5249d424 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/36in1.c @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,latch_addr & 7); + mem_setprg16(0xC,latch_addr & 7); + mem_setchr8(0,latch_addr & 7); + mem_setmirroring(((latch_addr >> 3) & 1) ^ 1); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_BMC_36IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/65in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/65in1.c new file mode 100644 index 00000000..6c8b2bdb --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/65in1.c @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static readfunc_t oldread; +static u8 bankmode,dip; + +static void sync() +{ + u8 prglo,prghi,chr; + + bankmode = (latch_addr >> 8) & 1; + prglo = ((latch_addr >> 4) & 0xE) & ~(~latch_addr >> 7 & 1); + prghi = ((latch_addr >> 4) & 0xE) | (~latch_addr >> 7 & 1); + chr = latch_addr & 0xF; + mem_setprg16(0x8,prglo); + mem_setprg16(0xC,prghi); + mem_setchr8(0,chr); + mem_setmirroring(((latch_addr >> 3) & 1) ^ 1); +} + +static u8 read(u32 addr) +{ + if(bankmode && addr >= 0x8000) { + return(dip); + } + return(oldread(addr)); +} + +static void write(u32 addr,u8 data) +{ + log_printf("bmc/65in1.c: write $%04X = $%02X\n",addr,data); + latch_write(addr,data); +} + +static void reset(int hard) +{ + int i; + + dip = 0; + latch_reset(sync,hard); + oldread = cpu_getreadfunc(); + cpu_setreadfunc(read); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(bankmode); + latch_state(mode,data); +} + +MAPPER(B_BMC_65IN1,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/70in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/70in1.c new file mode 100644 index 00000000..5815b1a0 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/70in1.c @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +#define BMC_70IN1 0 +#define BMC_70IN1B 1 + +static int revision; +static u8 bankmode,mirror; +static u8 bankhi,banklo,chrbank; +static u8 hwswitch; +static readfunc_t cpuread; + +static void sync() +{ + switch(bankmode) { + case 0x00: + case 0x10: + mem_setprg16(0x8,bankhi | banklo); + mem_setprg16(0xC,bankhi | 7); + break; + case 0x20: + mem_setprg32(0x8,(bankhi | banklo) >> 1); + break; + case 0x30: + mem_setprg16(0x8,bankhi | banklo); + mem_setprg16(0xC,bankhi | banklo); + break; + } + if(revision == BMC_70IN1) + mem_setchr8(0,chrbank); + else + mem_setvram8(0,0); + mem_setmirroring(mirror); +} + +static u8 read(u32 addr) +{ + if(addr >= 0x8000 && bankmode == 0x10) { + return(cpuread((addr & 0xFFF0) | hwswitch)); + } + return(cpuread(addr)); +} + +static void write_8000(u32 addr,u8 data) +{ + mirror = ((addr & 0x20) >> 5) ^ 1; + if(revision == BMC_70IN1B) + bankhi = (addr & 3) << 3; + else + chrbank = addr & 7; + sync(); +} + +static void write_C000(u32 addr,u8 data) +{ + bankmode = addr & 0x30; + banklo = addr & 7; + sync(); +} + +static void reset(int r,int hard) +{ + int i; + + revision = r; + cpuread = cpu_getreadfunc(); + cpu_setreadfunc(read); + for(i=0;i<4;i++) { + mem_setwritefunc(0x8 + i,write_8000); + mem_setwritefunc(0xC + i,write_C000); + } + mem_setvramsize(8); + bankmode = 0; + bankhi = banklo = 0; + if(hard) { + if(r == BMC_70IN1) + hwswitch = 0xD; + else + hwswitch = 0xF; + } + else { + hwswitch = (hwswitch + 1) & 0xF; + } + sync(); +} + +static void reset_70in1(int hard) +{ + reset(BMC_70IN1,hard); +} + +static void reset_70in1b(int hard) +{ + reset(BMC_70IN1B,hard); +} + +static void state(int mode,u8 *data) +{ + CFG_U8(hwswitch); + STATE_U8(bankmode); + STATE_U8(mirror); + STATE_U8(bankhi); + STATE_U8(banklo); + STATE_U8(chrbank); + sync(); +} + +MAPPER(B_BMC_70IN1,reset_70in1,0,0,state); +MAPPER(B_BMC_70IN1B,reset_70in1b,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/72in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/72in1.c new file mode 100644 index 00000000..d42fdd62 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/72in1.c @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static u8 ram[4]; + +static void sync() +{ + u8 prg,chr,hi; + + hi = (latch_addr >> 8) & 0x40; + prg = (latch_addr >> 6) & 0x3F; + prg |= hi; + chr = latch_addr & 0x3F; + chr |= hi; + if(((latch_addr >> 12) & 1) == 0) { + mem_setprg32(8,prg >> 1); + } + else { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + mem_setchr8(0,chr); + mem_setmirroring(((latch_addr >> 13) & 1) ^ 1); +} + +static u8 read(u32 addr) +{ + if(addr >= 0x5800) + return(ram[addr & 3]); + return((u8)(addr >> 8)); +} + +static void write(u32 addr,u8 data) +{ + if(addr >= 0x5800) + ram[addr & 3] = data; +} + +static void reset(int hard) +{ + latch_reset(sync,hard); + mem_setreadfunc(5,read); + mem_setwritefunc(5,write); + if(hard) { + ram[0] = ram[1] = ram[2] = ram[3] = 0; + } +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(ram,4); + latch_state(mode,data); +} + +MAPPER(B_BMC_72IN1,reset,0,0,state); + diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/76in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/76in1.c new file mode 100644 index 00000000..e2c8df59 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/76in1.c @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg, bankmode,mirror; + +static void sync() +{ + if(bankmode == 0) { + mem_setprg32(8,prg >> 1); + } + else { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + mem_setvram8(0,0); + mem_setmirroring(mirror); +} + +static void write(u32 addr,u8 data) +{ + if(addr & 1) { + prg &= 0x3F; + prg |= (data & 1) << 6; + } + else { + prg &= 0x40; + prg |= ((data >> 2) & 0x20) | (data & 0x1F); + bankmode = (data >> 5) & 1; + mirror = (data >> 6) & 1; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + mem_setvramsize(8); + for(i=8;i<16;i++) { + mem_setwritefunc(i,write); + } + if(hard) { + prg = bankmode = mirror = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(bankmode); + STATE_U8(mirror); + sync(); +} + +MAPPER(B_BMC_76IN1,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/ballgames11in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/ballgames11in1.c new file mode 100644 index 00000000..34574f71 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/ballgames11in1.c @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 bankmode,bank; + +static void sync() +{ + if(bankmode & 1) + mem_setprg8(0x6,bank | 0x23); + else + mem_setprg8(0x6,bank | 0x2F); + if(bankmode == 2) + mem_setprg16(0x8,(bank >> 1) | 1); + else + mem_setprg16(0x8,bank >> 1); + if(bankmode & 1) + mem_setprg16(0xC,(bank >> 1) | 1); + else + mem_setprg16(0xC,(bank >> 1) | 7); + mem_setmirroring((bankmode == 3) ? MIRROR_H : MIRROR_V); +} + +static void write67(u32 addr,u8 data) +{ + bankmode = ((data >> 3) & 2) | ((data >> 1) & 1); + sync(); +} + +static void write_upper(u32 addr,u8 data) +{ + bank = (data & 0xF) << 2; + if(bank & 4) + bankmode = ((data >> 3) & 2) | (bankmode & 1); + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=6;i<8;i++) + mem_setwritefunc(i,write67); + for(i=8;i<16;i++) + mem_setwritefunc(i,write_upper); + mem_setvramsize(8); + mem_setvram8(0,0); + bankmode = 1; + bank = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(bankmode); + STATE_U8(bank); + sync(); +} + +MAPPER(B_BMC_BALLGAMES11IN1,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/big7in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/big7in1.c new file mode 100644 index 00000000..7f31d5c1 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/big7in1.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg; +static writefunc_t writeA; + +static void sync() +{ + if(reg >= 6) { + mmc3_syncprg(0x1F,reg << 4); + mmc3_syncchr(0xFF,reg << 7); + } + else { + mmc3_syncprg(0x0F,reg << 4); + mmc3_syncchr(0x7F,reg << 7); + } + mmc3_syncsram(); + mmc3_syncmirror(); +} + +static void write(u32 addr,u8 data) +{ + if(addr & 1) { + reg = data & 7; + sync(); + } + writeA(addr,data); +} + +static void reset(int hard) +{ + mmc3_reset(C_MMC3,sync,hard); + reg = 0; + writeA = mem_getwritefunc(0xA); + mem_setwritefunc(0xA,write); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_BMC_BIG7IN1,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/contra-function-16.c b/apps/nesemu2/src/mappers/boards/other/bmc/contra-function-16.c new file mode 100644 index 00000000..0dfa50c5 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/contra-function-16.c @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + int bank,banks[4],x; + + bank = (latch_data << 1) & 0x7E; + x = (latch_data >> 7) & 1; + switch(latch_addr & 3) { + default: + case 0: + banks[0] = bank | 0; + banks[1] = bank | 1; + banks[2] = bank | 2; + banks[3] = bank | 3; + break; + case 1: + banks[0] = bank | 0; + banks[1] = bank | 1; + banks[2] = 0x7E; + banks[3] = 0x7F; + break; + case 2: + banks[0] = bank; + banks[1] = bank; + banks[2] = bank; + banks[3] = bank; + break; + case 3: + banks[0] = bank | 0; + banks[1] = bank | 1; + banks[2] = bank | 0; + banks[3] = bank | 1; + break; + } + mem_setprg8(0x8,banks[0] ^ x); + mem_setprg8(0xA,banks[1] ^ x); + mem_setprg8(0xC,banks[2] ^ x); + mem_setprg8(0xE,banks[3] ^ x); + mem_setmirroring((latch_data & 0x40) ? MIRROR_H : MIRROR_V); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + mem_setvram8(0,0); + latch_reset(sync,hard); +} + +MAPPER(B_BMC_CONTRA_FUNCTION_16,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/fk23c.c b/apps/nesemu2/src/mappers/boards/other/bmc/fk23c.c new file mode 100644 index 00000000..0caf0e6b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/fk23c.c @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +/* + mmc3 extra commands: + 8 - prg 8kb bank at $C + 9 - prg 8kb bank at $E + 10 - chr 1kb bank at $400 + 11 - chr 1kb bank at $C00 + + ** these are enabled with reg3 bit1 ** + + reg0: xxxx-xMMM + M - prg mode. + 4 = 32kb mode + 3 = 16kb mode + All others use mmc3 standard + + reg3: xxxx-xxCx + C - enable $400/$C00 kbyte banks and swapping prg of $C/$E +*/ + +static u8 reg[8],chr; + +static void syncprg(int a,int o) +{ + int i,n; + + n = reg[0] & 7; + if(n == 4 || n == 3) + return; + for(i=0;i<4;i++) { + if((reg[3] & 2) == 0 || i < 2) { + n = mmc3_getprgbank(i); + if (reg[0] & 3) + n = (n & (0x3F >> (reg[0] & 3))) | (reg[1] << 1); + mem_setprg8(8 + (i * 2),n); + } + else { + if(i == 2) + mem_setprg8(0xC,reg[4]); + else if(i == 3) + mem_setprg8(0xE,reg[5]); + } + } +} + +static void syncchr(int a,int o) +{ + int i; + + for(i=0;i<8;i++) { + if((reg[0] & 0x40)) { + if((reg[3] & 2) && (i == 1 || i == 3)) { + mem_setchr1(1,((reg[2] & 0x7F) << 3) | (reg[6 + ((i - 1) / 2)] & 0xFF)); + continue; + } + } + mem_setchr1(i,(reg[2] & 0x7FU) << 3 | mmc3_getchrbank(i)); + } +} + +static void sync() +{ + if((reg[0] & 7) == 4) { + mem_setprg32(8,reg[1] >> 1); + } + else if((reg[0] & 7) == 3) { + mem_setprg16(0x8,reg[1]); + mem_setprg16(0xC,reg[1]); + } + else { + syncprg(0xFF,0x00); + } + if(reg[0] & 0x40) { + mem_setchr8(0,reg[2] | chr); + } + else { + syncchr(0x3FF,0x00); + } + mmc3_syncsram(); + mmc3_syncmirror(); +} + +static void write_5000(u32 addr,u8 data) +{ + switch(addr) { + case 0x5FF0: + case 0x5FF1: + case 0x5FF2: + case 0x5FF3: + reg[addr & 3] = data; + sync(); + break; + } +} + +static void write(u32 addr,u8 data) +{ + u8 command; + + if(reg[0] & 0x40) { + chr = (reg[0] & 0x30) ? 0 : data & 3; + sync(); + return; + } + switch(addr & 0xE001) { + case 0x8000: mmc3_write(addr,data); break; + case 0x8001: + command = mmc3_getcommand() & 0xF; + if(command < 8) { + mmc3_write(addr,data); + } + else if((reg[3] << 2) != 0) { + switch(command) { + case 8: + case 9: + case 10: + case 11: + reg[4 + (command & 3)] = data; + sync(); + break; + } + } + break; + case 0xA000: mmc3_write(addr,data); break; + case 0xA001: mmc3_write(addr,data); break; + case 0xC000: mmc3_write(addr,data); break; + case 0xC001: mmc3_write(addr,data); break; + case 0xE000: mmc3_write(addr,data); break; + case 0xE001: mmc3_write(addr,data); break; + } +} + +static void reset(int hard) +{ + int i; + + for(i=0;i<8;i++) { + reg[i] = 0xFF; + } + chr = 0; + mem_setvramsize(8); + mmc3_reset(C_MMC3B,sync,hard); + mem_setwritefunc(5,write_5000); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,8); + STATE_U8(chr); + mmc3_state(mode,data); +} + +MAPPER(B_BMC_FK23C,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/goldengame260in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/goldengame260in1.c new file mode 100644 index 00000000..e95d090a --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/goldengame260in1.c @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + u8 prg; + + prg = (latch_addr & 0x1F) << 1; + prg |= (latch_addr & 0x300) >> 2; + prg |= (latch_addr & 0x1000) >> 12; + if(latch_addr & 0x800) { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + else { + mem_setprg32(0x8,prg >> 1); + } + mem_setvram8(0,0); + if(latch_addr & 0x400) + mem_setmirroring(MIRROR_1L); + else + mem_setmirroring(((latch_addr >> 13) & 1) ^ 1); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_BMC_GOLDENGAME260IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/mario7in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/mario7in1.c new file mode 100644 index 00000000..4192f4f5 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/mario7in1.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg,written; + +static void sync() +{ + if(reg & 8) { + mmc3_syncprg(0xF,(reg & 7) << 4); + } + else { + mmc3_syncprg(0x1F,(reg & 6) << 4); + } + if(reg & 0x40) { + mmc3_syncchr(0x7F,((reg & 0x20) << 4) | ((reg & 4) << 6) | ((reg & 0x10) << 3)); + } + else { + mmc3_syncchr(0xFF,((reg & 0x20) << 4) | ((reg & 4) << 6)); + } + //kludge + if(written) { + mem_setwritefunc(6,0); + mem_setwritefunc(7,0); + mmc3_syncsram(); + } + mmc3_syncmirror(); +} + +static void write(u32 addr,u8 data) +{ + if(written) + return; + reg = data; + written = 1; + sync(); +} + +static void reset(int hard) +{ + if(hard) { + mem_unsetcpu8(6); + mem_setwritefunc(6,write); + mem_setwritefunc(7,write); + written = 0; + reg = 0; + } + mmc3_reset(C_MMC3,sync,hard); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + STATE_U8(written); + mmc3_state(mode,data); +} + +MAPPER(B_BMC_MARIO7IN1,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/reset4in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/reset4in1.c new file mode 100644 index 00000000..2664343e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/reset4in1.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg = 0; + +static void sync() +{ + mem_setprg16(0x8,reg); + mem_setprg16(0xC,reg); + mem_setchr8(0,reg); +} + +static void reset(int hard) +{ + if(hard) + reg = 0; + else + reg++; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); +} + +MAPPER(B_BMC_RESET4IN1,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/super42in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/super42in1.c new file mode 100644 index 00000000..54a56631 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/super42in1.c @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + u8 prg; + + switch(latch_data >> 6) { + case 0: mem_setmirroring2(0,0,0,1); break; + case 1: mem_setmirroring(MIRROR_V); break; + case 2: mem_setmirroring(MIRROR_H); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } + prg = latch_data & 0x1F; + if(latch_data & 0x20) { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + else + mem_setprg32(8,prg >> 1); + mem_setvram8(0,0); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_BMC_SUPER42IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/super700in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/super700in1.c new file mode 100644 index 00000000..4de9f9cc --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/super700in1.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + u8 prg; + + prg = ((latch_addr >> 8) & 0x3F) | (latch_addr & 0x40); + if(latch_addr & 0x20) { + mem_setprg16(0x8,prg); + mem_setprg16(0xC,prg); + } + else { + mem_setprg32(8,prg >> 1); + } + mem_setchr8(0,(latch_data & 3) | ((latch_addr & 0x3F) << 2)); + mem_setmirroring(((latch_addr >> 7) & 1) ^ 1); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_BMC_SUPER700IN1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/supergk.c b/apps/nesemu2/src/mappers/boards/other/bmc/supergk.c new file mode 100644 index 00000000..c02bbc09 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/supergk.c @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[2]; + +static void sync() +{ + u8 prglo,prghi,chr; + + prglo = (reg[1] >> 5) & ~(reg[1] >> 7); + prghi = (reg[1] >> 5) | (reg[1] >> 7); + chr = ((reg[0] >> 3) & 8) | (reg[0] & 7) | (reg[1] & 7); + mem_setprg16(0x8,prglo); + mem_setprg16(0xC,prghi); + mem_setchr8(0,chr); + mem_setmirroring((reg[1] & 8) ? MIRROR_H : MIRROR_V); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0x8800) { + case 0x8000: + reg[0] = data; + break; + case 0x8800: + reg[1] = data; + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + reg[0] = reg[1] = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,2); + sync(); +} + +MAPPER(B_BMC_SUPERGK,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/superhik4in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/superhik4in1.c new file mode 100644 index 00000000..54cdee41 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/superhik4in1.c @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg; + +static void sync() +{ + if(reg & 1) + mmc3_syncprg(0xF,(reg & 0xC0) >> 2); + else + mem_setprg32(0x8,(reg & 0x30) >> 4); + mmc3_syncchr(0x7F,(reg & 0xC0) << 1); + mmc3_syncmirror(); +} + +static void write(u32 addr,u8 data) +{ + reg = data; + sync(); +} + +static void reset(int hard) +{ + mmc3_reset(C_MMC3B,sync,hard); + reg = 0; + mem_setwritefunc(6,write); + mem_setwritefunc(7,write); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_BMC_SUPERHIK4IN1,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/superhikXin1.c b/apps/nesemu2/src/mappers/boards/other/bmc/superhikXin1.c new file mode 100644 index 00000000..0479e8fe --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/bmc/superhikXin1.c @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg[4]; +static u8 regindex; + +static void sync() +{ + mmc3_syncprg(~reg[3] & 0x3F,reg[1]); + if(nes->cart->chr.size) + mmc3_syncchr(0xFF >> ((~reg[2]) & 0xF),reg[0] | ((reg[2] & 0xF0) << 4)); + else + mmc3_syncvram(7,0); + mmc3_syncmirror(); + mmc3_syncsram(); + if((reg[3] & 0x40) == 0) + mem_unsetcpu8(6); +} + +static void write(u32 addr,u8 data) +{ + if((reg[3] & 0x40) == 0) { + reg[regindex++] = data; + regindex &= 3; + sync(); + } +} + +static void reset(int hard) +{ + mmc3_reset(C_MMC3B,sync,hard); + mem_unsetcpu8(6); + mem_setwritefunc(6,write); + mem_setwritefunc(7,write); + reg[0] = reg[1] = reg[2] = reg[3] = 0; + regindex = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,4); + STATE_U8(regindex); + mmc3_state(mode,data); +} + +MAPPER(B_BMC_SUPERHIKXIN1,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/btl/biomiraclea.c b/apps/nesemu2/src/mappers/boards/other/btl/biomiraclea.c new file mode 100644 index 00000000..f4f907c7 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/btl/biomiraclea.c @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr,mirror,irqenabled; +static int irqcounter; + +static void sync() +{ + mem_setprg8(6,prg); + mem_setprg32(8,3); + if(nes->cart->chr.size) + mem_setchr8(0,chr); + else + mem_setvram8(0,0); + mem_setmirroring(mirror); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xE003) { + case 0x8000: + case 0x8001: + chr = data; + break; + case 0xE000: + prg = data; + break; + case 0xE001: + mirror = ((data & 8) >> 3) ^ 1; + break; + case 0xE002: + if(data & 2) + irqenabled = 1; + else { + cpu_clear_irq(IRQ_MAPPER); + irqenabled = 0; + irqcounter = 0; + } + break; + } + sync(); +} + +static void cpucycle() +{ + int prev = irqcounter; + + if(irqenabled == 0) + return; + irqcounter++; + if((irqcounter & 0x6000) != (prev & 0x6000)) { + if((irqcounter & 0x6000) == 0x6000) + cpu_set_irq(IRQ_MAPPER); + else + cpu_clear_irq(IRQ_MAPPER); + } +} + +static void reset(int hard) +{ + int i; + + for(i=0x8;i<0x10;i++) + mem_setwritefunc(i,write); + if(nes->cart->chr.size == 0) + mem_setvramsize(8); + prg = 0; + chr = 0; + mirror = 1; + irqenabled = 0; + irqcounter = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(chr); + STATE_U8(mirror); + STATE_U8(irqenabled); + STATE_INT(irqcounter); + sync(); +} + +MAPPER(B_BTL_BIOMIRACLEA,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/other/btl/mario1-malee2.c b/apps/nesemu2/src/mappers/boards/other/btl/mario1-malee2.c new file mode 100644 index 00000000..a7b45243 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/btl/mario1-malee2.c @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 *prg6; +static u8 *sram7; + +//reading +static u8 read6(u32 addr) { return(prg6[addr & 0x7FF]); } +static u8 read7(u32 addr) { return(sram7[addr & 0x7FF]); } + +//writing +static void write7(u32 addr,u8 data) { sram7[addr & 0x7FF] = data; } + +static void reset(int hard) +{ + mem_setwramsize(8); + + //initialize all bank pointers + mem_setprg4(6,8); + mem_setwram4(7,0); + mem_setprg32(8,0); + mem_setchr8(0,0); + + //get pointers to the data + prg6 = mem_getreadptr(6); + sram7 = mem_getreadptr(7); + + //remove pointers to memory + mem_setreadptr(6,0); + mem_setreadptr(7,0); + mem_setwriteptr(7,0); + + //insert the function pointers from here + mem_setreadfunc(6,read6); + mem_setreadfunc(7,read7); + mem_setwritefunc(7,write7); +} + +MAPPER(B_BTL_MARIO1_MALEE2,reset,0,0,0); diff --git a/apps/nesemu2/src/mappers/boards/other/btl/pikachuy2k.c b/apps/nesemu2/src/mappers/boards/other/btl/pikachuy2k.c new file mode 100644 index 00000000..20959667 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/btl/pikachuy2k.c @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg,xor; +//static writefunc_t writeA; +static u8 *wram; + +static void sync() +{ + mmc3_syncprg(0xFF,0); + mmc3_syncchr(0xFF,0); + mmc3_syncmirror(); +} + +static u8 read_wram(u32 addr) +{ + return(wram[addr & 0x1FFF] ^ (xor & reg)); +} + +static void write_wram(u32 addr,u8 data) +{ + wram[addr & 0x1FFF] = data; +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0x8000: + case 0x9000: + if((addr & 1) == 0) + reg = 0; + break; + case 0xA000: + if(addr & 1) + xor = data; + break; + } + mmc3_write(addr,data); +} + +static void reset(int hard) +{ + mmc3_reset(C_MMC3,sync,hard); + reg = 0xFF; + mem_setwramsize(8); + wram = nes->cart->wram.data; + mem_unsetcpu8(6); + mem_setreadfunc(6,read_wram); + mem_setreadfunc(7,read_wram); + mem_setwritefunc(6,write_wram); + mem_setwritefunc(7,write_wram); + mem_setwritefunc(0x8,write); + mem_setwritefunc(0x9,write); + mem_setwritefunc(0xA,write); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + STATE_U8(xor); + mmc3_state(mode,data); +} + +MAPPER(B_BTL_PIKACHUY2K,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/btl/smb2b.c b/apps/nesemu2/src/mappers/boards/other/btl/smb2b.c new file mode 100644 index 00000000..ef671576 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/btl/smb2b.c @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,irqenable; +static u16 irqcounter; +static writefunc_t write4; + +static void sync() +{ + mem_setprg8(0x6,0xF); + mem_setprg8(0x8,0x8); + mem_setprg8(0xA,0x9); + mem_setprg8(0xC,prg); + mem_setprg8(0xE,0xB); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + switch(addr & 0x4120) { + case 0x4020: + prg = (data >> 1) & 3; //low + prg |= (data & 1) << 2; //middle + prg |= data & 8; //high + sync(); + break; + case 0x4120: + irqenable = data; + if((data & 1) == 0) { + irqcounter = 0; + cpu_clear_irq(IRQ_MAPPER); + } + break; + } +} + +static void reset(int hard) +{ + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write); + mem_setwritefunc(5,write); + mem_setvramsize(8); + mem_setvram8(0,0); + if(hard) { + prg = 0; + irqenable = 0; + irqcounter = 0; + } + sync(); +} + +static void cpucycle() +{ + if(irqenable == 0) + return; + irqcounter++; + if(irqcounter == 0x1000) + cpu_set_irq(IRQ_MAPPER); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(irqenable); + STATE_U16(irqcounter); + sync(); +} + +MAPPER(B_BTL_SMB2B,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/other/colordreams.c b/apps/nesemu2/src/mappers/boards/other/colordreams.c new file mode 100644 index 00000000..0ff89525 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/colordreams.c @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_data & 0xF); + mem_setchr8(0,(latch_data >> 4) & 0xF); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_COLORDREAMS,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/coolboy.c b/apps/nesemu2/src/mappers/boards/other/coolboy.c new file mode 100644 index 00000000..c7be53aa --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/coolboy.c @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2013-2016 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 regs[4]; + +static void write(u32 addr, u8 data) +{ + u8 scramble[8] = { 0,3,1,5,6,7,2,4 }; + + switch (addr & 0xE001) { + case 0x8000: + break; + case 0x8001: + mmc3_write(0xA000, data); + break; + case 0xA000: + mmc3_write(0x8000, scramble[data & 7] | (data & 0xC0)); + break; + case 0xA001: + break; + case 0xC000: + mmc3_write(0x8001, data); + break; + case 0xC001: + mmc3_write(0xC000, data); + mmc3_write(0xC001, data); + break; + case 0xE000: + case 0xE001: + mmc3_write(addr, data); + break; + } +} + +static void write_coolboy(u32 addr, u8 data) +{ + +} + +static void reset(int hard) +{ + int i; + + for (i = 0; i < 4; i++) + regs[i] = 0; + mmc3_reset(C_MMC3B, mmc3_sync, hard); + for (i = 8; i<16; i++) + mem_setwritefunc(i, write); + mem_setwritefunc(6, write_coolboy); + mem_setwritefunc(7, write_coolboy); +} + +MAPPER(B_COOLBOY, reset, mmc3_ppucycle, 0, mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/other/deathrace.c b/apps/nesemu2/src/mappers/boards/other/deathrace.c new file mode 100644 index 00000000..104d5371 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/deathrace.c @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" +#include "nes/io.h" + +static void sync() +{ + u8 data = latch_data; + + if(latch_addr >= 0x8000) + data = nes_read_rom(latch_addr) & (data | 1); + mem_setprg32(8,data & 0xF); + mem_setchr8(0,(data >> 4) & 0xF); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_DEATHRACE,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/hes.c b/apps/nesemu2/src/mappers/boards/other/hes.c new file mode 100644 index 00000000..fa10b3af --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/hes.c @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg; +static writefunc_t write4; + +static void sync() +{ + mem_setprg32(8,(reg >> 3) & 7); + mem_setchr8(0,(reg & 7) | ((reg >> 3) & 8)); + mem_setmirroring(reg >> 7); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + if(addr & 0x4100) { + reg = data; + sync(); + } +} + +static void reset(int hard) +{ + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write); + mem_setwritefunc(5,write); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +MAPPER(B_HES,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/hosenkan.c b/apps/nesemu2/src/mappers/boards/other/hosenkan.c new file mode 100644 index 00000000..6147dd41 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/hosenkan.c @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void write(u32 addr,u8 data) +{ + u8 scramble[8] = {0,3,1,5,6,7,2,4}; + + switch(addr & 0xE001) { + case 0x8000: + break; + case 0x8001: + mmc3_write(0xA000,data); + break; + case 0xA000: + mmc3_write(0x8000,scramble[data & 7] | (data & 0xC0)); + break; + case 0xA001: + break; + case 0xC000: + mmc3_write(0x8001,data); + break; + case 0xC001: + mmc3_write(0xC000,data); + mmc3_write(0xC001,data); + break; + case 0xE000: + case 0xE001: + mmc3_write(addr,data); + break; + } +} + +static void reset(int hard) +{ + int i; + + mmc3_reset(C_MMC3B,mmc3_sync,hard); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); +} + +MAPPER(B_HOSENKAN,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/other/jycompany.c b/apps/nesemu2/src/mappers/boards/other/jycompany.c new file mode 100644 index 00000000..4cf96e61 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/jycompany.c @@ -0,0 +1,429 @@ +/*************************************************************************** + * Copyright (C) 2013-2016 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 irqmode, irqenabled, irqxor, irqwait; +static u8 mode, mirror, ntram, chrblock; +static u8 prgbank[4]; +static u8 chrbanklo[8]; +static u8 chrbankhi[8]; +static u8 ntbank[8]; +static u8 ram[8]; +static u8 mul[2]; +static u8 dip; +static u8 ntctl = 0; + +static u8 reverse(u8 byte) +{ + u8 ret = 0; + + ret |= (byte & 0x01) << 6; + ret |= (byte & 0x02) << 4; + ret |= (byte & 0x04) << 2; + ret |= (byte & 0x08) << 0; + ret |= (byte & 0x10) >> 2; + ret |= (byte & 0x20) >> 4; + ret |= (byte & 0x40) >> 6; + return(ret); +} + +static void sync_prg() +{ + u8 lastbank; + + //unmap prg at $6000-7FFF + if ((mode & 0x80) == 0) { + mem_setprg8(8,0); + } + + lastbank = (mode & 4) ? prgbank[3] : 0x7F; + switch (mode & 3) { + case 0: + if (mode & 0x80) + mem_setprg8(0x6, (prgbank[3] * 4) + 3); + mem_setprg32(0x8, lastbank); + break; + case 1: + if (mode & 0x80) + mem_setprg8(0x6, (prgbank[3] * 2) + 1); + mem_setprg16(0x8, prgbank[1]); + mem_setprg16(0xC, lastbank); + break; + case 2: + if (mode & 0x80) + mem_setprg8(0x6, prgbank[3]); + mem_setprg8(0x8, prgbank[0]); + mem_setprg8(0xA, prgbank[1]); + mem_setprg8(0xC, prgbank[2]); + mem_setprg8(0xE, lastbank); + break; + case 3: + if (mode & 0x80) + mem_setprg8(0x6, reverse(prgbank[3])); + mem_setprg8(0x8, reverse(prgbank[0])); + mem_setprg8(0xA, reverse(prgbank[1])); + mem_setprg8(0xC, reverse(prgbank[2])); + mem_setprg8(0xE, reverse(lastbank)); + break; + } + +} + +#define chrbank(nn) (chrbanklo[nn] | (chrbankhi[nn] << 8)) +#define chrblockbank(nn) (chrbanklo[nn] | ((chrblock & 0x1F) << 8)) + +void sync_chr() +{ + switch (((mode & 0x18) | (chrblock & 0x20)) >> 3) { + + //block mode enabled + case 0: + mem_setchr8(0, chrblockbank(0)); + break; + + case 1: + mem_setchr4(0, chrblockbank(0)); + mem_setchr4(4, chrblockbank(4)); + break; + + case 2: + mem_setchr2(0, chrblockbank(0)); + mem_setchr2(2, chrblockbank(2)); + mem_setchr2(4, chrblockbank(4)); + mem_setchr2(6, chrblockbank(6)); + break; + + case 3: + mem_setchr1(0, chrblockbank(0)); + mem_setchr1(1, chrblockbank(1)); + mem_setchr1(2, chrblockbank(2)); + mem_setchr1(3, chrblockbank(3)); + mem_setchr1(4, chrblockbank(4)); + mem_setchr1(5, chrblockbank(5)); + mem_setchr1(6, chrblockbank(6)); + mem_setchr1(7, chrblockbank(7)); + break; + + //block mode disabled + case 4: + mem_setchr8(0, chrbank(0)); + break; + + case 5: + mem_setchr4(0, chrbank(0)); + mem_setchr4(4, chrbank(4)); + break; + + case 6: + mem_setchr2(0, chrbank(0)); + mem_setchr2(2, chrbank(2)); + mem_setchr2(4, chrbank(4)); + mem_setchr2(6, chrbank(6)); + break; + + case 7: + mem_setchr1(0, chrbank(0)); + mem_setchr1(1, chrbank(1)); + mem_setchr1(2, chrbank(2)); + mem_setchr1(3, chrbank(3)); + mem_setchr1(4, chrbank(4)); + mem_setchr1(5, chrbank(5)); + mem_setchr1(6, chrbank(6)); + mem_setchr1(7, chrbank(7)); + break; + } +} + +#define ntbank(nn) (ntbank[nn] | (ntbank[(nn) + 4] << 8)) + +void sync_nt() +{ + int i; + + if (ntctl == 0) { + switch (mirror & 3) { + case 0: mem_setmirroring(MIRROR_V); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_1L); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } + } + + else { + for (i = 0; i < 4; i++) { + if ((mode & 0x40) || ((ntram ^ ntbank(i)) & 0x80)) { + mem_setchr1(0x8 + i, ntbank(i)); + mem_setchr1(0xC + i, ntbank(i)); + } + else { + mem_setnt1(0x8 + i, ntbank(i) & 1); + mem_setnt1(0xC + i, ntbank(i) & 1); + } + } + } +} + +//static void sync() +//{ +// sync_prg(); +// sync_chr(); +// sync_nt(); +//} + +static u8 read5(u32 addr) +{ + u8 ret = addr >> 8; + + switch (addr) { + case 0x5000: + ret = dip; + break; + case 0x5800: + ret = (mul[0] * mul[1]) & 0xFF; + break; + case 0x5801: + ret = (mul[0] * mul[1]) >> 8; + break; + + case 0x5803: + case 0x5804: + case 0x5805: + case 0x5806: + case 0x5807: + ret = ram[addr & 7]; + break; + + } + log_printf("read5: $%04X = $%02X\n", addr, ret); + return(ret); +} + +static void write5(u32 addr, u8 data) +{ + switch (addr) { + case 0x5800: + case 0x5801: + mul[addr & 1] = data; + break; + + case 0x5803: + case 0x5804: + case 0x5805: + case 0x5806: + case 0x5807: + ram[addr & 7] = data; + break; + } + log_printf("write5: $%04X = $%02X\n", addr, data); +} + +static u16 irqtotal; + +#define COUNT_UP() ((irqmode & 0xC0) == 0x40) + +static void update_prescaler(u8 n) +{ + n ^= irqxor; + n ^= (COUNT_UP() ? 0xFF : 0); + irqtotal &= (irqmode & 4) ? ~7 : ~0xFF; + irqtotal |= (irqmode & 4) ? (n & 7) : n; +} + +static void update_counter(u8 n) +{ + n ^= irqxor; + n ^= (COUNT_UP() ? 0xFF : 0); + irqtotal &= (irqmode & 4) ? 7 : 0xFF; + irqtotal |= (irqmode & 4) ? (n << 3) : (n << 8); +} + +static int irqline = -3; + +static void clock_irqtotal() +{ + if (irqtotal == 0) { + if (irqenabled) { + log_printf("irq\n"); + cpu_set_irq(IRQ_MAPPER); + irqline = SCANLINE; + } + } + irqtotal--; +// log_printf("clock_irqtotal: irqtotal = $%02X %02X\n",irqtotal >> 8, irqtotal & 0xFF); +} + +static void write(u32 addr, u8 data) +{ + switch (addr & 0xF000) { + case 0x8000: prgbank[addr & 3] = data; sync_prg(); break; + case 0x9000: chrbanklo[addr & 7] = data; sync_chr(); break; + case 0xA000: chrbankhi[addr & 7] = data; sync_chr(); break; + case 0xB000: ntbank[addr & 7] = data; sync_nt(); break; + case 0xC000: + switch (addr & 7) { + case 0: + break; + case 1: + irqmode = data; + log_printf("irqmode = %02X @ PC = %04X\n", data, nes->cpu.pc); + log_printf(" %d-bit prescaler\n", (irqmode & 4) ? 3 : 8); + log_printf(" counting %s\n", (irqmode & 0xC0) == 0x80 ? "down" : (irqmode & 0xC0) == 0x40 ? "up" : "paused"); + log_printf(" source %s\n", + (irqmode & 3) == 0 ? "cpu cycles" : + (irqmode & 3) == 1 ? "ppu a12 rising" : + (irqmode & 3) == 2 ? "ppu reads" : + (irqmode & 3) == 3 ? "cpu writes" : "unknown"); + log_printf(" funky mode %s\n", (irqmode & 8) ? "enabled" : "disabled"); + break; + case 2: + irqenabled = 0; + cpu_clear_irq(IRQ_MAPPER); + break; + case 3: + irqenabled = 1; + break; + case 4: + update_prescaler(data); + log_printf("irqprescaler = %02X (3bit= %1X) @ PC = %04X\n", data, data & 7, nes->cpu.pc); + break; + case 5: + update_counter(data); + log_printf("irqcounter = %02X @ PC = %04X\n", data, nes->cpu.pc); + break; + case 6: + irqxor = data; + break; + case 7: + break; + } + break; + case 0xD000: + switch (addr & 3) { + case 0: + mode = (data & ~0x20) | (ntctl ? 0x20 : 0); + log_printf("mode = %02X @ PC = %04X\n", data, nes->cpu.pc); + log_printf(" chr bank size = %d\n", 1 << (3 - ((mode & 0x18) >> 3))); + sync_prg(); + sync_chr(); + sync_nt(); + break; + case 1: + mirror = data; + sync_nt(); + break; + case 2: + ntram = data; + sync_nt(); + break; + case 3: + chrblock = data; + log_printf("chrblock = %02X @ PC = %04X\n", data, nes->cpu.pc); + sync_chr(); + break; + } + break; + case 0xE000: + break; + case 0xF000: + break; + } +} + +static void reset(int hard, int ntc) +{ + int i; + + ntctl = ntc; + mem_setreadfunc(5, read5); + mem_setwritefunc(5, write5); + for (i = 8; i<16; i++) + mem_setwritefunc(i, write); + + if (hard) { + mode = ntctl ? 0x20 : 0; + mirror = 0; + ntram = 0; + chrblock = 0; + for (i = 0; i < 8; i++) { + prgbank[i & 3] = 0; + chrbanklo[i] = 0; + chrbankhi[i] = 0; + ntbank[i] = 0; + } + } + + irqenabled = irqmode = 0; + irqxor = 0; + irqwait = 0; + + sync_prg(); + sync_chr(); + sync_nt(); +} + +static void reset_normal(int hard) +{ + reset(hard, 0); +} + +static void reset_ntctl(int hard) +{ + reset(hard, 1); + dip = 0xC0; +} + +static u32 irqaddr; + +static void ppucycle() +{ + if (irqenabled && (irqmode & 3) == 1) { + if ((irqaddr & 0x1000) == 0 && (nes->ppu.busaddr & 0x1000)) + clock_irqtotal(); + irqaddr = nes->ppu.busaddr; + } +} + +void video_updaterawpixel(int line, int pixel, u32 s); + +static void cpucycle() +{ + int i; + + if (LINECYCLES == 0) { + if (irqline != -3) { + for (i = 0; i < 256; i++) { + //video_updaterawpixel(irqline, i, 0x00FF00); + } + irqline = -3; + } + } + if (irqenabled && (irqmode & 3) == 0) { + clock_irqtotal(); + } +} + +static void state(int mode, u8 *data) +{ + +} + +MAPPER(B_JYCOMPANY, reset_normal, ppucycle, cpucycle, state); +MAPPER(B_JYCOMPANY_NTCTL, reset_ntctl, ppucycle, cpucycle, state); diff --git a/apps/nesemu2/src/mappers/boards/other/kasing.c b/apps/nesemu2/src/mappers/boards/other/kasing.c new file mode 100644 index 00000000..f43b1f23 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/kasing.c @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg[2]; + +static void sync() +{ + mmc3_syncprg(0xFF,0x00); + if(reg[0] & 0x80) { + mem_setprg16(8,reg[0] & 0xF); + } + mmc3_syncchr(0xFF,(reg[1] & 1) << 8); + mmc3_syncmirror(); +} + +static void write(u32 addr,u8 data) +{ + log_printf("other/kasing.c: write $%04X = $%02X\n"); + reg[addr & 1] = data; + sync(); +} + +static void reset(int hard) +{ + if(hard) + reg[0] = reg[1] = 0; + mmc3_reset(C_MMC3B,sync,hard); + mem_setwritefunc(6,write); + mem_setwritefunc(7,write); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,2); + mmc3_state(mode,data); +} + +MAPPER(B_KASING,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/magicseries.c b/apps/nesemu2/src/mappers/boards/other/magicseries.c new file mode 100644 index 00000000..ae23a975 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/magicseries.c @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_data >> 1); + mem_setchr8(0,latch_data); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_MAGICSERIES,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/nanjing.c b/apps/nesemu2/src/mappers/boards/other/nanjing.c new file mode 100644 index 00000000..4186bd61 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/nanjing.c @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[2],prg,security,trigger,strobe,vram[2]; + +static void sync() +{ + mem_setprg32(8,prg); + mem_setvram4(0, vram[0]); + mem_setvram4(4, vram[1]); +} + +static u8 read(u32 addr) +{ + log_printf("nanjing read: $%04X (PC = %04X)\n", addr, nes->cpu.pc); + switch(addr & 0x7700) { + case 0x5000: + return(4); + case 0x5100: + return(security); + case 0x5500: + return(security & trigger); + } + return((u8)(addr >> 8)); +} + +static void write(u32 addr, u8 data) +{ + log_printf("nanjing write: $%04X = $%02X (PC = %04X)\n", addr, data, nes->cpu.pc); + if(addr == 0x5101) { + u8 tmp = strobe; + + strobe = data; + if(tmp && strobe == 0) + trigger ^= 0xFF; + return; + } + switch(addr & 0x7300) { + case 0x5000: + reg[0] = data; + prg = (reg[0] & 0xF) | ((reg[1] & 0xF) << 4); + if((reg[0] & 0x80) == 0) { + vram[0] = 0; + vram[1] = 1; + } + sync(); + break; + case 0x5100: + if(data == 6) + prg = 3; + sync(); + break; + case 0x5200: + reg[1] = data; + prg = (reg[0] & 0xF) | ((reg[1] & 0xF) << 4); + sync(); + break; + case 0x5300: + security = data; + break; + } +} + +static u8 sram[0x2000]; + +static u8 read_sram(u32 addr) +{ + u8 ret = addr >> 8; + +// log_printf("sram read: $%04X (pc = %04X)\n", addr, nes->cpu.pc); + if (addr >= 0x6000 && addr < 0x8000) + ret = sram[addr & 0x1FFF]; + + return(ret); +} + +static void write_sram(u32 addr, u8 data) +{ +// log_printf("sram write: $%04X = $%02X (pc = %04X)\n", addr, data, nes->cpu.pc); + if (addr >= 0x6000 && addr < 0x8000) + sram[addr & 0x1FFF] = data; +} + +static void reset(int hard) +{ + mem_setreadfunc(5, read); + mem_setwritefunc(5, write); + mem_setreadfunc(6, read_sram); + mem_setwritefunc(6, write_sram); + mem_setreadfunc(7, read_sram); + mem_setwritefunc(7, write_sram); + mem_setvramsize(8); + mem_setwramsize(8); + reg[0] = 0xFF; + reg[1] = 0x00; + strobe = 0xFF; + trigger = 0; + security = 0; + vram[0] = 0; + vram[1] = 1; + sync(); +} + +static void ppucycle() +{ + if((reg[0] & 0x80) == 0) + return; + if(SCANLINE == 128) { + vram[0] = vram[1] = 1; + sync(); + } + else if(SCANLINE == 240) { + vram[0] = vram[1] = 0; + sync(); + } +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,2); + STATE_U8(security); + STATE_U8(trigger); + STATE_U8(strobe); + STATE_ARRAY_U8(vram,2); + sync(); +} + +MAPPER(B_NANJING,reset,ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/nesdisksystem.c b/apps/nesemu2/src/mappers/boards/other/nesdisksystem.c new file mode 100644 index 00000000..f1455a11 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/nesdisksystem.c @@ -0,0 +1,51 @@ +/*************************************************************************** +* Copyright (C) 2013-2016 by James Holodnak * +* jamesholodnak@gmail.com * +* * +* 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., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include "mappers/mapperinc.h" + +static readfunc_t read4; +static writefunc_t write4; + +static u8 read(u32 addr) +{ + //default handler + return(read4(addr)); +} + +static void write(u32 addr, u8 data) +{ + write4(addr, data); +} + +static void reset(int hard) +{ + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4, read); + mem_setwritefunc(4, write); + mem_setvramsize(8); + mem_setwramsize(32); + + mem_setprg32(0x5, 0); + mem_setprg4(0xF, 0xFF); + mem_setvram8(0, 0); +} + +MAPPER(B_NES_DISK_SYSTEM, reset, 0, 0, 0); diff --git a/apps/nesemu2/src/mappers/boards/other/nitra.c b/apps/nesemu2/src/mappers/boards/other/nitra.c new file mode 100644 index 00000000..453fb29f --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/nitra.c @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void write(u32 addr,u8 data) +{ + mmc3_write((addr & 0xE000) | (addr >> 10 & 1),addr & 0xFF); +} + +static void reset(int hard) +{ + int i; + + mmc3_reset(C_MMC3B,mmc3_sync,hard); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); +} + +MAPPER(B_NITRA,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/other/opencorp.c b/apps/nesemu2/src/mappers/boards/other/opencorp.c new file mode 100644 index 00000000..1ce6129d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/opencorp.c @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr[8],chrhi[8]; + +static void sync() +{ + int i; + + mem_setwram8(0x6,0); + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i] | (chrhi[i] << 8)); +} + +static void write(u32 addr,u8 data) +{ + switch(addr) { + case 0xC000: + case 0xC001: + case 0xC002: + case 0xC003: + chr[addr & 3] = data; + break; + case 0xC004: + case 0xC005: + case 0xC006: + case 0xC007: + chrhi[addr & 3] = data; + break; + case 0xC008: + case 0xC009: + case 0xC00A: + case 0xC00B: + chr[(addr & 3) + 4] = data; + break; + case 0xC00C: + case 0xC00D: + case 0xC00E: + case 0xC00F: + chrhi[(addr & 3) + 4] = data; + break; + case 0xC010: + prg = data; + break; + default: + log_printf("opencorp.c: unhandled write $%04X = $%02X\n",addr,data); + break; + } + sync(); +} + +static void reset(int hard) +{ + mem_setwramsize(8); + mem_setwritefunc(0xC,write); + prg = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_ARRAY_U8(chr,8); + STATE_ARRAY_U8(chrhi,8); + sync(); +} + +MAPPER(B_OPENCORP,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/rumblestation.c b/apps/nesemu2/src/mappers/boards/other/rumblestation.c new file mode 100644 index 00000000..48593fbc --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/rumblestation.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[2]; + +static void sync() +{ + mem_setprg32(8,((reg[1] & 0xF) << 1) | (reg[0] & 1)); + mem_setchr8(0,((reg[1] & 0xF0) >> 1) | ((reg[0] & 0x70) >> 4)); +} + +//write high chr/prg bits (write addresses $6000-7FFF) +static void writehi(u32 addr,u8 data) +{ + reg[1] = data; + sync(); +} + +//write low chr/prg bits (write addresses $8000-FFFF) +static void writelo(u32 addr,u8 data) +{ + reg[0] = data; + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=6;i<8;i++) + mem_setwritefunc(i,writehi); + for(i=8;i<16;i++) + mem_setwritefunc(i,writelo); + reg[0] = 0; + reg[1] = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg[0]); + STATE_U8(reg[1]); + sync(); +} + +MAPPER(B_RUMBLESTATION,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/unl/bitcorp.c b/apps/nesemu2/src/mappers/boards/other/unl/bitcorp.c new file mode 100644 index 00000000..4d4258cf --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/unl/bitcorp.c @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_data); + mem_setchr8(0,latch_data >> 2); +} + +static void reset(int hard) +{ + int i; + + latch_reset(sync,hard); + mem_setwritefunc(6,latch_write); + mem_setwritefunc(7,latch_write); + for(i=8;i<16;i++) + mem_setwritefunc(i,0); +} + +MAPPER(B_UNL_BITCORP,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/unl/fs304.c b/apps/nesemu2/src/mappers/boards/other/unl/fs304.c new file mode 100644 index 00000000..e34b20af --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/unl/fs304.c @@ -0,0 +1,104 @@ +/*************************************************************************** +* Copyright (C) 2016 by James Holodnak * +* jamesholodnak@gmail.com * +* * +* 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., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static u8 regs[4]; + +static u8 _prgBank; + +static void sync() +{ + int bank; + +// log_printf("fs304: sync: data = %02X\n", data); +/* + switch (regs[3] & 7) { + case 0: //000 + case 2: //010 + bank = (regs[0] & 0xc) | (regs[1] & 2) | ((regs[2] & 0xf) << 4); + break; + + case 1: //001 + case 3: //011 + bank = (regs[0] & 0xc) | (regs[2] & 0xf) << 4; + break; + + case 4: //100 + case 6: //110 + bank = (regs[0] & 0xe) | ((regs[1] >> 1) & 1) | ((regs[2] & 0xf) << 4); + break; + + case 5: //101 + case 7: //111 + bank = (regs[0] & 0xf) | ((regs[2] & 0xf) << 4); + break; + } + +// bank = ((regs[2] & 0x0f) << 4) | ((regs[1] >> 1) & 1) | (regs[0] & 0x0e); + + bank = regs[0] & 0xF; + bank |= (regs[1] & 0xF) << 4; + mem_setprg32(8, bank); + */ + + bank = (regs[0] & 0xe) | ((regs[1] >> 1) & 1) | ((regs[2] & 0xf) << 4); + + mem_setprg32(8, bank); +} + +static void write(u32 addr, u8 data) +{ + if(addr == 0x5300) + log_printf("fs304: write: %04X = %02X (pc = $%04X)\n", addr, data,nes->cpu.pc); + regs[(addr >> 8) & 3] = data; + + sync(); +} + +static void write_high(u32 addr, u8 data) +{ + log_printf("fs304: write: %04X = %02X\n", addr, data); +} + +static void reset(int hard) +{ + int i; + + _prgBank = 0xF; + + for (i = 8; i < 16; i++) { + mem_setwritefunc(i, write_high); + } + mem_setwramsize(8); + mem_setvramsize(8); + mem_setwram8(6, 0); + mem_setvram8(0, 0); + mem_setwritefunc(0x5, write); +// mem_setwritefunc(0xD, write); + regs[0] = 0xF; + regs[1] = 0xF; + regs[2] = 0xF; + regs[3] = 0xF; + sync(); +} + +MAPPER(B_UNL_FS304, reset, 0, 0, latch_state); diff --git a/apps/nesemu2/src/mappers/boards/other/unl/h2288.c b/apps/nesemu2/src/mappers/boards/other/unl/h2288.c new file mode 100644 index 00000000..4a6c3f46 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/unl/h2288.c @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 security[8] = {0,3,1,5,6,7,2,4}; +static u8 reg[2]; + +static void sync() +{ + u8 bank; + + if(reg[0] & 0x40) { + bank = (reg[0] & 5) | ((reg[0] >> 2) & 2) | ((reg[0] >> 2) & 8); + if(reg[0] & 2) + mem_setprg32(0x8,bank >> 1); + else { + mem_setprg16(0x8,bank); + mem_setprg16(0xC,bank); + } + } + else + mmc3_syncprg(0xFF,0); + mmc3_syncchr(0xFF,0); + mmc3_syncsram(); + mmc3_syncmirror(); +} + +static u8 read5(u32 addr) +{ + return(((addr >> 8) & 0xFE) | (((~addr >> 8) | addr) & 1)); +} + +static void write5(u32 addr,u8 data) +{ + if(addr < 0x5800) { + return; + } + reg[addr & 1] = data; + sync(); +} + +static void write_security(u32 addr,u8 data) +{ + if(addr & 1) + mmc3_write(addr,data); + else + mmc3_write(addr,(data & 0xC0) | security[data & 7]); +} + +static void reset(int hard) +{ + if(hard) + reg[0] = reg[1] = 0; + mem_setreadfunc(5,read5); + mem_setwritefunc(5,write5); + mmc3_reset(C_MMC3B,sync,hard); + mem_setwritefunc(8,write_security); + mem_setwritefunc(9,write_security); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,2); + mmc3_state(mode,data); +} + +MAPPER(B_UNL_H2288,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/unl/racermate.c b/apps/nesemu2/src/mappers/boards/other/unl/racermate.c new file mode 100644 index 00000000..567af884 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/unl/racermate.c @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr; +static u32 irqcounter; + +static void sync() +{ + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xFF); + mem_setvram4(0,0); + mem_setvram4(4,chr); +} + +static void write_cic(u32 addr,u8 data) +{ +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xC000) { + case 0x8000: + prg = data >> 6; + chr = data & 0xF; + break; + case 0xC000: + cpu_clear_irq(IRQ_MAPPER); + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + mem_setwritefunc(6,write_cic); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + mem_setvramsize(64); + prg = 0; + chr = 0; + irqcounter = 0; + sync(); +} + +static void cpucycle() +{ + irqcounter++; + if(irqcounter == 1024) { + irqcounter = 0; + cpu_set_irq(IRQ_MAPPER); + } +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(chr); + STATE_U32(irqcounter); + sync(); +} + +MAPPER(B_UNL_RACERMATE,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/other/unl/superlionking.c b/apps/nesemu2/src/mappers/boards/other/unl/superlionking.c new file mode 100644 index 00000000..6dcfc099 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/unl/superlionking.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 prg,valid; + +static void sync() +{ + mmc3_syncprg(0xFF,0x00); + mmc3_syncchr(0xFF,0x00); + mmc3_syncmirror(); + if(prg & 0x80) { + mem_setprg16(0x8,prg & 0x1F); + mem_setprg16(0xC,prg & 0x1F); + } +} + +static void writelo(u32 addr,u8 data) +{ + prg = data; + sync(); +} + +static void write(u32 addr,u8 data) +{ + u8 scramble[8] = {0,3,1,5,6,7,2,4}; + + switch(addr & 0xE000) { + case 0x8000: + mmc3_write(0xA000,data); + break; + case 0xA000: + mmc3_write(0x8000,scramble[data & 7] | (data & 0xC0)); + valid = 1; + break; + case 0xC000: + if(valid) { + mmc3_write(0x8001,data); + valid = 0; + } + break; + case 0xE000: + if(addr == 0xE002) { + mmc3_write(0xE000,data); + } + else if(addr == 0xE003) { + mmc3_write(0xC000,data); + mmc3_write(0xC001,data); + mmc3_write(0xE001,data); + } + break; + } +} + +static void reset(int hard) +{ + int i; + + prg = 0; + valid = 1; + mmc3_reset(C_MMC3B,mmc3_sync,hard); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + mem_setwritefunc(5,writelo); + mem_setwritefunc(6,writelo); + mem_setwritefunc(7,writelo); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(valid); + mmc3_state(mode,data); +} + +MAPPER(B_UNL_SUPERLIONKING,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/other/unl/uy.c b/apps/nesemu2/src/mappers/boards/other/unl/uy.c new file mode 100644 index 00000000..2575f6ce --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/other/unl/uy.c @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setchr8(0,latch_data & 3); +} + +static void reset(int hard) +{ + int i; + + latch_reset(sync,hard); + mem_setwritefunc(6,latch_write); + mem_setwritefunc(7,latch_write); + for(i=8;i<16;i++) + mem_setwritefunc(i,0); + mem_setprg32(8,0); +} + +MAPPER(B_UNL_UY,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/rexsoft/dbz5.c b/apps/nesemu2/src/mappers/boards/rexsoft/dbz5.c new file mode 100644 index 00000000..1e7a6443 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/rexsoft/dbz5.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg; +static writefunc_t write4; + +static void mmc3_syncchrlo(int a,int o) +{ + int i; + + for(i=0;i<4;i++) + mem_setchr1(i,(mmc3_getchrbank(i) & a) | o); +} + +static void mmc3_syncchrhi(int a,int o) +{ + int i; + + for(i=4;i<8;i++) + mem_setchr1(i,(mmc3_getchrbank(i) & a) | o); +} + +static void sync() +{ + mmc3_syncprg(0xFF,0x00); + mmc3_syncchrlo(0x1FF,(reg << 8) & 0x100); + mmc3_syncchrhi(0x1FF,(reg << 4) & 0x100); + mmc3_syncmirror(); + mmc3_syncsram(); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + reg = data; +} + +static void reset(int hard) +{ + mmc3_reset(C_MMC3B,sync,hard); + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write); + mem_setwritefunc(5,write); + reg = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_REXSOFT_DBZ5,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/rexsoft/sl1632.c b/apps/nesemu2/src/mappers/boards/rexsoft/sl1632.c new file mode 100644 index 00000000..6262877b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/rexsoft/sl1632.c @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 prg[2],chr[8],mirror, bankmode; +static writefunc_t write4; + +static void syncprg() +{ + if(bankmode & 2) { + mmc3_syncprg(0xFF,0x00); + } + else { + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,0xFE); + mem_setprg8(0xE,0xFF); + } +} + +static void syncchr() +{ + u8 modes[4] = {5,5,3,1}; + int i,bank; + + if(bankmode & 2) { + for(i=0;i<8;i++) { + bank = mmc3_getchrreg(i); + bank |= bankmode << modes[(i >> 1) ^ ((mmc3_getcommand() >> 6) & 2)] & 0x100; + mem_setchr1(i,bank); + } + } + else { + for(i=0;i<8;i++) { + mem_setchr1(i,chr[i]); + } + } +} + +static void sync() +{ + syncprg(); + syncchr(); + mmc3_syncsram(); + mmc3_syncmirror(); +} + +static void write_mode(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + log_printf("sl1632.c: mode: $%04X = $%02X\n",addr,data); + if((addr & 0xFF00) == 0x4100) { + log_printf("sl1632.c: mode: $%04X = $%02X\n",addr,data); + bankmode = data; + sync(); + } +} + +static void write(u32 addr,u8 data) +{ + int n,shift; + + if((addr & 0xA131) == 0xA131) { + log_printf("sl1632.c: mode: $%04X = $%02X\n",addr,data); + bankmode = data; + } + + if(bankmode & 2) { + mmc3_write(addr,data); + } + else { + if(addr >= 0xB000 && addr <= 0xE003) { + shift = ((addr & 1) << 2); + n = ((((addr & 2) | (addr >> 10)) >> 1) + 2) & 7; + chr[n] = (chr[n] & (0xF0 >> shift)) | ((data & 0x0F) << shift); + } + else { + switch(addr & 0xF003) { + case 0x8000: + prg[0] = data; + break; + case 0x9000: + mirror = data; + break; + case 0xA000: + prg[1] = data; + break; + } + } + sync(); + } + log_printf("sl1632.c: write: $%04X = $%02X\n",addr,data); +} + +static void reset(int hard) +{ + int i; + + bankmode = 0; + if(hard) { + for(i=0;i<8;i++) { + prg[i & 1] = 0; + chr[i] = 0; + } + mirror = 0; + } + mmc3_reset(C_MMC3B,sync,hard); + write4 = mem_getwritefunc(4); + for(i=4;i<16;i++) { + if(i < 8) + mem_setwritefunc(i,write_mode); + else + mem_setwritefunc(i,write); + } + sync(); +} + +static void ppucycle() +{ + mmc3_ppucycle(); +} + +static void state(int mode,u8 *data) +{ + mmc3_state(bankmode,data); +} + +MAPPER(B_REXSOFT_SL1632,reset,ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/sachen/74ls374n.c b/apps/nesemu2/src/mappers/boards/sachen/74ls374n.c new file mode 100644 index 00000000..8c973a0b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/74ls374n.c @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static int type; +static u8 cmd,prg,chr,mirror; +static readfunc_t read4; +static writefunc_t write4; + +static void sync() +{ + mem_setprg16(0x8,(prg << 1) | 0); + mem_setprg16(0xC,(prg << 1) | 1); + mem_setchr8(0,chr); +// log_printf("sync: prg = $%X (L:%X H:%X), chr = $%X, chr / 2 = $%X\n",prg,(prg<<1),(prg<<1)|1,chr,chr/2); + switch(mirror) { + case 0: + case 1: + mem_setmirroring(mirror & 1); + break; + case 2: + mem_setmirroring2(0,1,1,1); + break; + case 3: + mem_setmirroring(MIRROR_1H); + break; + } +} + +static u8 read(u32 addr) +{ + if(addr < 0x4020) + return(read4(addr)); + if(addr == 0x4100) + return(~cmd & 0x3F); + return(0); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } +// data &= 7; + if((addr & 0x4101) == 0x4100) { + cmd = data & 7; + } + else if((addr & 0x4101) == 0x4101) { + log_printf("sachen-74ls374n: write cmd %d = $%02X\n",cmd,data); + switch(cmd) { +/* case 0: + prg = 0; + chr = 3; + break; +*/ case 2: + chr &= ~8; + chr |= (data & 1) << 3; + break; + case 4: + chr &= ~4; + chr |= (data & 1) << 2; + break; + case 5: + prg = data & 7; + break; + case 6: + chr &= ~3; + chr |= (data & 3) << 0; + break; + case 7: + mirror = (data & 6) >> 1; + break; + default: + log_printf("sachen-74ls374n: unknown write cmd %d (data = $%02X)\n",cmd,data); + break; + } + } + sync(); +} + +static void reset(int t,int hard) +{ + type = t; + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4,read); + mem_setwritefunc(4,write); + cmd = 0; + prg = 0; + chr = 3; + mirror = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(cmd); + STATE_U8(prg); + STATE_U8(chr); + STATE_U8(mirror); + sync(); +} + +static void reset_74ls374n(int hard) { reset(B_SACHEN_74LS374N,hard); } +//static void reset_74ls374na(int hard) { reset(B_SACHEN_74LS374NA,hard); } + +MAPPER(B_SACHEN_74LS374N,reset_74ls374n,0,0,state); +//MAPPER(B_SACHEN_74LS374NA,reset_74ls374na,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/sachen/8259x.c b/apps/nesemu2/src/mappers/boards/sachen/8259x.c new file mode 100644 index 00000000..583f1b30 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/8259x.c @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/sachen-8259.h" + +static void reset_8259a(int hard) { sachen8259_reset(B_SACHEN_8259A,hard); } +static void reset_8259b(int hard) { sachen8259_reset(B_SACHEN_8259B,hard); } +static void reset_8259c(int hard) { sachen8259_reset(B_SACHEN_8259C,hard); } +static void reset_8259d(int hard) { sachen8259_reset(B_SACHEN_8259D,hard); } + +MAPPER(B_SACHEN_8259A,reset_8259a,0,0,sachen8259_state); +MAPPER(B_SACHEN_8259B,reset_8259b,0,0,sachen8259_state); +MAPPER(B_SACHEN_8259C,reset_8259c,0,0,sachen8259_state); +MAPPER(B_SACHEN_8259D,reset_8259d,0,0,sachen8259_state); diff --git a/apps/nesemu2/src/mappers/boards/sachen/sa-003x.c b/apps/nesemu2/src/mappers/boards/sachen/sa-003x.c new file mode 100644 index 00000000..ab0fa9ff --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/sa-003x.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg; +static writefunc_t write4; +static void (*sync)() = 0; + +static void sync_0036() +{ + mem_setprg32(0x8,0); + log_printf("sync_0036: reg = %02X\n",reg); + mem_setchr8(0,(reg >> 7)); +} + +static void sync_0037() +{ + mem_setprg32(8,(reg >> 3) & 1); + log_printf("sync_0037: reg = %02X\n",reg); + mem_setchr8(0,reg & 7); +} + +static void write45(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + log_printf("sa-003x.c: write45: $%04X = $%02X\n"); + if(addr & 0x100) { + reg = data; + sync(); + } +} + +static void reset(int type,int hard) +{ + if(type == B_SA_0036) + sync = sync_0036; + else + sync = sync_0037; + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write45); + mem_setwritefunc(5,write45); + reg = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +static void reset_0036(int hard) { reset(B_SA_0036,hard); } +static void reset_0037(int hard) { reset(B_SA_0037,hard); } + +MAPPER(B_SA_0036,reset_0036,0,0,state); +MAPPER(B_SA_0037,reset_0037,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/sachen/sa-0161m.c b/apps/nesemu2/src/mappers/boards/sachen/sa-0161m.c new file mode 100644 index 00000000..63928600 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/sa-0161m.c @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static writefunc_t write4; +static u8 reg; + +static void sync() +{ + mem_setprg32(8,(reg >> 3) & 1); + mem_setchr8(0,reg & 7); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + if(addr & 0x100) { + reg = data; + sync(); + } +} + +static void reset(int hard) +{ + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write); + mem_setwritefunc(5,write); + if(hard) { + reg = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +MAPPER(B_SA_0161M,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/sachen/sa-7200x.c b/apps/nesemu2/src/mappers/boards/sachen/sa-7200x.c new file mode 100644 index 00000000..330af82d --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/sa-7200x.c @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg; +static writefunc_t write4; +static void (*sync)() = 0; + +static void sync_72007() +{ + mem_setprg16(0x8,0); + mem_setprg16(0xC,1); + mem_setchr8(0,reg >> 7); +} + +static void sync_72008() +{ + mem_setprg32(8,(reg >> 2) & 1); + mem_setchr8(0,reg & 3); +} + +static void write45(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + if(addr & 0x100) { + reg = data; + sync(); + } +} + +static void reset(int type,int hard) +{ + if(type == B_SA_72007) + sync = sync_72007; + else + sync = sync_72008; + write4 = mem_getwritefunc(4); + mem_setwritefunc(4,write45); + mem_setwritefunc(5,write45); + reg = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +static void reset_72007(int hard) { reset(B_SA_72007,hard); } +static void reset_72008(int hard) { reset(B_SA_72008,hard); } + +MAPPER(B_SA_72007,reset_72007,0,0,state); +MAPPER(B_SA_72008,reset_72008,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/sachen/tca01.c b/apps/nesemu2/src/mappers/boards/sachen/tca01.c new file mode 100644 index 00000000..7b384996 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/tca01.c @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static readfunc_t read4; + +static u8 read(u32 addr) +{ + if(addr < 0x4020) + return(read4(addr)); + if(addr & 0x100) + return((~addr & 0x3F) | (nes->cpu.x & 0xC0)); + return(0xFF); +} + +static void reset(int hard) +{ + read4 = mem_getreadfunc(4); + mem_setreadfunc(4,read); + mem_setreadfunc(5,read); + mem_setprg32(8,0); + mem_setchr8(0,0); +} + +MAPPER(B_SACHEN_TCA01,reset,0,0,0); diff --git a/apps/nesemu2/src/mappers/boards/sachen/tcu01.c b/apps/nesemu2/src/mappers/boards/sachen/tcu01.c new file mode 100644 index 00000000..706e33a9 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sachen/tcu01.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static writefunc_t write4; +static u8 prg,chr; + +static void sync() +{ + mem_setprg32(8,prg); + mem_setchr8(0,chr); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + if((addr & 0x103) == 0x102) { + prg = (data >> 2) & 1; + chr = (data >> 3) & 0xF; + sync(); + } +} + +static void reset(int hard) +{ + int i; + + write4 = mem_getwritefunc(4); + for(i=4;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + prg = chr = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(chr); + sync(); +} + +MAPPER(B_SACHEN_TCU01,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/subor/studygame.c b/apps/nesemu2/src/mappers/boards/subor/studygame.c new file mode 100644 index 00000000..a3922e70 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/subor/studygame.c @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,latch_data); +// mem_setchr8(0,latch_data >> 2); +} + +static void reset(int hard) +{ + latch_reset(sync,hard); +} + +MAPPER(B_SUBOR_STUDYGAME,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-1.c b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-1.c new file mode 100644 index 00000000..2fce12e6 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-1.c @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,0); + mem_setchr4(0,latch_data & 7); + mem_setchr4(4,((latch_data >> 4) & 7) | 4); +} + +static void reset(int hard) +{ + int i; + + latch_reset(sync,hard); + for(i=8;i<16;i++) + mem_setwritefunc(i,0); + mem_setwritefunc(6,latch_write); + mem_setwritefunc(7,latch_write); +} + +MAPPER(B_SUNSOFT_1,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-2.c b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-2.c new file mode 100644 index 00000000..4f9de69e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-2.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg16(0x8,(latch_data >> 4) & 7); + mem_setprg16(0xC,0xFF); + if(nes->cart->chr.size == 0) { + mem_setvram8(0,0); + } + else { + mem_setchr8(0,(latch_data & 7) | ((latch_data >> 4) & 8)); + mem_setmirroring(MIRROR_1L + ((latch_data >> 3) & 1)); + } +} + +static void reset(int hard) +{ + if(nes->cart->chr.size == 0) { + mem_setvramsize(8); + } + latch_reset(sync,hard); +} + +MAPPER(B_SUNSOFT_2,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-3.c b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-3.c new file mode 100644 index 00000000..84c9ed91 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-3.c @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr[4],mirror; +static u16 irqcounter; +static u8 irqenable,irqtoggle; + +static void sync() +{ + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xFF); + mem_setchr2(0,chr[0]); + mem_setchr2(2,chr[1]); + mem_setchr2(4,chr[2]); + mem_setchr2(6,chr[3]); + switch(mirror) { + case 0: mem_setmirroring(MIRROR_V); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_1L); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xF800) { + case 0x8800: + case 0x9800: + case 0xA800: + case 0xB800: + chr[(addr >> 12) & 3] = data; + break; + case 0xC800: + if(irqtoggle) { + irqcounter = (irqcounter & 0xFF00) | data; + } + else { + irqcounter = (irqcounter & 0x00FF) | (data << 8); + } + irqtoggle ^= 1; + break; + case 0xD800: + irqenable = data & 0x10; + irqtoggle = 0; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xE800: + mirror = data & 3; + break; + case 0xF800: + prg = data; + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + prg = 0; + chr[0] = chr[1] = chr[2] = chr[3] = 0; + mirror = 0; + irqcounter = 0xFFFF; + irqenable = irqtoggle = 0; + } + sync(); +} + +static void cpucycle() +{ + if(irqenable == 0) + return; + if(irqcounter == 0) { + irqenable = 0; + cpu_set_irq(IRQ_MAPPER); + } + irqcounter--; +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_ARRAY_U8(chr,4); + STATE_U8(mirror); + STATE_U16(irqcounter); + STATE_U8(irqenable); + STATE_U8(irqtoggle); + sync(); +} + +MAPPER(B_SUNSOFT_3,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-4.c b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-4.c new file mode 100644 index 00000000..0cfba03f --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-4.c @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg,chr[4],nt[2],mirror; + +static void sync() +{ + if(prg & 0x10) + mem_setwram8(6,0); + else + mem_unsetcpu8(6); + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xFF); + mem_setchr2(0,chr[0]); + mem_setchr2(2,chr[1]); + mem_setchr2(4,chr[2]); + mem_setchr2(6,chr[3]); + if(mirror & 0x10) { + if((mirror & 1) == 0) { + mem_setchr1(0x8,nt[0] | 0x80); + mem_setchr1(0x9,nt[1] | 0x80); + mem_setchr1(0xA,nt[0] | 0x80); + mem_setchr1(0xB,nt[1] | 0x80); + } + else { + mem_setchr1(0x8,nt[0] | 0x80); + mem_setchr1(0x9,nt[0] | 0x80); + mem_setchr1(0xA,nt[1] | 0x80); + mem_setchr1(0xB,nt[1] | 0x80); + } + } + else + mem_setmirroring((mirror & 1) ^ 1); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0x8000: + case 0x9000: + case 0xA000: + case 0xB000: + chr[(addr >> 12) & 3] = data; + break; + case 0xC000: + case 0xD000: + nt[(addr >> 12) & 1] = data; + break; + case 0xE000: + mirror = data; + break; + case 0xF000: + prg = data; + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + prg = 0; + chr[0] = chr[1] = chr[2] = chr[3] = 0; + nt[0] = nt[1] = 0; + mirror = 0; + } + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_ARRAY_U8(chr,4); + STATE_ARRAY_U8(nt,2); + STATE_U8(mirror); + sync(); +} + +MAPPER(B_SUNSOFT_4,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-5b.c b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-5b.c new file mode 100644 index 00000000..850e8138 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/sunsoft/sunsoft-5b.c @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/sound/s_FME7.h" +#include "nes/nes.h" + +static apu_external_t fme7 = { + FME7sound_Load, + FME7sound_Unload, + FME7sound_Reset, + FME7sound_Get, + 0 +}; + +static u8 prg[4],chr[8],mirror; +static u16 irqcounter; +static u8 irqcontrol,command; + +static void sync() +{ + int i; + + switch((prg[0] >> 6) & 3) { + case 0: + case 2: + mem_setprg8(6,prg[0] & 0x3F); + break; + case 1: + //open bus + mem_unsetcpu8(6); + break; + case 3: + mem_setwram8(6,0); + break; + } + mem_setprg8(0x8,prg[1]); + mem_setprg8(0xA,prg[2]); + mem_setprg8(0xC,prg[3]); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i,chr[i]); + switch(mirror) { + case 0: mem_setmirroring(MIRROR_V); break; + case 1: mem_setmirroring(MIRROR_H); break; + case 2: mem_setmirroring(MIRROR_1L); break; + case 3: mem_setmirroring(MIRROR_1H); break; + } +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xE000) { + case 0x8000: + command = data & 0xF; + break; + case 0xA000: + switch(command) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + chr[command] = data; + break; + case 0x8: + case 0x9: + case 0xA: + case 0xB: + prg[command & 3] = data; + break; + case 0xC: + mirror = data; + break; + case 0xD: + irqcontrol = data; + if((data & 1) == 0) + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xE: + irqcounter = (irqcounter & 0xFF00) | data; + break; + case 0xF: + irqcounter = (irqcounter & 0x00FF) | (data << 8); + break; + } + sync(); + break; + } +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + for(i=0;i<8;i++) { + prg[i & 3] = 0; + chr[i] = 0; + } + mirror = 0; + irqcounter = 0xFFFF; + irqcontrol = 0; + command = 0; + } + apu_setexternal(&fme7); + sync(); +} + +static void cpucycle() +{ + if(irqcontrol & 0x80) { + irqcounter--; + } + if(irqcounter == 0) { + if(irqcontrol & 1) + cpu_set_irq(IRQ_MAPPER); + irqcontrol = 0; + } +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,4); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U16(irqcounter); + STATE_U8(irqcontrol); + STATE_U8(command); + sync(); +} + +MAPPER(B_SUNSOFT_5B,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/taito/tc0190fmc.c b/apps/nesemu2/src/mappers/boards/taito/tc0190fmc.c new file mode 100644 index 00000000..31fdd075 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/taito/tc0190fmc.c @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/taito-tc0190fmc.h" + +MAPPER(B_TAITO_TC0190FMC,tc0190fmc_reset,0,0,tc0190fmc_state); diff --git a/apps/nesemu2/src/mappers/boards/taito/tc0190fmc_pal16r4.c b/apps/nesemu2/src/mappers/boards/taito/tc0190fmc_pal16r4.c new file mode 100644 index 00000000..e33ee31e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/taito/tc0190fmc_pal16r4.c @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/taito-tc0190fmc.h" + +static u8 irqlatch,irqcounter; +static u8 irqreload,irqenabled; +static u8 irqwait,needirq; + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xE003) { + case 0xC000: + irqlatch = data ^ 0xFF; + break; + case 0xC001: + irqcounter = 0; + irqreload = 1; + break; + case 0xC002: + irqenabled = 0; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xC003: + irqenabled = 1; + break; + } +} + +static void reset(int hard) +{ + tc0190fmc_reset(hard); + mem_setwritefunc(0xC,write); + mem_setwritefunc(0xD,write); + irqcounter = irqlatch = 0; + irqenabled = irqreload = 0; + irqwait = needirq = 0; +} + +static void ppucycle() +{ + u8 tmp; + + if(needirq && (--needirq) == 0) + cpu_set_irq(IRQ_MAPPER); + if(irqwait) + irqwait--; + if((irqwait == 0) && (nes->ppu.busaddr & 0x1000)) { + tmp = irqcounter; + if((irqcounter == 0) || irqreload) + irqcounter = irqlatch; + else + irqcounter--; + if((tmp || irqreload) && (irqcounter == 0) && irqenabled) + needirq = 12; + irqreload = 0; + } + if(nes->ppu.busaddr & 0x1000) { + irqwait = 8; + } +} + +static void state(int mode,u8 *data) +{ + tc0190fmc_state(mode,data); + STATE_U8(irqlatch); + STATE_U8(irqcounter); + STATE_U8(irqreload); + STATE_U8(irqenabled); + STATE_U8(irqwait); + STATE_U8(needirq); +} + +MAPPER(B_TAITO_TC0190FMC_PAL16R4,reset,ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/taito/x1-005.c b/apps/nesemu2/src/mappers/boards/taito/x1-005.c new file mode 100644 index 00000000..19decf1e --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/taito/x1-005.c @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +enum taito_x1_005_e { + TAITO_X1_005, + TAITO_X1_005A, +}; + +static u8 prg[3],chr[6]; +static u8 mirror,ramenable; +static u8 ram[128]; +static u8 type; + +static void sync() +{ + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,0xFF); + mem_setchr2(0,chr[0] >> 1); + mem_setchr2(2,chr[1] >> 1); + mem_setchr1(4,chr[2]); + mem_setchr1(5,chr[3]); + mem_setchr1(6,chr[4]); + mem_setchr1(7,chr[5]); + mem_setmirroring(mirror); +} + +static u8 read_low(u32 addr) +{ + if(addr >= 0x7F00) { + if(ramenable == 0xA3) { + return(ram[addr & 0x7F]); + } + } + return((u8)(addr >> 8)); +} + +static void write_low(u32 addr,u8 data) +{ + if(addr >= 0x7F00) { + if(ramenable == 0xA3) { + ram[addr & 0x7F] = data; + } + } + switch(addr) { + case 0x7EF0: + case 0x7EF1: + case 0x7EF2: + case 0x7EF3: + case 0x7EF4: + case 0x7EF5: + chr[addr & 7] = data; + break; + case 0x7EF6: + case 0x7EF7: + mirror = data & 1; + break; + case 0x7EF8: + case 0x7EF9: + ramenable = data; + break; + case 0x7EFA: + case 0x7EFB: + prg[0] = data; + break; + case 0x7EFC: + case 0x7EFD: + prg[1] = data; + break; + case 0x7EFE: + case 0x7EFF: + prg[2] = data; + break; + } + sync(); +} + +static void reset(int t,int hard) +{ + int i; + + type = t; + mem_setreadfunc(7,read_low); + mem_setwritefunc(7,write_low); + prg[0] = prg[1] = prg[2] = 0; + for(i=0;i<6;i++) { + chr[i] = 0; + } + mirror = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,3); + STATE_ARRAY_U8(chr,6); + STATE_U8(mirror); + STATE_ARRAY_U8(ram,128); + sync(); +} + +static void reset_x1_005(int hard) +{ + reset(TAITO_X1_005,hard); +} + +static void reset_x1_005a(int hard) +{ + reset(TAITO_X1_005A,hard); +} + +MAPPER(B_TAITO_X1_005,reset_x1_005,0,0,state); +MAPPER(B_TAITO_X1_005A,reset_x1_005a,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/taito/x1-017.c b/apps/nesemu2/src/mappers/boards/taito/x1-017.c new file mode 100644 index 00000000..7ee10679 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/taito/x1-017.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg[3],chr[6],ramenable[3]; +static u8 mirror,chrinvert; +static u8 *sram; + +static void sync() +{ + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + mem_setprg8(0xE,0xFF); + mem_setchr2(0 ^ chrinvert,chr[0] >> 1); + mem_setchr2(2 ^ chrinvert,chr[1] >> 1); + mem_setchr1(4 ^ chrinvert,chr[2]); + mem_setchr1(5 ^ chrinvert,chr[3]); + mem_setchr1(6 ^ chrinvert,chr[4]); + mem_setchr1(7 ^ chrinvert,chr[5]); + mem_setmirroring(mirror); +} + +static u8 read_low(u32 addr) +{ + if(addr < 0x6800) { + if(ramenable[0] == 0xCA) + return(sram[addr & 0x1FFF]); + } + else if(addr < 0x7000) { + if(ramenable[0] == 0x69) + return(sram[addr & 0x1FFF]); + } + else if(addr < 0x7400) { + if(ramenable[0] == 0x84) + return(sram[addr & 0x1FFF]); + } + return((u8)(addr >> 8)); +} + +static void write_low(u32 addr,u8 data) +{ + if(addr < 0x6800) { + if(ramenable[0] == 0xCA) + sram[addr & 0x1FFF] = data; + } + else if(addr < 0x7000) { + if(ramenable[0] == 0x69) + sram[addr & 0x1FFF] = data; + } + else if(addr < 0x7400) { + if(ramenable[0] == 0x84) + sram[addr & 0x1FFF] = data; + } + else { + switch(addr) { + case 0x7EF0: + case 0x7EF1: + case 0x7EF2: + case 0x7EF3: + case 0x7EF4: + case 0x7EF5: + chr[addr & 7] = data; + break; + case 0x7EF6: + mirror = data & 1; + chrinvert = (data & 2) << 1; + break; + case 0x7EF7: + case 0x7EF8: + case 0x7EF9: + ramenable[addr - 0x7EF7] = data; + break; + case 0x7EFA: + case 0x7EFB: + case 0x7EFC: + prg[addr - 0x7EFA] = data >> 2; + break; + } + sync(); + } +} + +static void reset(int hard) +{ + int i; + + mem_setreadfunc(6,read_low); + mem_setreadfunc(7,read_low); + mem_setwritefunc(6,write_low); + mem_setwritefunc(7,write_low); + prg[0] = prg[1] = prg[2] = 0; + for(i=0;i<6;i++) { + chr[i] = 0; + } + mirror = 0; + mem_setwramsize(8); + mem_setwram8(6,0); + sram = mem_getreadptr(6); + mem_unsetcpu8(6); + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,3); + STATE_ARRAY_U8(chr,6); + STATE_ARRAY_U8(ramenable,3); + STATE_U8(mirror); + STATE_U8(chrinvert); + sync(); +} + +MAPPER(B_TAITO_X1_017,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/tengen/mimic-1.c b/apps/nesemu2/src/mappers/boards/tengen/mimic-1.c new file mode 100644 index 00000000..91337753 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/tengen/mimic-1.c @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/namcot-108.h" + +static void reset(int hard) +{ + namcot108_reset(namcot108_sync,hard); +} + +MAPPER(B_TENGEN_MIMIC_1,reset,0,0,namcot108_state); diff --git a/apps/nesemu2/src/mappers/boards/tengen/rambo-1.c b/apps/nesemu2/src/mappers/boards/tengen/rambo-1.c new file mode 100644 index 00000000..5536122b --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/tengen/rambo-1.c @@ -0,0 +1,183 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 control,mirror; +static u8 irqsource,irqlatch,irqreload,irqenabled,irqcounter,irqcpu,irqwait,needirq; +static u8 prg[3],chr[8]; + +static void sync() +{ + int i; + u8 chrxor = (control & 0x80) >> 5; + + if((control & 0x40) == 0) { + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,prg[2]); + } + else { + mem_setprg8(0xA,prg[0]); + mem_setprg8(0xC,prg[1]); + mem_setprg8(0x8,prg[2]); + } + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) + mem_setchr1(i ^ chrxor,chr[i]); + if((control & 0x20) == 0) { + mem_setchr2(0 ^ chrxor,chr[0] >> 1); + mem_setchr2(2 ^ chrxor,chr[2] >> 1); + } + mem_setmirroring(mirror); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xE001) { + //control register + case 0x8000: + control = data; + break; + case 0x8001: + switch(control & 0xF) { + case 0x0: chr[0] = data; break; + case 0x1: chr[2] = data; break; + case 0x2: chr[4] = data; break; + case 0x3: chr[5] = data; break; + case 0x4: chr[6] = data; break; + case 0x5: chr[7] = data; break; + case 0x6: prg[0] = data; break; + case 0x7: prg[1] = data; break; + case 0x8: chr[1] = data; break; + case 0x9: chr[3] = data; break; + case 0xF: prg[2] = data; break; + } + break; + case 0xA000: + mirror = (data & 1) ^ 1; + break; + case 0xA001: + break; + case 0xC000: + irqlatch = data; + break; + case 0xC001: + irqsource = data & 1; + irqcounter = 0; + irqreload = 1; + break; + case 0xE000: + irqenabled = 0; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xE001: + irqenabled = 1; + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + control = 0; + mirror = 0; + prg[0] = prg[1] = prg[2] = 0xFF; + for(i=0;i<8;i++) + chr[i] = i; + irqsource = 0; + irqlatch = 0; + irqreload = 0; + irqenabled = 0; + irqcounter = 0; + irqcpu = 0; + sync(); +} + +static void clockirq() +{ + u8 tmp = irqcounter; + if((irqcounter == 0) || irqreload) { + irqcounter = irqlatch; + } + else { + irqcounter--; + } + if((tmp || irqreload) && (irqcounter == 0) && irqenabled) { + needirq = 3; + } + irqreload = 0; +} + +static void ppucycle() +{ + if(irqsource == 0) { + if(needirq && (--needirq) == 0) { + cpu_set_irq(IRQ_MAPPER); + } + if(irqwait) { + irqwait--; + } + if((irqwait == 0) && (nes->ppu.busaddr & 0x1000)) { + clockirq(); + } + if(nes->ppu.busaddr & 0x1000) { + irqwait = 8; + } + } +} + +static void cpucycle() +{ + if(irqsource) { + irqcpu--; + if(irqcpu == 0) { + irqcpu = 4; + clockirq(); + if(needirq) { + needirq = 0; + cpu_set_irq(IRQ_MAPPER); + } + } + } +} + +static void state(int mode,u8 *data) +{ + STATE_U8(control); + STATE_ARRAY_U8(prg,3); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(irqsource); + STATE_U8(irqlatch); + STATE_U8(irqreload); + STATE_U8(irqenabled); + STATE_U8(irqcounter); + STATE_U8(irqcpu); + STATE_U8(irqwait); + STATE_U8(needirq); + sync(); +} + +MAPPER(B_TENGEN_RAMBO_1,reset,ppucycle,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/boards/txc/22211.c b/apps/nesemu2/src/mappers/boards/txc/22211.c new file mode 100644 index 00000000..57fff477 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/txc/22211.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static int revision; +static u8 reg[4]; +static readfunc_t read4; +static writefunc_t write4; + +static void sync() +{ + mem_setprg32(8,(reg[2] >> 2) & 3); + if(revision == B_TXC_22211B) + mem_setchr8(0,(((latch_data ^ reg[2]) >> 3) & 2) | (((latch_data ^ reg[2]) >> 5) & 1)); + else + mem_setchr8(0,reg[2] & 3); +} + +static u8 read_reg(u32 addr) +{ + if(addr < 0x4020) + return(read4(addr)); + if(addr == 0x4100) { + u8 o = 0x40; + + if(revision == B_TXC_22211C) + o = 0x41; + return((reg[1] ^ reg[2]) | o); + } + return(0); +} + +static void write_reg(u32 addr,u8 data) +{ + if(addr < 0x4020) + write4(addr,data); + else if(addr >= 0x4100 && addr < 0x4104) + reg[addr & 3] = data; +} + +static void reset(int r,int hard) +{ + revision = r; + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4,read_reg); + mem_setwritefunc(4,write_reg); + reg[0] = reg[1] = reg[2] = reg[3] = 0; + latch_reset(sync,hard); + mem_setprg32(8,0); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,4); + latch_state(mode,data); +} + +static void reset_txc_22211a(int hard) { reset(B_TXC_22211A,hard); } +static void reset_txc_22211b(int hard) { reset(B_TXC_22211B,hard); } +static void reset_txc_22211c(int hard) { reset(B_TXC_22211C,hard); } + +MAPPER(B_TXC_22211A,reset_txc_22211a,0,0,state); +MAPPER(B_TXC_22211B,reset_txc_22211b,0,0,state); +MAPPER(B_TXC_22211C,reset_txc_22211c,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/txc/mxmdhtwo.c b/apps/nesemu2/src/mappers/boards/txc/mxmdhtwo.c new file mode 100644 index 00000000..4d22c8f5 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/txc/mxmdhtwo.c @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg; + +static void sync() +{ + mem_setprg32(8,(reg >> 4) | reg); + if(nes->cart->chr.size == 0) + mem_setvram8(0,0); + else + mem_setchr8(0,0); +} + +static u8 read(u32 addr) +{ + if(addr == 0x5FF0) + return(0x50); + return(0); +} + +static void write(u32 addr,u8 data) +{ +// log_printf("txc-mxmdhtwo: write to $%04X = $%02X\n",addr,data); + reg = data; + sync(); +} + +static void reset(int hard) +{ + int i; + + for(i=8;i<0x10;i++) + mem_setwritefunc(i,write); + mem_setreadfunc(5,read); + mem_setvramsize(8); + mem_setwramsize(8); + mem_setwram8(6,0); + reg = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +MAPPER(B_TXC_MXMDHTWO,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/txc/strikewolf.c b/apps/nesemu2/src/mappers/boards/txc/strikewolf.c new file mode 100644 index 00000000..f1c98e55 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/txc/strikewolf.c @@ -0,0 +1,81 @@ +#include "mappers/mapperinc.h" + +static u8 reg; +static u8 irqenable,irqmode; +static readfunc_t read4; +static writefunc_t write4; + +static void sync() +{ + mem_setprg32(8,reg >> 4); + mem_setchr8(0,reg & 0xF); +} + +static u8 read_reg(u32 addr) +{ + if(addr < 0x4020) + return(read4(addr)); + log_printf("txc-strikewolf.c: read_reg: $%04X\n",addr); + return(0); +} + +static void write_reg(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + switch(addr) { + case 0x4100: + break; + case 0x4101: + break; + case 0x4102: + break; + case 0x4103: + irqenable = 0; + break; + case 0x4104: + irqenable = 1; + break; + } + log_printf("txc-strikewolf.c: write_reg: $%04X = $%02X\n",addr,data); +} + +static void write(u32 addr,u8 data) +{ + if(addr >= 0x8400 && addr < 0xFFFF) + reg = data; + else + log_printf("txc-strikewolf.c: write: $%04X = $%02X\n",addr,data); + sync(); +} + +static void reset(int hard) +{ + int i; + + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4,read_reg); + mem_setwritefunc(4,write_reg); + for(i=8;i<0x10;i++) + mem_setwritefunc(i,write); + reg = 0; + irqenable = 0; + irqmode = 0; + sync(); +} + +static void ppucycle() +{ + +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + sync(); +} + +MAPPER(B_TXC_STRIKEWOLF,reset,ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/txc/tw.c b/apps/nesemu2/src/mappers/boards/txc/tw.c new file mode 100644 index 00000000..0b92e3d4 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/txc/tw.c @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 reg; +static writefunc_t write4; + +static void sync() +{ + mem_setprg32(8,reg); + mmc3_syncchr(0xFF,0); + mmc3_syncmirror(); + mmc3_syncsram(); +} + +static u8 read(u32 addr) +{ +// log_printf("txc-tw: read from $%04X\n",addr); + if(addr == 0x5FF0) + return(0x50); + return(0); +} + +static void write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } +// log_printf("txc-tw: write to $%04X = $%02X\n",addr,data); + reg = (data & 0xF) | (data >> 4); + sync(); +} + +static void reset(int hard) +{ + write4 = mem_getwritefunc(4); + mem_setreadfunc(5,read); + mem_setwritefunc(4,write); + reg = 0; + mmc3_reset(C_MMC3A,sync,hard); +} + +static void state(int mode,u8 *data) +{ + STATE_U8(reg); + mmc3_state(mode,data); +} + +MAPPER(B_TXC_TW,reset,mmc3_ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/waixing/ffv.c b/apps/nesemu2/src/mappers/boards/waixing/ffv.c new file mode 100644 index 00000000..8b8b3a71 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/waixing/ffv.c @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 reg[2],latch; + +static void sync() +{ + switch(reg[0] & 0x70) { + + //swap 16kb at $8000, $C000 is last in the chunk + case 0x00: + case 0x20: + case 0x40: + case 0x60: + mem_setprg16(0x8,(reg[0] & 0xF) | ((reg[1] & 1) << 5) | ((reg[0] & 0x20) >> 1)); + mem_setprg16(0xC,0x1F | ((reg[1] & 1) << 5)); + break; + + //swap 32kb at $8000 + case 0x50: + mem_setprg32(8,(reg[0] & 0xF) | ((reg[1] & 1) << 4)); + break; + + //swap 16kb at $8000 (alternate), $C000 is last in the chunk + case 0x70: + mem_setprg16(0x8,(reg[0] & 0xF) | ((reg[1] & 1) << 5) | ((reg[0] & 0x8) << 1)); + mem_setprg16(0xC,0x1F | ((reg[1] & 1) << 5)); + break; + } + mem_setwram8(6,0); + mem_setvram8(0,0); +} + +static u8 read(u32 addr) +{ + switch(addr & 0x7700) { + case 0x5100: + return(latch); + } + return((u8)(addr >> 8)); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0x7300) { + case 0x5000: + reg[0] = data; + break; + case 0x5100: + reg[1] = data; + break; + case 0x5300: + latch = data; + break; + } + sync(); +} + +static void reset(int hard) +{ + mem_setwramsize(8); + mem_setvramsize(8); + mem_setwritefunc(0x5,write); + mem_setwritefunc(0xD,write); + mem_setreadfunc(0x5,read); + reg[0] = 0x7F; + reg[1] = 0; + sync(); +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,2); + STATE_U8(latch); + sync(); +} + +MAPPER(B_WAIXING_FFV,reset,0,0,state); diff --git a/apps/nesemu2/src/mappers/boards/waixing/sgz.c b/apps/nesemu2/src/mappers/boards/waixing/sgz.c new file mode 100644 index 00000000..d2502602 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/waixing/sgz.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg[2],chr[8],mirror; +static u8 irqenable,irqlatch,irqcounter; + +static void sync() +{ + int i; + + mem_setwram8(6,0); + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg8(0xC,0xFE); + mem_setprg8(0xE,0xFF); + for(i=0;i<8;i++) { + if(chr[i] == 6 || chr[i] == 7) + mem_setvram1(i,chr[i] & 1); + else + mem_setchr1(i,chr[i]); + } +} + +static void write(u32 addr,u8 data) +{ + int index; + + log_printf("waixing: sgz.c: write $%04X = $%02X\n",addr,data); + switch(addr & 0xF000) { + case 0x8000: + prg[0] = data; + break; + case 0xA000: + prg[1] = data; + break; + case 0xB000: + case 0xC000: + case 0xD000: + case 0xE000: + index = (addr & 8) | (addr >> 8); + index >>= 3; + index += 2; + index &= 7; + if(addr & 4) { + chr[index] &= 0x0F; + chr[index] |= (data & 0x0F) << 4; + } + else { + chr[index] &= 0xF0; + chr[index] |= data & 0x0F; + } + break; + case 0xF000: + switch(addr & 0xC) { + case 0x0: + cpu_clear_irq(IRQ_MAPPER); + irqlatch &= 0xF0; + irqlatch |= data & 0xF; + break; + case 0x4: + cpu_clear_irq(IRQ_MAPPER); + irqlatch &= 0x0F; + irqlatch |= (data & 0xF) << 4; + break; + case 0x8: + cpu_clear_irq(IRQ_MAPPER); + irqcounter = irqlatch; + irqenable = data & 2; + break; + case 0xC: + break; + } + break; + } + sync(); +} + +static void reset(int hard) +{ + int i; + + mem_setwramsize(8); + mem_setvramsize(8); + for(i=8;i<16;i++) + mem_setwritefunc(i,write); + if(hard) { + prg[0] = prg[1] = 0; + for(i=0;i<8;i++) + chr[i] = i; + mirror = 0; + irqlatch = irqcounter = 0; + irqenable = 0; + } + sync(); +} + +static void ppucycle() +{ + if(irqenable == 0) + return; + if(LINECYCLES == 257) { + if(irqcounter == 0xFF) { + irqcounter = irqlatch; + cpu_set_irq(IRQ_MAPPER); + } + irqcounter++; + } +} + +static void state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U8(chr,8); + STATE_U8(mirror); + STATE_U8(irqenable); + STATE_U8(irqlatch); + STATE_U8(irqcounter); + sync(); +} + +MAPPER(B_WAIXING_SGZ,reset,ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/waixing/sh2.c b/apps/nesemu2/src/mappers/boards/waixing/sh2.c new file mode 100644 index 00000000..41354a46 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/waixing/sh2.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static u8 latch[2][2],latchstate[2]; +static readfunc_t ppuread; + +static INLINE void syncchr() +{ + u8 bank; + + bank = mmc3_getchrreg(latchstate[0] ? 2 : 0) >> 2; + if(bank == 0) + mem_setvram4(0,0); + else + mem_setchr4(0,bank); + bank = mmc3_getchrreg(latchstate[1] ? 6 : 4) >> 2; + if(bank == 0) + mem_setvram4(4,0); + else + mem_setchr4(4,bank); +} + +static void sync() +{ + mmc3_syncprg(0xFF,0); + syncchr(); + mem_setmirroring(mmc3_getmirror() ^ 1); + mmc3_syncsram(); +} + +static u8 readtile(u32 addr) +{ + switch(addr & 0xFFF8) { + case 0x0FD8: + latchstate[0] = 0; + syncchr(); + break; + case 0x0FE8: + latchstate[0] = 1; + syncchr(); + break; + case 0x1FD8: + latchstate[1] = 0; + syncchr(); + break; + case 0x1FE8: + latchstate[1] = 1; + syncchr(); + break; + } + return(ppuread(addr)); +} + +static void reset(int hard) +{ + mem_setvramsize(4); + latch[0][0] = latch[0][1] = 0; + latch[1][0] = latch[1][1] = 0; + latchstate[0] = latchstate[1] = 0; + ppuread = ppu_getreadfunc(); + ppu_setreadfunc(readtile); + mmc3_reset(C_MMC3B,sync,hard); +} + +static void ppucycle() +{ + mmc3_ppucycle(); +} + +static void state(int mode,u8 *data) +{ + mmc3_state(mode,data); +} + +MAPPER(B_WAIXING_SH2,reset,ppucycle,0,state); diff --git a/apps/nesemu2/src/mappers/boards/waixing/type-d.c b/apps/nesemu2/src/mappers/boards/waixing/type-d.c new file mode 100644 index 00000000..d931906a --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/waixing/type-d.c @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void sync() +{ + int i,bank; + + mmc3_syncprg(0xFF,0x00); + for(i=0;i<8;i++) { + bank = mmc3_getchrbank(i); + if(bank < 2) { + mem_setvram1(i,bank); + log_printf("waixing/type-d.c: sync: mapping vram!\n"); + } + else + mem_setchr1(i,bank); + } + mmc3_syncmirror(); + mmc3_syncsram(); +} + +static void reset(int hard) +{ + mem_setvramsize(2); + mmc3_reset(C_MMC3B,sync,hard); +} + +MAPPER(B_WAIXING_TYPE_D,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/waixing/type-h.c b/apps/nesemu2/src/mappers/boards/waixing/type-h.c new file mode 100644 index 00000000..cf454b54 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/waixing/type-h.c @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static void sync() +{ + mmc3_syncprg(0x3F,(mmc3_getchrreg(0) & 2) << 5); + if(nes->cart->chr.size == 0) { + mem_setvram4(0,((mmc3_getcommand() >> 7) & 1) ^ 0); + mem_setvram4(4,((mmc3_getcommand() >> 7) & 1) ^ 1); + } + else + mmc3_syncchr(0xFF,0); + mmc3_syncmirror(); + mmc3_syncsram(); +} + +static void reset(int hard) +{ + mmc3_reset(C_MMC3B,sync,hard); +} + +MAPPER(B_WAIXING_TYPE_H,reset,mmc3_ppucycle,0,mmc3_state); diff --git a/apps/nesemu2/src/mappers/boards/waixing/zs.c b/apps/nesemu2/src/mappers/boards/waixing/zs.c new file mode 100644 index 00000000..b81d6639 --- /dev/null +++ b/apps/nesemu2/src/mappers/boards/waixing/zs.c @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void sync() +{ + mem_setprg32(8,(latch_addr >> 3) & 0xF); + mem_setvram8(0,0); + mem_setwram8(6,0); + mem_setmirroring(((latch_addr >> 1) & 1) ^ 1); +} + +static void reset(int hard) +{ + mem_setvramsize(8); + mem_setwramsize(8); + latch_reset(sync,hard); +} + +MAPPER(B_WAIXING_ZS,reset,0,0,latch_state); diff --git a/apps/nesemu2/src/mappers/chips/latch.c b/apps/nesemu2/src/mappers/chips/latch.c new file mode 100644 index 00000000..9fe3d6b7 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/latch.c @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/latch.h" + +static void (*sync)(); +u8 latch_data; +u32 latch_addr; + +void latch_write(u32 addr,u8 data) +{ +// if (nes->cpu.readpages[addr >> 10][addr & 0x3FF] != data) + log_printf("latch_write: $%04X = $%02X (PC = $%04X)\n", addr, data, nes->cpu.pc); + latch_addr = addr; + latch_data = data; + sync(); +} + +void latch_reset(void (*s)(),int hard) +{ + int i; + + sync = s; + for(i=8;i<16;i++) + mem_setwritefunc(i,latch_write); + if(hard) { + latch_data = 0; + latch_addr = 0; + } + sync(); +} + +void latch_state(int mode,u8 *data) +{ + STATE_U8(latch_data); + STATE_U16(latch_addr); + sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/latch.h b/apps/nesemu2/src/mappers/chips/latch.h new file mode 100644 index 00000000..4faac9fe --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/latch.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __latch_h__ +#define __latch_h__ + +extern u8 latch_data; +extern u32 latch_addr; + +void latch_write(u32 addr,u8 data); +void latch_reset(void (*sync)(),int hard); +void latch_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/mmc1.c b/apps/nesemu2/src/mappers/chips/mmc1.c new file mode 100644 index 00000000..763a68a6 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc1.c @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* + differences in mmc1 chip types (from the wiki): + MMC1A: PRG RAM is always enabled. + MMC1B: PRG RAM is enabled by default. + MMC1C: PRG RAM is disabled by default. +*/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc1.h" + +static void (*sync)() = 0; +static u8 regs[4]; +static u8 latch,latchpos; +static u8 lastreg; +static int type; + +void mmc1_sync() +{ + mmc1_syncmirror(); + if(nes->cart->prg.size > 0x40000) { + mem_setprg16(0x8,(mmc1_getlowprg() & 0xf) | (mmc1_getlowchr() & 0x10)); + mem_setprg16(0xC,(mmc1_gethighprg() & 0xf) | (mmc1_getlowchr() & 0x10)); + } + else + mmc1_syncprg(0xF,0); + if(nes->cart->chr.size) + mmc1_syncchr(0x1F,0); + else + mmc1_syncvram(1,0); + mmc1_syncsram(); +} + +int mmc1_getlowprg() +{ + if(regs[0] & 8) { + if(regs[0] & 4) + return(regs[3] & 0xF); + else + return(0); + } + else + return(regs[3] & 0xE); +} + +int mmc1_gethighprg() +{ + if(regs[0] & 8) { + if(regs[0] & 4) + return(0xF); + else + return(regs[3] & 0xF); + } + else + return((regs[3] & 0xE) | 1); +} + +int mmc1_getlowchr() +{ + if(regs[0] & 0x10) + return(regs[1] & 0x1F); + else + return(regs[1] & 0x1E); +} + +int mmc1_gethighchr() +{ + if(regs[0] & 0x10) + return(regs[2] & 0x1F); + else + return((regs[1] & 0x1E) | 1); +} + +void mmc1_reset(int t,void (*s)(),int hard) +{ + int i; + + type = t; + for(i=8;i<0x10;i++) + mem_setwritefunc(i,mmc1_write); + sync = s; + if(hard) { + regs[0] = 0x0C; + regs[1] = regs[2] = regs[3] = 0x00; + if(type == C_MMC1C) + regs[3] = 0x10; + latch = 0; + latchpos = 0; + lastreg = 0; + } + sync(); +} + +void mmc1_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(regs,4); + STATE_U8(latch); + STATE_U8(latchpos); + STATE_U8(lastreg); + sync(); +} + +void mmc1_write(u32 addr,u8 data) +{ + u8 t = (u8)((addr >> 12) >> 1) & 3; + + if(data & 0x80) { + latch = latchpos = 0; + regs[0] |= 0xC; + return; + } + if(t != lastreg) + latch = latchpos = 0; + lastreg = t; + latch |= (data & 1) << latchpos++; + if(latchpos == 5) { + regs[t] = latch & 0x1F; + latch = latchpos = 0; + sync(); + } +} + +void mmc1_syncmirror() +{ + switch(regs[0] & 3) { + case 0:mem_setmirroring(MIRROR_1L);break; + case 1:mem_setmirroring(MIRROR_1H);break; + case 2:mem_setmirroring(MIRROR_V);break; + case 3:mem_setmirroring(MIRROR_H);break; + } +} + +void mmc1_syncprg(int aand,int oor) +{ + mem_setprg16(0x8,(mmc1_getlowprg() & aand) | oor); + mem_setprg16(0xC,(mmc1_gethighprg() & aand) | oor); +} + +void mmc1_syncchr(int aand,int oor) +{ + mem_setchr4(0,(mmc1_getlowchr() & aand) | oor); + mem_setchr4(4,(mmc1_gethighchr() & aand) | oor); +} + +void mmc1_syncvram(int aand,int oor) +{ + mem_setvram4(0,(mmc1_getlowchr() & aand) | oor); + mem_setvram4(4,(mmc1_gethighchr() & aand) | oor); +} + +void mmc1_syncsram() +{ + if(nes->cart->wram.size == 16 * 1024) { + mem_setwram8(6,(mmc1_getlowchr() >> 3) & 1); + return; + } + else if(nes->cart->wram.size == 32 * 1024) { + mem_setwram8(6,(mmc1_getlowchr() >> 2) & 3); + return; + } + + if(type == C_MMC1A) { + mem_setwram8(6,0); + } + else { + if(regs[3] & 0x10) + mem_unsetcpu8(6); + else + mem_setwram8(6,0); + } +} diff --git a/apps/nesemu2/src/mappers/chips/mmc1.h b/apps/nesemu2/src/mappers/chips/mmc1.h new file mode 100644 index 00000000..199a7061 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc1.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mmc1_h__ +#define __mmc1_h__ + +#include "types.h" + +#define C_MMC1 0x10 + +#define C_MMC1A (0 | C_MMC1) +#define C_MMC1B (1 | C_MMC1) +#define C_MMC1C (2 | C_MMC1) + +void mmc1_sync(); +void mmc1_reset(int type,void (*s)(),int hard); +void mmc1_state(int mode,u8 *spot); +void mmc1_write(u32 addr,u8 value); +void mmc1_syncmirror(); +void mmc1_syncprg(int aand,int oor); +void mmc1_syncchr(int aand,int oor); +void mmc1_syncvram(int aand,int oor); +void mmc1_syncsram(); +int mmc1_getlowprg(); +int mmc1_gethighprg(); +int mmc1_getlowchr(); +int mmc1_gethighchr(); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/mmc2.c b/apps/nesemu2/src/mappers/chips/mmc2.c new file mode 100644 index 00000000..0d61cf10 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc2.c @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc2.h" +#include "nes/memory.h" + +static u8 prg,mirroring; +static u8 latch[2][2],latchstate[2]; +static readfunc_t ppuread; + +static u8 readtile(u32 addr) +{ + switch(addr & 0xFFF8) { + case 0x0FD8: + latchstate[0] = 0; + mem_setchr4(0,latch[0][0]); + break; + case 0x0FE8: + latchstate[0] = 1; + mem_setchr4(0,latch[0][1]); + break; + case 0x1FD8: + latchstate[1] = 0; + mem_setchr4(4,latch[1][0]); + break; + case 0x1FE8: + latchstate[1] = 1; + mem_setchr4(4,latch[1][1]); + break; + } + return(ppuread(addr)); +} + +void mmc2_sync() +{ + mem_setprg8(0x8,prg); + mem_setprg8(0xA,0xD); + mem_setprg8(0xC,0xE); + mem_setprg8(0xE,0xF); + mem_setchr4(0,latch[0][latchstate[0]]); + mem_setchr4(4,latch[1][latchstate[1]]); + mem_setmirroring(mirroring); +} + +void mmc2_reset(int hard) +{ + int i; + + for(i=8;i<0x10;i++) + mem_setwritefunc(i,mmc2_write); + latch[0][0] = latch[0][1] = 0; + latch[1][0] = latch[1][1] = 0; + latchstate[0] = latchstate[1] = 0; + prg = 0; + mirroring = 0; + ppuread = ppu_getreadfunc(); + ppu_setreadfunc(readtile); + mmc2_sync(); +} + +void mmc2_write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0xA000: + prg = data & 0xF; + break; + case 0xB000: + latch[0][0] = data & 0x1F; + break; + case 0xC000: + latch[0][1] = data & 0x1F; + break; + case 0xD000: + latch[1][0] = data & 0x1F; + break; + case 0xE000: + latch[1][1] = data & 0x1F; + break; + case 0xF000: + mirroring = (data & 1) ^ 1; + break; + } + mmc2_sync(); +} + +void mmc2_state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(mirroring); + STATE_ARRAY_U8(latch[0],2); + STATE_ARRAY_U8(latch[1],2); + STATE_U8(latchstate[0]); + STATE_U8(latchstate[1]); + mmc2_sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/mmc2.h b/apps/nesemu2/src/mappers/chips/mmc2.h new file mode 100644 index 00000000..231249a3 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc2.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mmc2_h__ +#define __mmc2_h__ + +#include "types.h" + +#define C_MMC2 0x20 +#define C_MMC4 0x40 + +void mmc2_sync(); +void mmc2_reset(int hard); +void mmc2_write(u32 addr,u8 value); +void mmc2_tile(int tile); +void mmc2_state(int mode,u8 *spot); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/mmc3.c b/apps/nesemu2/src/mappers/chips/mmc3.c new file mode 100644 index 00000000..03e8ab31 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc3.c @@ -0,0 +1,296 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc3.h" + +static int type; +static void (*sync)() = 0; +static u8 command; +static u8 prg[4],chr[8]; +static u8 mirror; +static u8 sramenabled; +static u8 irqlatch,irqcounter,irqenabled,irqreload,irqwait; +static u8 *sram7; + +void mmc3_sync() +{ + mmc3_syncprg(0xFF,0); + if(nes->cart->chr.size) + mmc3_syncchr(0xFF,0); + else + mmc3_syncvram(7,0); + if(nes->cart->mirroring == MIRROR_4) + mem_setmirroring(MIRROR_4); + else + mmc3_syncmirror(); + mmc3_syncsram(); +} + +u8 mmc3_getprgbank(int n) +{ + if(n & 1) + return(prg[n]); + return(prg[n ^ ((command & 0x40) >> 5)]); +} + +u8 mmc3_getchrbank(int n) +{ + return(chr[n ^ ((command & 0x80) >> 5)]); +} + +u8 mmc3_getchrreg(int n) +{ + return(chr[n & 7]); +} + +u8 mmc3_getcommand() +{ + return(command); +} + +u8 mmc3_getmirror() +{ + return(mirror); +} + +u8 mmc3_getsramenabled() +{ + return(sramenabled); +} + +void mmc3_syncprg(int a,int o) +{ + mem_setprg8(0x8,(mmc3_getprgbank(0) & a) | o); + mem_setprg8(0xA,(mmc3_getprgbank(1) & a) | o); + mem_setprg8(0xC,(mmc3_getprgbank(2) & a) | o); + mem_setprg8(0xE,(mmc3_getprgbank(3) & a) | o); +} + +void mmc3_syncchr(int a,int o) +{ + int i; + + for(i=0;i<8;i++) + mem_setchr1(i,(mmc3_getchrbank(i) & a) | o); +} + +void mmc3_syncvram(int a,int o) +{ + int i; + + for(i=0;i<8;i++) + mem_setvram1(i,(mmc3_getchrbank(i) & a) | o); +} + +void mmc3_syncsram() +{ +/* if((type & C_MMCNUM) == C_MMC3) { + if((sramenabled & 0xC0) == 0x80) + mem_setwram8(6,0); + else + mem_unsetcpu8(6); + }*/ + if((type & C_MMCNUM) == C_MMC3) + mem_setwram8(6,0); +} + +void mmc3_syncmirror() +{ + mem_setmirroring(mirror); +} + +u8 mmc6_readsram(u32 addr) +{ + if(command & 0x20) { + if(addr >= 0x7000 && addr < 0x7200 && sramenabled & 0x20) { + return(sram7[addr & 0x3FF]); + } + if(addr >= 0x7200 && addr < 0x7400 && sramenabled & 0x80) { + return(sram7[addr & 0x3FF]); + } + } + return(0); +} + +void mmc6_writesram(u32 addr,u8 data) +{ + if(command & 0x20) { + if(addr >= 0x7000 && addr < 0x7200 && sramenabled & 0x10) { + sram7[addr & 0x3FF] = data; + } + if(addr >= 0x7200 && addr < 0x7400 && sramenabled & 0x40) { + sram7[addr & 0x3FF] = data; + } + } +} + +void mmc3_reset(int t,void (*s)(),int hard) +{ + int i; + + type = t; + mem_setwramsize(8); + if(nes->cart->chr.size == 0) + mem_setvramsize(8); + for(i=8;i<0x10;i++) + mem_setwritefunc(i,mmc3_write); + if((type & C_MMCNUM) == C_MMC6) { + mem_setwram4(7,0); + sram7 = mem_getwriteptr(7); + mem_unsetcpu4(7); + mem_setreadfunc(6,mmc6_readsram); + mem_setreadfunc(7,mmc6_readsram); + mem_setwritefunc(6,mmc6_writesram); + mem_setwritefunc(7,mmc6_writesram); + sramenabled = 0; + } + else { +// mem_setwram8(6,0); + //keep enabled for now + sramenabled = 0x80; + } + sync = s; + command = 0; + for(i=0;i<4;i++) + prg[i] = 0x3C + i; + for(i=0;i<8;i++) + chr[i] = i; + mirror = 0; + irqcounter = irqlatch = 0; + irqenabled = irqreload = 0; + irqwait = 0; + sync(); +} + +void mmc3_write(u32 addr,u8 data) +{ + switch(addr & 0xE001) { + case 0x8000: + command = data; + if((data & 0x20) == 0) + sramenabled = 0; + sync(); + break; + case 0x8001: + switch(command & 7) { + case 0: + data &= 0xFE; + chr[0] = data | 0; + chr[1] = data | 1; + break; + case 1: + data &= 0xFE; + chr[2] = data | 0; + chr[3] = data | 1; + break; + case 2: + chr[4] = data; + break; + case 3: + chr[5] = data; + break; + case 4: + chr[6] = data; + break; + case 5: + chr[7] = data; + break; + case 6: + prg[0] = data; + break; + case 7: + prg[1] = data; + break; + } + sync(); + break; + case 0xA000: + mirror = (data & 1) ^ 1; + sync(); + break; + case 0xA001: + if(type == C_MMC6) { + if(command & 0x20) { + sramenabled = data & 0xF0; + sync(); + } + } + else { + sramenabled = data & 0xC0; + sync(); + } + break; + case 0xC000: + irqlatch = data; + break; + case 0xC001: + irqcounter = 0; + irqreload = 1; + break; + case 0xE000: + irqenabled = 0; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0xE001: + irqenabled = 1; + break; + } +} + +void mmc3_ppucycle() +{ + u8 tmp; + + if(irqwait) { + irqwait--; + } + if((irqwait == 0) && (nes->ppu.busaddr & 0x1000)) { + tmp = irqcounter; + if((irqcounter == 0) || irqreload) { + irqcounter = irqlatch; + } + else { + irqcounter--; + } + if((tmp || irqreload) && (irqcounter == 0) && irqenabled) { + cpu_set_irq(IRQ_MAPPER); + } + irqreload = 0; + } + if(nes->ppu.busaddr & 0x1000) { + irqwait = 8; + } +} + +void mmc3_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U8(chr,8); + STATE_U8(command); + STATE_U8(mirror); + STATE_U8(sramenabled); + STATE_U8(irqlatch); + STATE_U8(irqcounter); + STATE_U8(irqenabled); + STATE_U8(irqreload); + STATE_U8(irqwait); + sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/mmc3.h b/apps/nesemu2/src/mappers/chips/mmc3.h new file mode 100644 index 00000000..12d65cae --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc3.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mmc3_h__ +#define __mmc3_h__ + +#include "types.h" + +#define C_MMC3 0x30 +#define C_MMC6 0x60 + +#define C_MMC3A (0 | C_MMC3) +#define C_MMC3B (1 | C_MMC3) +#define C_MMC3C (2 | C_MMC3) +#define C_MMC6B (0 | C_MMC6) + +void mmc3_sync(); +u8 mmc3_getprgbank(int n); +u8 mmc3_getchrbank(int n); +u8 mmc3_getchrreg(int n); +u8 mmc3_getcommand(); +u8 mmc3_getmirror(); +u8 mmc3_getsramenabled(); +void mmc3_syncprg(int a,int o); +void mmc3_syncchr(int a,int o); +void mmc3_syncvram(int a,int o); +void mmc3_syncsram(); +void mmc3_syncmirror(); +void mmc3_write(u32 addr,u8 data); +void mmc3_reset(int t,void (*s)(),int hard); +void mmc3_ppucycle(); +void mmc3_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/mmc4.c b/apps/nesemu2/src/mappers/chips/mmc4.c new file mode 100644 index 00000000..3579dd6c --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc4.c @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc4.h" +#include "nes/memory.h" + +static u8 prg,mirroring; +static u8 latch[2][2],latchstate[2]; +static readfunc_t ppuread; + +static u8 readtile(u32 addr) +{ + switch(addr & 0xFFF8) { + case 0x0FD8: + latchstate[0] = 0; + mem_setchr4(0,latch[0][0]); + break; + case 0x0FE8: + latchstate[0] = 1; + mem_setchr4(0,latch[0][1]); + break; + case 0x1FD8: + latchstate[1] = 0; + mem_setchr4(4,latch[1][0]); + break; + case 0x1FE8: + latchstate[1] = 1; + mem_setchr4(4,latch[1][1]); + break; + } + return(ppuread(addr)); +} + +void mmc4_sync() +{ + mem_setwram8(0x6,0); + mem_setprg16(0x8,prg); + mem_setprg16(0xC,0xF); + mem_setchr4(0,latch[0][latchstate[0]]); + mem_setchr4(4,latch[1][latchstate[1]]); + mem_setmirroring(mirroring); +} + +void mmc4_init(int hard) +{ + int i; + + for(i=8;i<0x10;i++) + mem_setwritefunc(i,mmc4_write); + mem_setwramsize(8); + latch[0][0] = latch[0][1] = 0; + latch[1][0] = latch[1][1] = 0; + latchstate[0] = latchstate[1] = 0; + prg = 0; + mirroring = 0; + ppuread = ppu_getreadfunc(); + ppu_setreadfunc(readtile); + mmc4_sync(); +} + +void mmc4_write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0xA000: + prg = data & 0xF; + break; + case 0xB000: + latch[0][0] = data & 0x1F; + break; + case 0xC000: + latch[0][1] = data & 0x1F; + break; + case 0xD000: + latch[1][0] = data & 0x1F; + break; + case 0xE000: + latch[1][1] = data & 0x1F; + break; + case 0xF000: + mirroring = (data & 1) ^ 1; + break; + } + mmc4_sync(); +} + +void mmc4_state(int mode,u8 *data) +{ + STATE_U8(prg); + STATE_U8(mirroring); + STATE_ARRAY_U8(latch[0],2); + STATE_ARRAY_U8(latch[1],2); + STATE_U8(latchstate[0]); + STATE_U8(latchstate[1]); + mmc4_sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/mmc4.h b/apps/nesemu2/src/mappers/chips/mmc4.h new file mode 100644 index 00000000..5f5f366a --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc4.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mmc4_h__ +#define __mmc4_h__ + +#include "types.h" + +void mmc4_init(int hard); +void mmc4_write(u32 addr,u8 value); +void mmc4_tile(int tile); +void mmc4_state(int mode,u8 *spot); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/mmc5.c b/apps/nesemu2/src/mappers/chips/mmc5.c new file mode 100644 index 00000000..81d1da1f --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc5.c @@ -0,0 +1,454 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/mmc5.h" +#include "mappers/sound/s_MMC5.h" + +static apu_external_t mmc5sound = { + MMC5sound_Load, + MMC5sound_Unload, + MMC5sound_Reset, + MMC5sound_Get, + 0 +}; + +static u8 prg[4],chrhi,prgram,mirror; +static u16 chra[8],chrb[4]; +static u8 prgmode,chrmode,exrammode,chrselect; +static u8 prgprotect[2]; +static u8 multiply[2]; +static u8 filltile,fillattrib; +static u8 irqtarget,irqenable,irqstatus,irqcounter; +static u8 *exram; +static void (*sync)(); +static readfunc_t ppuread; +static u8 splitmode, splitscroll, splitbank; + +u8 mmc5_ppuread(u32 addr) +{ + return(ppuread(addr)); +} + +u8 mmc5_ppureadfill(u32 addr) +{ +// log_printf("mmc5: reading nt fill data!\n"); + addr &= 0x3FF; + if(addr < 0x3C0) + return(filltile); + return(fillattrib); +} + +void mmc5_setprg(int size,int bank,int page) +{ + //rom mapped here + if(page & 0x80) { + page &= 0x7F; + if(size == 32) { + mem_setprg32(bank,page >> 2); + } + else if(size == 16) { + mem_setprg16(bank,page >> 1); + } + else { //8kb + mem_setprg8(bank,page); + } + } + else { +// log_printf("mmc5: mapping %dkb prg ram sram to %04X\n",size,bank*0x1000); + if(size == 16) { + mem_setwram16(bank,page); + } + else if(size == 8) { + mem_setwram8(bank,page); + } + else + log_printf("mmc5: mapping %dkb prg ram? no!\n",size); + } +} + +void mmc5_setmirror(int bank,int data) +{ + bank += 8; + switch(data) { + case 0: + case 1: + mem_setnt1(bank,data); + mem_setppureadfunc(bank,0); + break; + case 2: + //map in exram as nametable data + if(exrammode < 2) { +// log_printf("mmc5: exram mapped into ppu bank %d!\n",bank); + mem_setppureadptr(bank,exram); + mem_setppuwriteptr(bank,exram); + } + //map in disabled exram + else { +// log_printf("mmc5: disabled exram mapped into ppu bank %d!\n",bank); + mem_setppureadptr(bank,exram + 0xC00); + mem_setppuwriteptr(bank,exram + 0xC00); + } + mem_setppureadfunc(bank,0); + break; + case 3: + mem_unsetppu1(bank); + mem_setppureadfunc(bank,mmc5_ppureadfill); + break; + } +} + +void mmc5_syncprg() +{ + mem_setwram8(6,prgram); + switch(prgmode) { + case 0: + mmc5_setprg(32,0x8,prg[3] | 0x80); + break; + case 1: + mmc5_setprg(16,0x8,prg[1]); + mmc5_setprg(16,0xC,prg[3] | 0x80); + break; + case 2: + mmc5_setprg(16,0x8,prg[1]); + mmc5_setprg(8,0xC,prg[2]); + mmc5_setprg(8,0xE,prg[3] | 0x80); + break; + case 3: + mmc5_setprg(8,0x8,prg[0]); + mmc5_setprg(8,0xA,prg[1]); + mmc5_setprg(8,0xC,prg[2]); + mmc5_setprg(8,0xE,prg[3] | 0x80); + break; + } +} + +void mmc5_syncchra() +{ + switch(chrmode) { + case 0: + mem_setchr8(0,chra[7]); + break; + case 1: + mem_setchr4(0,chra[3]); + mem_setchr4(4,chra[7]); + break; + case 2: + mem_setchr2(0,chra[1]); + mem_setchr2(2,chra[3]); + mem_setchr2(4,chra[5]); + mem_setchr2(6,chra[7]); + break; + case 3: + mem_setchr1(0,chra[0]); + mem_setchr1(1,chra[1]); + mem_setchr1(2,chra[2]); + mem_setchr1(3,chra[3]); + mem_setchr1(4,chra[4]); + mem_setchr1(5,chra[5]); + mem_setchr1(6,chra[6]); + mem_setchr1(7,chra[7]); + break; + } +} + +void mmc5_syncchrb() +{ + switch(chrmode) { + case 0: + mem_setchr8(0,chrb[3]); + break; + case 1: + mem_setchr4(0,chrb[3]); + mem_setchr4(4,chrb[3]); + break; + case 2: + mem_setchr2(0,chrb[1]); + mem_setchr2(2,chrb[3]); + mem_setchr2(4,chrb[1]); + mem_setchr2(6,chrb[3]); + break; + case 3: + mem_setchr1(0,chrb[0]); + mem_setchr1(1,chrb[1]); + mem_setchr1(2,chrb[2]); + mem_setchr1(3,chrb[3]); + mem_setchr1(4,chrb[0]); + mem_setchr1(5,chrb[1]); + mem_setchr1(6,chrb[2]); + mem_setchr1(7,chrb[3]); + break; + } +} + +void mmc5_syncchr() +{ + if(chrselect == 0) + mmc5_syncchra(); + else + mmc5_syncchrb(); +} + +void mmc5_syncmirror() +{ + mmc5_setmirror(0,mirror & 3); + mmc5_setmirror(1,(mirror >> 2) & 3); + mmc5_setmirror(2,(mirror >> 4) & 3); + mmc5_setmirror(3,(mirror >> 6) & 3); +} + +void mmc5_sync() +{ + mmc5_syncprg(); + mmc5_syncchr(); + mmc5_syncmirror(); +} + +u8 mmc5_read(u32 addr) +{ + u8 ret = 0xFF; + + if(addr >= 0x5C00) { + if(exrammode >= 2) + return(exram[addr & 0x3FF]); + return(0xFF); + } + + switch(addr) { + //sound + case 0x5015: + return(MMC5sound_Read(addr)); + + //irq status + case 0x5204: + //return value + ret = irqstatus; + + //acknowledge the irq + irqstatus &= ~0x80; + cpu_clear_irq(IRQ_MAPPER); + return(ret); + + //low 8 bits of product + case 0x5205: + return((u8)(multiply[0] * multiply[1])); + + //high 8 bits of product + case 0x5206: + return((u8)((multiply[0] * multiply[1]) >> 8)); + + default: + log_printf("mmc5: unhandled read $%04X\n",addr); + break; + } + return(0xFF); +} + +void mmc5_write(u32 addr,u8 data) +{ + if(addr >= 0x5C00) { + if(exrammode == 2) + exram[addr & 0x3FF] = data; + return; + } + + switch(addr) { + //sound registers + case 0x5000: + case 0x5001: + case 0x5002: + case 0x5003: + case 0x5004: + case 0x5005: + case 0x5006: + case 0x5007: + case 0x5010: + case 0x5011: + case 0x5015: MMC5sound_Write(addr,data); break; + case 0x5100: prgmode = data & 3; break; + case 0x5101: chrmode = data & 3; break; + case 0x5102: prgprotect[0] = data & 3; break; + case 0x5103: prgprotect[1] = data & 3; break; + case 0x5104: exrammode = data & 3; break; + case 0x5105: mirror = data; break; + case 0x5106: filltile = data & 3; break; + case 0x5107: fillattrib = data & 3; break; + case 0x5113: prgram = data & 7; break; + case 0x5114: + case 0x5115: + case 0x5116: + case 0x5117: prg[addr & 3] = data; mmc5_syncprg(); break; + case 0x5120: + case 0x5121: + case 0x5122: + case 0x5123: + case 0x5124: + case 0x5125: + case 0x5126: + case 0x5127: + chra[addr & 7] = data | (chrhi << 8); + chrselect = 0; + mmc5_syncchr(); + break; + case 0x5128: + case 0x5129: + case 0x512A: + case 0x512B: + chrb[addr & 3] = data | (chrhi << 8); + chrselect = 1; + mmc5_syncchr(); + break; + case 0x5130: chrhi = data & 3; break; + case 0x5200: splitmode = data; break; + case 0x5201: splitscroll = data; break; + case 0x5202: splitbank = data; break; + case 0x5203: irqtarget = data; break; + case 0x5204: irqenable = data; break; + case 0x5205: multiply[0] = data; break; + case 0x5206: multiply[1] = data; break; + + default: + log_printf("mmc5: unhandled write $%04X = $%02X\n",addr,data); + break; + } + sync(); +} + +void mmc5_reset(int hard) +{ + int i; + + //setup read/write funcs + mem_setreadfunc(5,mmc5_read); + mem_setwritefunc(5,mmc5_write); + + //setup maximum amount of wram + extra for exram + mem_setwramsize(64 + 4); + + //map in wram and get pointer to data (for exram) + exram = nes->cart->wram.data + 0x10000; + + //hijack the ppu memory read function + ppuread = ppu_getreadfunc(); + ppu_setreadfunc(mmc5_ppuread); + + //zero out exram (for disabled exram) + for(i=0;i<4096;i++) + exram[i] = 0; + + //clear the registers + for(i=0;i<8;i++) { + prg[i & 3] = 0; + chra[i] = chrb[i & 3] = 0; + } + + //setup the registers + prg[3] = 0xFF; + chrhi = 0; + prgram = 0; + prgmode = 3; + chrmode = exrammode = 0; + irqenable = 0; + irqtarget = 0; + irqstatus = 0; + irqcounter = 0; + sync = mmc5_sync; + apu_setexternal(&mmc5sound); + sync(); +} + +static void scanline_detected() +{ + //see if 'in frame' flag is set + if(irqstatus & 0x40) { + irqcounter++; + if(irqcounter == irqtarget && irqenable) { + irqstatus |= 0x80; + cpu_set_irq(IRQ_MAPPER); + } + } + + else { + //set the in frame flag + irqstatus |= 0x40; + + //clear irq pending flag + irqstatus &= ~0x80; + cpu_clear_irq(IRQ_MAPPER); + + //reset counter + irqcounter = 0; + } +// log_printf("mmc5: irqcounter incremented to %d on line %d\n",irqcounter,SCANLINE); +} + +void mmc5_ppucycle() +{ + //visible scanlines + if(SCANLINE < 240) { + + //see if rendering is enabled + if(CONTROL1 & 0x18) { + + //detect scanlines + if(LINECYCLES == 337) { + scanline_detected(); + } + + //if 8x16 sprites + if(CONTROL0 & 0x20) { + //detect when to switch between sprite and bg tiles + if(LINECYCLES == 257) { + mmc5_syncchra(); + chrselect = 0; + } + if(LINECYCLES == 321) { + mmc5_syncchrb(); + chrselect = 1; + } + } + + } + } + else { + //clear in frame flag + irqstatus &= ~0x40; + } +} + +void mmc5_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,4); + STATE_ARRAY_U16(chra,8); + STATE_ARRAY_U16(chrb,4); + STATE_U8(chrhi); + STATE_U8(prgram); + STATE_U8(mirror); + STATE_U8(prgmode); + STATE_U8(chrmode); + STATE_U8(exrammode); + STATE_ARRAY_U8(multiply,2); + STATE_U8(filltile); + STATE_U8(fillattrib); + STATE_U8(irqtarget); + STATE_U8(irqcounter); + STATE_U8(irqenable); + MMC5sound_SaveLoad(mode,0,data); +} diff --git a/apps/nesemu2/src/mappers/chips/mmc5.h b/apps/nesemu2/src/mappers/chips/mmc5.h new file mode 100644 index 00000000..3be0b38d --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/mmc5.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mmc5_h__ +#define __mmc5_h__ + +#include "types.h" + +void mmc5_sync(); +u8 mmc5_read(u32 addr); +void mmc5_write(u32 addr,u8 data); +void mmc5_reset(int hard); +void mmc5_ppucycle(); +void mmc5_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/namcot-108.c b/apps/nesemu2/src/mappers/chips/namcot-108.c new file mode 100644 index 00000000..a87eb2e8 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/namcot-108.c @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static void (*sync)(); +static u8 reg[8],index; + +u8 namcot108_getindex() +{ + return(index); +} + +u8 *namcot108_getregs() +{ + return(reg); +} + +void namcot108_sync() +{ + mem_setchr2(0,reg[0] >> 1); + mem_setchr2(2,reg[1] >> 1); + mem_setchr1(4,reg[2]); + mem_setchr1(5,reg[3]); + mem_setchr1(6,reg[4]); + mem_setchr1(7,reg[5]); + mem_setprg8(0x8,reg[6]); + mem_setprg8(0xA,reg[7]); + mem_setprg16(0xC,0xFF); +} + +void namcot108_write(u32 addr,u8 data) +{ + switch(addr) { + case 0x8000: + index = data; + break; + case 0x8001: + reg[index & 7] = data; + sync(); + break; + } +} + +void namcot108_reset(void (*syncfunc)(),int hard) +{ + int i; + + for(i=8;i<16;i++) + mem_setwritefunc(i,namcot108_write); + for(i=0;i<8;i++) + reg[i] = 0; + index = 0; + sync = syncfunc; + sync(); +} + +void namcot108_state(int mode,u8 *data) +{ + STATE_U8(index); + STATE_ARRAY_U8(reg,8); + sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/namcot-108.h b/apps/nesemu2/src/mappers/chips/namcot-108.h new file mode 100644 index 00000000..87c27d6f --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/namcot-108.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __namcot_108_h__ +#define __namcot_108_h__ + +#include "types.h" + +u8 namcot108_getindex(); +u8 *namcot108_getregs(); +void namcot108_sync(); +void namcot108_reset(void (*syncfunc)(),int hard); +void namcot108_write(u32 addr,u8 data); +void namcot108_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/namcot-163.c b/apps/nesemu2/src/mappers/chips/namcot-163.c new file mode 100644 index 00000000..4f962d74 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/namcot-163.c @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/sound/s_N106.h" + +static apu_external_t sound = { + N106sound_Load, + N106sound_Unload, + N106sound_Reset, + N106sound_Get, + 0 +}; + +static readfunc_t read4; +static writefunc_t write4; +static void (*sync)(); +static u8 prg[4],chr[8],nt[4]; +static u8 protect; +static u16 irqcounter; + +void namcot163_sync() +{ + int i; + + mem_setwram8(6,0); + for(i=0;i<4;i++) { + mem_setprg8(8 + (i * 2),prg[i] & 0x3F); + } + for(i=0;i<4;i++) { + if(chr[i] >= 0xE0 && (prg[1] & 0x40) == 0) + mem_setvram1(i,chr[i] - 0xE0); + else + mem_setchr1(i,chr[i]); + if(chr[i+4] >= 0xE0 && (prg[1] & 0x80) == 0) + mem_setvram1(i+4,chr[i+4] - 0xE0); + else + mem_setchr1(i+4,chr[i+4]); + } + for(i=0;i<4;i++) { + if(nt[i] >= 0xE0) { + mem_setnt1(i + 0x8,nt[i] & 1); + mem_setnt1(i + 0xC,nt[i] & 1); + } + else { + mem_setchr1(i + 0x8,nt[i]); + mem_setchr1(i + 0xC,nt[i]); + } + } +} + +u8 namcot163_readsound(u32 addr) +{ + if(addr >= 0x4800) + return(N106sound_Read(addr)); + return(read4(addr)); +} + +void namcot163_writesound(u32 addr,u8 data) +{ + if(addr >= 0x4800) + N106sound_Write(addr,data); + else + write4(addr,data); +} + +u8 namcot163_read(u32 addr) +{ + switch(addr & 0xF800) { + case 0x5000: + cpu_clear_irq(IRQ_MAPPER); + return((u8)irqcounter); + case 0x5800: + cpu_clear_irq(IRQ_MAPPER); + return((u8)(irqcounter >> 8)); + } + return((u8)(addr >> 8)); +} + +void namcot163_write(u32 addr,u8 data) +{ + switch(addr & 0xF800) { + case 0x5000: + irqcounter = (irqcounter & 0xFF00) | data; + cpu_clear_irq(IRQ_MAPPER); + break; + case 0x5800: + irqcounter = (irqcounter & 0x00FF) | (data << 8); + cpu_clear_irq(IRQ_MAPPER); + break; + case 0x8000: + case 0x8800: + case 0x9000: + case 0x9800: + case 0xA000: + case 0xA800: + case 0xB000: + case 0xB800: + chr[(addr >> 11) & 7] = data; + sync(); + break; + case 0xC000: + case 0xC800: + case 0xD000: + case 0xD800: + nt[(addr >> 11) & 3] = data; + log_printf("namcot-163.c: write: nt %d = $%02X\n",(addr >> 11) & 3,data); + sync(); + break; + case 0xE000: + prg[0] = data; + sync(); + break; + case 0xE800: + prg[1] = data; + sync(); + break; + case 0xF000: + prg[2] = data & 0x3F; + sync(); + break; + case 0xF800: + protect = data; + sync(); + break; + } +} + +void namcot163_reset(void (*syncfunc)(),int hard) +{ + int i; + + mem_setvramsize(8); + mem_setwramsize(8); + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4,namcot163_readsound); + mem_setwritefunc(4,namcot163_writesound); + mem_setreadfunc(5,namcot163_read); + mem_setwritefunc(5,namcot163_write); + for(i=8;i<16;i++) + mem_setwritefunc(i,namcot163_write); + if(hard) { + for(i=0;i<8;i++) { + prg[i & 3] = 0; + chr[i] = 0; + nt[i & 3] = 0; + } + prg[3] = 0xFF; + irqcounter = 0; + } + sync = syncfunc; + apu_setexternal(&sound); + sync(); +} + +void namcot163_cpucycle() +{ + if(irqcounter >= 0x8000) { + irqcounter++; + if(irqcounter == 0) + cpu_set_irq(IRQ_MAPPER); + } +} + +void namcot163_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,4); + STATE_ARRAY_U8(chr,8); + STATE_ARRAY_U8(nt,4); + STATE_U8(protect); + STATE_U16(irqcounter); + sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/namcot-163.h b/apps/nesemu2/src/mappers/chips/namcot-163.h new file mode 100644 index 00000000..1a9f54e0 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/namcot-163.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __namcot_163_h__ +#define __namcot_163_h__ + +#include "types.h" + +#define NAMCOT_HAS_SOUND 0x01 //has sound registers +#define NAMCOT_HAS_IRQ 0x02 //has irq registers +#define NAMCOT_HAS_NTSELECT 0x04 //has nt selection registers +#define NAMCOT_HAS_CHRDISABLE 0x08 //has chr disable register +#define NAMCOT_HAS_WRITEPROT 0x10 //has wram write protection +#define NAMCOT_HAS_RAMENABLE 0x20 //has prg ram enable register +#define NAMCOT_HAS_MIRRCTRL 0x40 //has mirroring control register + +#define MAKENAMCOT(n,f) (((n) << 8) | (f)) + +#define NAMCOT_129 MAKENAMCOT(0,NAMCOT_HAS_SOUND | NAMCOT_HAS_IRQ | NAMCOT_HAS_NTSELECT | NAMCOT_HAS_CHRDISABLE | NAMCOT_HAS_WRITEPROT) +#define NAMCOT_163 MAKENAMCOT(1,NAMCOT_HAS_SOUND | NAMCOT_HAS_IRQ | NAMCOT_HAS_NTSELECT | NAMCOT_HAS_CHRDISABLE | NAMCOT_HAS_WRITEPROT) +#define NAMCOT_175 MAKENAMCOT(2,NAMCOT_HAS_RAMENABLE) +#define NAMCOT_340 MAKENAMCOT(3,NAMCOT_HAS_NTSELECT | NAMCOT_HAS_MIRRCTRL) + +void namcot163_sync(); +void namcot163_write(u32 addr,u8 data); +void namcot163_reset(void (*syncfunc)(),int hard); +void namcot163_cpucycle(); +void namcot163_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/sachen-8259.c b/apps/nesemu2/src/mappers/chips/sachen-8259.c new file mode 100644 index 00000000..e6d7e4fe --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/sachen-8259.c @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/chips/sachen-8259.h" + +static u8 cmd,reg[8]; +static writefunc_t write4; +static void (*syncchr)(); + +void sachen8259_sync() +{ + mem_setprg32(8,reg[5] & 7); + if(reg[7]&1) + mem_setmirroring(MIRROR_V); + else { + switch(reg[7] & 6) { + case 0: mem_setmirroring(MIRROR_V); break; + case 2: mem_setmirroring(MIRROR_H); break; + case 4: mem_setmirroring2(0,1,1,1); break; + case 6: mem_setmirroring(MIRROR_1L); break; + } + } + if(nes->cart->chr.size) + syncchr(); +} + +void sachen8259a_syncchr() +{ + int i,j; + + for(i=0;i<4;i++) { + if(reg[7] & 1) + j = (reg[0] & 7) | ((reg[4] & 7) << 3); + else + j = (reg[i] & 7) | ((reg[4] & 7) << 3); + mem_setchr2(i * 2,(j << 1) | (i & 1)); + } +} + +void sachen8259b_syncchr() +{ + int i; + + for(i=0;i<4;i++) { + if(reg[7] & 1) + mem_setchr2(i * 2,(reg[0] & 7) | ((reg[4] & 7) << 3)); + else + mem_setchr2(i * 2,(reg[i] & 7) | ((reg[4] & 7) << 3)); + } +} + +void sachen8259c_syncchr() +{ + int i,j; + + for(i=0;i<4;i++) { + if(reg[7] & 1) + j = (reg[0] & 7) | ((reg[4] & 7) << 3); + else + j = (reg[i] & 7) | ((reg[4] & 7) << 3); + mem_setchr2(i * 2,(j << 2) | (i & 3)); + } +} + +void sachen8259d_syncchr() +{ + int i,j; + + for(i=0;i<4;i++) { + j = reg[i] & 7; + switch(i) { + case 1: j |= (reg[4] & 1) << 4; break; + case 2: j |= (reg[4] & 2) << 3; break; + case 3: j |= ((reg[4] & 4) << 2) | ((reg[6] & 1) << 3); break; + } + mem_setchr1(i,j); + } + mem_setchr4(4,~0); +} + +void sachen8259_write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + addr &= 0x101; + if(addr == 0x100) + cmd = data & 7; + else { + reg[cmd] = data; + sachen8259_sync(); + } +} + +void sachen8259_reset(int revision,int hard) +{ + int i; + + switch(revision) { + default: + case B_SACHEN_8259A: syncchr = sachen8259a_syncchr; break; + case B_SACHEN_8259B: syncchr = sachen8259b_syncchr; break; + case B_SACHEN_8259C: syncchr = sachen8259c_syncchr; break; + case B_SACHEN_8259D: syncchr = sachen8259d_syncchr; break; + } + write4 = mem_getwritefunc(4); + for(i=4;i<8;i++) + mem_setwritefunc(i,sachen8259_write); + for(i=0;i<8;i++) + reg[i] = 0; + cmd = 0; + sachen8259_sync(); +} + +void sachen8259_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(reg,8); + STATE_U8(cmd); + sachen8259_sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/sachen-8259.h b/apps/nesemu2/src/mappers/chips/sachen-8259.h new file mode 100644 index 00000000..fb3682a8 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/sachen-8259.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __sachen_8259_h__ +#define __sachen_8259_h__ + +void sachen8259_reset(int r,int hard); +void sachen8259_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.c b/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.c new file mode 100644 index 00000000..e19b9ea9 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.c @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" + +static u8 prg[2],chr[6]; +static u8 mirror; + +void tc0190fmc_sync() +{ + mem_setprg8(0x8,prg[0]); + mem_setprg8(0xA,prg[1]); + mem_setprg16(0xC,0xFF); + mem_setchr2(0,chr[0]); + mem_setchr2(2,chr[1]); + mem_setchr1(4,chr[2]); + mem_setchr1(5,chr[3]); + mem_setchr1(6,chr[4]); + mem_setchr1(7,chr[5]); + mem_setmirroring(mirror); +} + +void tc0190fmc_write(u32 addr,u8 data) +{ + switch(addr & 0xA003) { + case 0x8000: + prg[0] = data & 0x3F; + mirror = ((data >> 6) & 1) ^ 1; + break; + case 0x8001: + prg[1] = data; + break; + case 0x8002: + case 0x8003: + chr[addr & 1] = data; + break; + case 0xA000: + case 0xA001: + case 0xA002: + case 0xA003: + chr[(addr & 3) + 2] = data; + break; + } + tc0190fmc_sync(); +} + +void tc0190fmc_reset(int hard) +{ + int i; + + for(i=8;i<0xC;i++) + mem_setwritefunc(i,tc0190fmc_write); + prg[0] = prg[1] = 0; + for(i=0;i<6;i++) { + chr[i] = 0; + } + mirror = 0; + tc0190fmc_sync(); +} + +void tc0190fmc_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(prg,2); + STATE_ARRAY_U8(chr,6); + STATE_U8(mirror); + tc0190fmc_sync(); +} diff --git a/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h b/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h new file mode 100644 index 00000000..5e242071 --- /dev/null +++ b/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __taito_tc0190fmc_h__ +#define __taito_tc0190fmc_h__ + +#include "types.h" + +void tc0190fmc_sync(); +void tc0190fmc_reset(int hard); +void tc0190fmc_write(u32 addr,u8 data); +void tc0190fmc_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/mappers/fds/calls.h b/apps/nesemu2/src/mappers/fds/calls.h new file mode 100644 index 00000000..d6891b34 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls.h @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __fds__calls_h__ +#define __fds__calls_h__ + +#include "nes/nes.h" +#include "misc/log.h" + +#define HLECALL(name) void hle_ ## name () + +typedef struct fds_disk_header_s { + u8 blockid; + u8 ident[14]; + u8 manufacturer_code; + u8 name[4]; + u8 version; + u8 diskside; + u8 disknum; + u8 extra[2]; + u8 bootid; + u8 data[31]; + u8 numfiles; +} fds_disk_header_t; + +typedef struct fds_file_header_s { + u8 unk; + u8 blockid; + u8 index; +//start of file header on disk + u8 fileid; + u8 name[8]; + u16 loadaddr; + u16 filesize; + u8 loadarea; +//end of file header on disk +} fds_file_header_t; + +typedef struct fds_file_header2_s { + u8 fileid; + u8 name[8]; + u16 loadaddr; + u16 filesize; + u8 loadarea; + u16 srcaddr; + u8 srcarea; +} fds_file_header2_t; + +//disk.c +HLECALL(loadbootfiles); +HLECALL(loadfiles); +HLECALL(writefile); +HLECALL(appendfile); +HLECALL(getdiskinfo); +HLECALL(checkfilecount); +HLECALL(adjustfilecount); +HLECALL(setfilecount1); +HLECALL(setfilecount); + +//diskutil.c +HLECALL(xferdone); +HLECALL(checkdiskheader); +HLECALL(getnumfiles); +HLECALL(checkblocktype); +HLECALL(filematchtest); +HLECALL(loaddata); + +//ppu.c +HLECALL(enpf); +HLECALL(dispf); +HLECALL(enobj); +HLECALL(disobj); +HLECALL(enpfobj); +HLECALL(dispfobj); +HLECALL(setscroll); +HLECALL(spritedma); + +//vram.c +HLECALL(vramfill); +HLECALL(vramstructwrite); +HLECALL(writevrambuffer); +HLECALL(readvrambuffer); +HLECALL(preparevramstring); +HLECALL(preparevramstrings); +HLECALL(getvrambufferbyte); +HLECALL(loadtileset); + +//pads.c +HLECALL(readpads); +HLECALL(readverifypads); +HLECALL(orpads); +HLECALL(downpads); +HLECALL(readordownpads); +HLECALL(readdownverifypads); +HLECALL(readordownverifypads); +HLECALL(readdownexppads); +HLECALL(readkeyboard); + +//util.c +HLECALL(counterlogic); +HLECALL(random); +HLECALL(fetchdirectptr); +HLECALL(jumpengine); +HLECALL(memfill); +HLECALL(pixel2nam); +HLECALL(nam2pixel); +HLECALL(gethcparam); + +//misc.c +HLECALL(delay132); +HLECALL(delayms); +HLECALL(vintwait); +HLECALL(unk_EC22); + +//vector.c +HLECALL(nmi); +HLECALL(irq); +HLECALL(reset); + +#endif diff --git a/apps/nesemu2/src/mappers/fds/calls/disk.c b/apps/nesemu2/src/mappers/fds/calls/disk.c new file mode 100644 index 00000000..c1257540 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/disk.c @@ -0,0 +1,297 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "mappers/fds/calls.h" +#include "mappers/fds/fds.h" +#include "mappers/fds/hle.h" + +HLECALL(loadbootfiles) +{ + fds_disk_header_t disk_header; + fds_file_header_t file_header; + int i,j,pos; + u8 loadedfiles = 0; + + //change reset action + cpu_write(0x103,0x53); + + //leet hax + log_hle("loadbootfiles: setting diskside to 0\n"); + diskside = 0; + + //load disk header + pos = diskside * 65500; + memcpy(&disk_header,nes->cart->disk.data + pos,sizeof(fds_disk_header_t)); + pos += 57; + + //load boot files + for(i=0;icart->disk.data + pos,sizeof(fds_file_header_t)); + pos += 17; + if(file_header.fileid <= disk_header.bootid) { + memcpy(fn,file_header.name,8); + log_hle("loadbootfiles: loading file '%s', id %d, size $%X, load addr $%04X\n",fn,file_header.fileid,file_header.filesize,file_header.loadaddr); + loadedfiles++; + + //load into cpu + if(file_header.loadarea == 0) { + for(j=0;jcart->disk.data[pos + j + 1]); + } + + //load into ppu + else { + for(j=0;jcart->disk.data[pos + j + 1]); + } + } + pos += file_header.filesize; + } + + //put loaded files into y register + nes->cpu.y = loadedfiles; + + //put error code in accumulator + nes->cpu.a = 0; +} + +HLECALL(loadfiles) +{ + fds_disk_header_t disk_header; + fds_file_header_t file_header; + u8 diskid,loadedfiles = 0; + u32 addr1,addr2; + int pos,i,j,k; + u8 fileidlist[0x20]; + + //bios needs these flags set + nes->cpu.flags.z = 1; + nes->cpu.flags.n = 0; + nes->cpu.flags.v = 0; + nes->cpu.flags.c = 0; + nes->cpu.a = 0; + nes->cpu.y = 0; + + if(diskside == 0xFF) { + log_hle("loadfiles: hax: disk not inserted, setting to 0\n"); + diskside = 0; +// log_hle("loadfiles: aborting, disk not inserted\n"); +// return; + } + + addr1 = hle_getparam(0); + addr2 = hle_getparam(1); + hle_fixretaddr(2); + + log_hle("loadfiles: addr1,2 = $%04X, $%04X\n",addr1,addr2); + + //get diskid + diskid = cpu_read(addr1); + + //load file id list + for(i=0;i<20;i++) { + fileidlist[i] = cpu_read(addr2 + i); + log_hle("loadfiles: id list[%d] = $%02X\n",i,fileidlist[i]); + if(fileidlist[i] == 0xFF) + break; + } + + log_hle("loadfiles: disk id required = %d, disknum = %d\n",diskid,diskside); + + //read in disk header + pos = diskside * 65500; + memcpy(&disk_header,nes->cart->disk.data + pos,sizeof(fds_disk_header_t)); + pos += 57; + + //$FF indicates to load system boot files + if(fileidlist[0] == 0xFF) { + hle_loadbootfiles(); + return; + } + + //load files from id list + for(i=0;icart->disk.data + pos,sizeof(fds_file_header_t)); + pos += 17; + memcpy(fn,file_header.name,8); + for(k=0;fileidlist[k] != 0xFF && k < 20;k++) { + if(file_header.fileid == 0xE0) { + log_hle("loadfiles: file '%s', id $%X, size $%X, load addr $%04X (data @ %d, side %d)\n",fn,file_header.fileid,file_header.filesize,file_header.loadaddr,pos % 65500,diskside); + } + if(file_header.fileid == fileidlist[k]) { + log_hle("loadfiles: loading file '%s', id $%X, size $%X, load addr $%04X\n",fn,file_header.fileid,file_header.filesize,file_header.loadaddr); + loadedfiles++; + wasloaded = 1; + //load into cpu + if(file_header.loadarea == 0) { + for(j=0;jcart->disk.data[pos + j + 1]); + } + + //load into ppu + else { + for(j=0;jcart->disk.data[pos + j + 1]); + } + } + } + if(wasloaded == 0) + log_hle("loadfiles: skipping file '%s', id $%X, size $%X, load addr $%04X\n",fn,file_header.fileid,file_header.filesize,file_header.loadaddr); + pos += file_header.filesize; + } + + log_hle("loadfiles: loaded %d files\n",loadedfiles); + + //put loaded files into y register + nes->cpu.y = loadedfiles; + + //put error code in accumulator + nes->cpu.a = 0; +} + +HLECALL(writefile) +{ + fds_disk_header_t *disk_header; + fds_file_header_t *file_header; + fds_file_header2_t file_header2; + u32 addr1,addr2,pos; + char fn[10] = " \0"; + int i; + + addr1 = hle_getparam(0); + addr2 = hle_getparam(1); + hle_fixretaddr(2); + + log_hle("hle writefile: addr1 = %04X, addr2 = %04X\n",addr1,addr2); + + //position of disk data start + pos = diskside * 65500; + + //pointer to disk header + disk_header = (fds_disk_header_t*)(nes->cart->disk.data + pos); + + log_hle("hle writefile: %d files on disk (A = %d)\n",disk_header->numfiles,nes->cpu.a); + + //seek past the disk header + pos += 57; + + log_hle("file header: "); + for(i=0;i<17;i++) { + log_hle(" $%02X",cpu_read(addr2 + i)); + } + log_hle("\n"); + + //fill the file header2 struct + file_header2.fileid = cpu_read(addr2 + 0); + for(i=0;i<8;i++) + file_header2.name[i] = cpu_read(addr2 + 1); + file_header2.loadaddr = cpu_read(addr2 + 9) | (cpu_read(addr2 + 10) << 8); + file_header2.filesize = cpu_read(addr2 + 11) | (cpu_read(addr2 + 12) << 8); + file_header2.loadarea = cpu_read(addr2 + 13); + file_header2.srcaddr = cpu_read(addr2 + 14) | (cpu_read(addr2 + 15) << 8); + file_header2.srcarea = cpu_read(addr2 + 16); + + //show some debug info + memcpy(fn,&file_header2.name,8); + log_hle("writing file, header:\n"); + log_hle(" fileid: %02X\n",file_header2.fileid); + log_hle(" name: '%8s'\n",fn); + log_hle(" loadaddr: %04X\n",file_header2.loadaddr); + log_hle(" filesize: %04X\n",file_header2.filesize); + log_hle(" loadarea: %02X\n",file_header2.loadarea); + log_hle(" srcaddr: %04X\n",file_header2.srcaddr); + log_hle(" srcarea: %02X\n",file_header2.srcarea); + + //loop thru all the files + for(i=0;inumfiles;i++) { + file_header = (fds_file_header_t*)(nes->cart->disk.data + pos); + pos += 17; + if(file_header->fileid == file_header2.fileid) { + log_hle("hle writefile: match! '%s', id $%X, size $%X, load addr $%04X\n",fn,file_header->fileid,file_header->filesize,file_header->loadaddr); + log_hle("hle writefile: fileid match, writing at %d bytes @ %d (side %d)\n",file_header2.filesize,pos % 65500,diskside); + + memcpy(file_header->name,file_header2.name,8); + file_header->loadaddr = file_header2.loadaddr; + file_header->filesize = file_header2.filesize; + file_header->loadarea = file_header2.loadarea; + for(i=0;icart->disk.data[pos + i + 1] = cpu_read(file_header2.srcaddr + i); + else + nes->cart->disk.data[pos + i + 1] = ppu_memread(file_header2.srcaddr + i); + } + nes->cart->disk.data[pos + i + 1] = 0xFF; + break; + } + pos += file_header->filesize; + memcpy(fn,file_header->name,8); + log_hle("hle writefile: seeking over '%s', id $%X, size $%X, load addr $%04X\n",fn,file_header->fileid,file_header->filesize,file_header->loadaddr); + } + + if(nes->cpu.a != 0xFF) + disk_header->numfiles = nes->cpu.a + 1; + else + log_printf("appendfile!\n"); + + cpu_write(6,disk_header->numfiles); + + nes->cpu.a = 0; + nes->cpu.x = 0; + nes->cpu.flags.n = 0; + nes->cpu.flags.z = 1; +} + +HLECALL(appendfile) +{ + log_printf("appendfile!\n"); + nes->cpu.a = 0xFF; + hle_writefile(); +} + +HLECALL(getdiskinfo) +{ + log_printf("getdiskinfo not implemented\n"); +} + +HLECALL(checkfilecount) +{ + log_printf("checkfilecount not implemented\n"); +} + +HLECALL(adjustfilecount) +{ + log_printf("adjustfilecount not implemented\n"); +} + +HLECALL(setfilecount1) +{ + log_printf("setfilecount1 not implemented\n"); +} + +HLECALL(setfilecount) +{ + log_printf("setfilecount not implemented\n"); +} diff --git a/apps/nesemu2/src/mappers/fds/calls/diskutil.c b/apps/nesemu2/src/mappers/fds/calls/diskutil.c new file mode 100644 index 00000000..189124a1 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/diskutil.c @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" +#include "mappers/fds/fds.h" + +HLECALL(xferdone) +{ + log_printf("xferdone: not implemented\n"); + //no error + if(nes->cpu.flags.z == 0) { + nes->cpu.a = (cpu_read(0xFA) & 9) | 0x26; + cpu_write(0xFA,nes->cpu.a); + cpu_write(0x4025,nes->cpu.a); + nes->cpu.a = nes->cpu.x = 0; + nes->cpu.flags.i = 0; + return; + } + else { + + } +} + +HLECALL(checkdiskheader) +{ + log_printf("checkdiskheader: not implemented\n"); +} + +HLECALL(getnumfiles) +{ + fds_disk_header_t *disk_header; + int pos; + + //position of disk data start + pos = diskside * 65500; + + //pointer to disk header + disk_header = (fds_disk_header_t*)(nes->cart->disk.data + pos); + + cpu_write(6,disk_header->numfiles); + + log_printf("getnumfiles: %d files on disk %d side %d\n",disk_header->numfiles,diskside >> 1,diskside & 1); +} + +HLECALL(checkblocktype) +{ + log_printf("checkblocktype: not implemented\n"); +} + +HLECALL(filematchtest) +{ + log_printf("filematchtest: not implemented\n"); +} + +HLECALL(loaddata) +{ + log_printf("loaddata: not implemented\n"); +} diff --git a/apps/nesemu2/src/mappers/fds/calls/misc.c b/apps/nesemu2/src/mappers/fds/calls/misc.c new file mode 100644 index 00000000..845c66dc --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/misc.c @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" + +HLECALL(delay132) +{ + int i; + + for(i=0;i<132;i++) + cpu_tick(); +} + +HLECALL(delayms) +{ + int delay = 1790 * nes->cpu.y + 5; + int i; + + for(i=0;icpu.sp-- | 0x100,nes->cpu.a); + + //save old nmi action + cpu_write(nes->cpu.sp-- | 0x100,cpu_read(0x100)); + + //write new nmi action + cpu_write(0x100,0); + + //enable nmi + tmp = cpu_read(0xFF) | 0x80; + cpu_write(0xFF,tmp); + cpu_write(0x2000,tmp); + + //clear zero flag, set negative flag + nes->cpu.flags.z = 0; + nes->cpu.flags.n = 1; + + log_hle("vintwait!\n"); +} + +HLECALL(unk_EC22) +{ + log_printf("unk_EC22 not implemented\n"); +} diff --git a/apps/nesemu2/src/mappers/fds/calls/pads.c b/apps/nesemu2/src/mappers/fds/calls/pads.c new file mode 100644 index 00000000..a72c6304 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/pads.c @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" + +HLECALL(readpads) +{ + int i; + u8 data,tmp1,tmp2,tmp3,tmp4; + + data = cpu_read(0xFB); + cpu_write(0x4016,data + 1); + cpu_write(0x4016,data); + for(tmp1=tmp2=tmp3=tmp4=0,i=0;i<8;i++) { + tmp1 <<= 1; + tmp2 <<= 1; + tmp3 <<= 1; + tmp4 <<= 1; + data = cpu_read(0x4016); + tmp1 |= data & 1; + tmp2 |= (data >> 1) & 1; + data = cpu_read(0x4017); + tmp3 |= data & 1; + tmp4 |= (data >> 1) & 1; + } + cpu_write(0xF5,tmp1); + cpu_write(0x00,tmp2); + cpu_write(0xF6,tmp3); + cpu_write(0x01,tmp4); + nes->cpu.flags.z = 1; + nes->cpu.flags.n = 0; +} + +HLECALL(readverifypads) +{ + u8 tmp[4]; + + //read initial pad state + hle_readpads(); + + //save initial pad state + tmp[0] = cpu_read(0xF5); + tmp[1] = cpu_read(0x00); + tmp[2] = cpu_read(0xF6); + tmp[3] = cpu_read(0x01); + + //keep looping until reads match + for(;;) { + + //read pad data + hle_readpads(); + + //check for match + if(tmp[0] == cpu_read(0xF5) && + tmp[1] == cpu_read(0x00) && + tmp[2] == cpu_read(0xF6) && + tmp[3] == cpu_read(0x01)) + break; + + //save pad state + tmp[0] = cpu_read(0xF5); + tmp[1] = cpu_read(0x00); + tmp[2] = cpu_read(0xF6); + tmp[3] = cpu_read(0x01); + } + log_printf("readverifypads\n"); +} + +HLECALL(orpads) +{ + cpu_write(0xF5,cpu_read(0x00) | cpu_read(0xF5)); + cpu_write(0xF6,cpu_read(0x01) | cpu_read(0xF6)); +} + +HLECALL(downpads) +{ + int i; + + for(i = 1; i >= 0; i--) { + nes->cpu.a = cpu_read(0xF5 + i); + nes->cpu.y = nes->cpu.a; + nes->cpu.a ^= cpu_read(0xF7 + i); + nes->cpu.a &= cpu_read(0xF5 + i); + cpu_write(0xF5 + i,nes->cpu.a); + cpu_write(0xF7 + i,nes->cpu.y); + } +} + +HLECALL(readordownpads) +{ + hle_readpads(); + hle_orpads(); + hle_downpads(); + log_hle("readordownpads\n"); +} + +HLECALL(readdownverifypads) +{ + hle_readverifypads(); + hle_downpads(); +} + +HLECALL(readordownverifypads) +{ + hle_readverifypads(); + hle_orpads(); + hle_downpads(); +} + +HLECALL(readdownexppads) +{ + hle_readpads(); + //now read expansion port! + hle_downpads(); +//log_printf("readdownexppads not implemented\n"); +} + +HLECALL(readkeyboard) +{ + log_printf("readkeyboard not implemented\n"); +} diff --git a/apps/nesemu2/src/mappers/fds/calls/ppu.c b/apps/nesemu2/src/mappers/fds/calls/ppu.c new file mode 100644 index 00000000..69a44db8 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/ppu.c @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" + +HLECALL(enpf) +{ + nes->cpu.a = cpu_read(0xFE) | 0x08; + cpu_write(0xFE,nes->cpu.a); + cpu_write(0x2001,nes->cpu.a); +} + +HLECALL(dispf) +{ + nes->cpu.a = cpu_read(0xFE) & 0xF7; + cpu_write(0xFE,nes->cpu.a); + cpu_write(0x2001,nes->cpu.a); +} + +HLECALL(enobj) +{ + nes->cpu.a = cpu_read(0xFE) | 0x10; + cpu_write(0xFE,nes->cpu.a); + cpu_write(0x2001,nes->cpu.a); +} + +HLECALL(disobj) +{ + nes->cpu.a = cpu_read(0xFE) & 0xEF; + cpu_write(0xFE,nes->cpu.a); + cpu_write(0x2001,nes->cpu.a); +} + +HLECALL(enpfobj) +{ + hle_enpf(); + hle_enobj(); +} + +HLECALL(dispfobj) +{ + hle_dispf(); + hle_disobj(); +} + +HLECALL(setscroll) +{ + cpu_read(0x2002); + cpu_write(0x2005,cpu_read(0xFD)); + cpu_write(0x2005,cpu_read(0xFC)); + cpu_write(0x2000,cpu_read(0xFF)); +} + +HLECALL(spritedma) +{ + cpu_write(0x2003,0); //sprite address + cpu_write(0x4014,2); //sprite dma +} diff --git a/apps/nesemu2/src/mappers/fds/calls/util.c b/apps/nesemu2/src/mappers/fds/calls/util.c new file mode 100644 index 00000000..199a9ec7 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/util.c @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" + +HLECALL(counterlogic) +{ + u8 tmp; + int i; + + log_hle("counterlogic: a,x,y = $%02X, $%02X, $%02X\n",nes->cpu.a,nes->cpu.x,nes->cpu.y); + + //first counter is decimal counter (9 down to 0, repeat) + tmp = cpu_read(nes->cpu.x); + + //when this one transitions, decrement counters in upper region + if(tmp == 0) { + + //reset counter to 9 + cpu_write(nes->cpu.x,9); + + //these stay at 0 + for(i=nes->cpu.a+1;i<=nes->cpu.y;i++) { + tmp = cpu_read(i); + if(tmp > 0) + cpu_write(i,tmp - 1); + } + } + else + cpu_write(nes->cpu.x,tmp - 1); + + //the rest stay at 0 + for(i=nes->cpu.x+1;i<=nes->cpu.a;i++) { + tmp = cpu_read(i); + if(tmp > 0) + cpu_write(i,tmp - 1); + } +} + +HLECALL(random) +{ + u8 i; + + for(i=0;icpu.y;i++) { + cpu_write(nes->cpu.x + i,(u8)rand()); + } + log_hle("random: Y = $%02X\n",nes->cpu.y); +} + +//doesnt handle stack wrapping (easy fix! wtf!) +HLECALL(fetchdirectptr) +{ + u32 tmp,retaddr; + + //current stack address + tmp = 0x100 + nes->cpu.sp + 2; + + //get the return address off the stack + retaddr = cpu_read(tmp + 1); + retaddr |= cpu_read(tmp + 2) << 8; + + //read input parameter, write to memory + cpu_write(0,cpu_read(retaddr + 1)); + cpu_write(1,cpu_read(retaddr + 2)); + + log_hle("direct pointer fetched is $%02X%02X (retaddr = $%04X)\n",cpu_read(1),cpu_read(0),retaddr); + + //offset to new return address + retaddr += 2; + + //write new return address to the stack + cpu_write(tmp + 1,(u8)retaddr); + cpu_write(tmp + 2,(u8)(retaddr >> 8)); +} + +HLECALL(jumpengine) +{ + u32 addr; + + //read address coming after the opcode calling us + addr = cpu_read(++nes->cpu.sp | 0x100); + addr |= cpu_read(++nes->cpu.sp | 0x100) << 8; + + //add one (jsr return address is 1 off) + addr++; + + //offset into the jump table + addr += nes->cpu.a * 2; + + //jmp indirect + hlemem[0x10] = 0x6C; + + //destination address + hlemem[0x11] = (u8)addr; + hlemem[0x12] = (u8)(addr >> 8); + + log_hle("jumpengine: entry = %d, address of table = $%04X (dest = $%02X%02X)\n",nes->cpu.a,addr - nes->cpu.a * 2,cpu_read(addr+1),cpu_read(addr)); +} + +HLECALL(memfill) +{ + int i; + u32 start,stop; + + log_hle("memfill: fill start $%04X, stop $%04X, data $%02X\n",nes->cpu.x << 8,nes->cpu.y << 8,nes->cpu.a); + start = nes->cpu.x << 8; + stop = nes->cpu.y << 8; + while(start <= stop) { + log_hle("memfill: filling 256 bytes at $%04X with $%02X\n",start,nes->cpu.a); + for(i=0;i<256;i++) { + cpu_write(start + i,nes->cpu.a); + } + start += 0x100; + } +} + +HLECALL(pixel2nam) +{ + u8 x,y; + u32 address; + + x = cpu_read(2) / 8; + y = cpu_read(3) / 8; + address = 0x2000 + x + (y * 32); + cpu_write(0,(u8)(address >> 8)); + cpu_write(1,(u8)address); + log_hle("pixel2nam: x,y = %d,%d address = $%04X\n",cpu_read(2),cpu_read(3),address); +} + +HLECALL(nam2pixel) +{ + u8 x,y; + u32 address; + + address = ((cpu_read(0) << 8) | cpu_read(1)) & 0x3FF; + x = (address & 0x1F) * 8; + y = ((address >> 5) & 0x1F) * 8; + cpu_write(2,x); + cpu_write(3,y); + log_hle("nam2pixel: address = $%04X x,y = %d,%d\n",(cpu_read(0) << 8) | cpu_read(1),x,y); +} + +HLECALL(gethcparam) +{ + u8 writeprotectcheck = nes->cpu.flags.c ^ 1; + u32 addr1,addr2,tmp; + + log_hle("gethcparam: writeprotectcheck = %d, a = $%02X\n",writeprotectcheck,nes->cpu.a); + log_hle("stack: %02X %02X %02X %02X %02X %02X\n",cpu_read((nes->cpu.sp + 1) | 0x100),cpu_read((nes->cpu.sp + 2) | 0x100),cpu_read((nes->cpu.sp + 3) | 0x100),cpu_read((nes->cpu.sp + 4) | 0x100),cpu_read((nes->cpu.sp + 5) | 0x100),cpu_read((nes->cpu.sp + 6) | 0x100)); + + tmp = cpu_read((nes->cpu.sp + 3) | 0x100); + tmp |= cpu_read((nes->cpu.sp + 4) | 0x100) << 8; + + log_hle("tmp = $%04X\n",tmp); + + addr1 = cpu_read(tmp + 0) << 8; + addr1 |= cpu_read(tmp + 1); + + addr2 = cpu_read(tmp + 2) << 8; + addr2 |= cpu_read(tmp + 3); + + log_hle("addr1 = $%04X\n",addr1); + log_hle("addr2 = $%04X\n",addr2); + + cpu_write(0,cpu_read(tmp + 0)); + cpu_write(1,cpu_read(tmp + 1)); + if(nes->cpu.a == 0xFF) { + cpu_write(2,cpu_read(tmp + 2)); + cpu_write(3,cpu_read(tmp + 3)); + } +} diff --git a/apps/nesemu2/src/mappers/fds/calls/vector.c b/apps/nesemu2/src/mappers/fds/calls/vector.c new file mode 100644 index 00000000..636c2249 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/vector.c @@ -0,0 +1,237 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" + +//nmi vector +HLECALL(nmi) +{ + u8 tmp,action = cpu_read(0x100) >> 6; + +// log_hle("nmi: action = $%02X\n",action); + switch(action) { + + //returning from vintwait, return to address that called vintwait + case 0: + + //disable nmi + tmp = cpu_read(0xFF) & 0x7F; + cpu_write(0xFF,tmp); + cpu_write(0x2000,tmp); + cpu_read(0x2002); + + //eat nmi return address + nes->cpu.sp += 3; + + //restore old nmi action + cpu_write(0x100,cpu_read(++nes->cpu.sp | 0x100)); + + //restore a + nes->cpu.a = cpu_read(++nes->cpu.sp | 0x100); + + //rts + hlemem[0] = 0x60; + return; + + //game vectors + case 1: + case 2: + case 3: + //jmp ($DFF6/8/A) + hlemem[0] = 0x6C; + hlemem[1] = 0xF6 + ((action - 1) * 2); + hlemem[2] = 0xDF; + break; + } +} + +static void irqdisktransfer() +{ + //eat the irq return address and pushed flags + nes->cpu.sp += 3; + + //read data from disk into the a register + nes->cpu.x = cpu_read(0x4031); + + //write data in a reg to disk + cpu_write(0x4024,nes->cpu.a); + + //txa + nes->cpu.a = nes->cpu.x; + + //set hle register to rts opcode + hlemem[8] = 0x60; //rts +} + +//irq vector +HLECALL(irq) +{ + u8 action = cpu_read(0x101); + u8 tmp = action & 0x3F; + +// log_hle("irq: action = $%02X\n",action); + switch(action >> 6) { + //disk byte skip routine ([$0101] = 00nnnnnn; n is # of bytes to skip) + //this is mainly used when the CPU has to do some calculations while bytes + //read off the disk need to be discarded. + case 0: + + //decrement the number of bytes to skip + tmp = (action & 0x3F) - 1; + + //if it hasnt expired, throw data away and store new value + if(tmp != 0xFF) { + cpu_write(0x101,tmp); + cpu_read(0x4031); + } + + //modify return action + hlemem[8] = 0x40; //rti + break; + + case 1: + irqdisktransfer(); + break; + + //disk IRQ acknowledge routine ([$0101] = 10xxxxxx). + //don't know what this is used for, or why a delay is put here. + case 2: + //ack the interrupt + cpu_read(0x4030); + + //set hle register to rti opcode + hlemem[8] = 0x40; //rti + break; + + //game irq + case 3: + //jmp indirect + hlemem[0x8] = 0x6C; + hlemem[0x9] = 0xFE; + hlemem[0xA] = 0xDF; + break; + } +} + +/* +($0102): PC action on reset +[$0101]: PC action on IRQ. set to $80 on reset +[$0100]: PC action on NMI. set to $C0 on reset +($DFFE): disk game IRQ vector (if [$0101] = 11xxxxxxB) +($DFFC): disk game reset vector (if ($0102) = $5335, or $AC35) +($DFFA): disk game NMI vector #3 (if [$0100] = 11xxxxxxB) +($DFF8): disk game NMI vector #2 (if [$0100] = 10xxxxxxB) +($DFF6): disk game NMI vector #1 (if [$0100] = 01xxxxxxB) + +[$FF]: value last written to $2000 $80 on reset. +[$FE]: value last written to $2001 $06 on reset +[$FD]: value last written to $2005#1 0'd on reset. +[$FC]: value last written to $2005#2 0'd on reset. +[$FB]: value last written to $4016 0'd on reset. +[$FA]: value last written to $4025 $2E on reset. +[$F9]: value last written to $4026 $FF on reset. +$F5..$F8 : Used by controller read routines +$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too. +*/ + +extern int showdisasm; + +//reset vector +HLECALL(reset) +{ + //u8 m102,m103; + + srand((u32)time(0)); +// showdisasm = 1; + + //flag setup + nes->cpu.flags.i = 1; + nes->cpu.flags.d = 0; + + cpu_write(0x101,0xC0); //action on irq + cpu_write(0x100,0xC0); //action on nmi + + //$2000 + cpu_write(0xFF,0x10); + cpu_write(0x2000,0x10); + + //$2001 + cpu_write(0xFE,0x06); + cpu_write(0x2001,0x06); + + cpu_write(0x4022,0x00); //disable timer irq + cpu_write(0x4023,0x00); //disable sound & disk I/O + cpu_write(0x4023,0x83); //enable sound & disk I/O + + cpu_write(0xFD,0x00); + cpu_write(0xFC,0x00); + cpu_write(0xFB,0x00); + cpu_write(0x4016,0x00); + + cpu_write(0xFA,0x2E); + cpu_write(0x4025,0x2E); + + cpu_write(0xF9,0xFF); + cpu_write(0x4026,0xFF); + + cpu_write(0x4010,0x00); + cpu_write(0x4015,0x0F); + cpu_write(0x4080,0x80); + cpu_write(0x408A,0xE8); + cpu_write(0x4017,0xC0); + + nes->cpu.sp = 0xFF; + //m102 = cpu_read(0x102); + //m103 = cpu_read(0x103); + cpu_read(0x102); + cpu_read(0x103); + +/*;if ([$102]=$35)and(([$103]=$53)or([$103]=$AC)) then +; [$103]:=$53 +; CALL RstPPU05 +; CLI +; JMP [$DFFC] +$EE84 LDA $0102 +$EE87 CMP #$35 +$EE89 BNE $EEA2 +$EE8B LDA $0103 +$EE8E CMP #$53 +$EE90 BEQ $EE9B +$EE92 CMP #$ac +$EE94 BNE $EEA2 +$EE96 LDA #$53 +$EE98 STA $0103 +$EE9B JSR RstPPU05 +$EE9E CLI; enable interrupts +$EE9F JMP ($DFFC)*/ + + memset(nes->ppu.nametables,0,0x1000); + {//if(m102 == 0x35 && (m103 == 0x53 || m103 == 0xAC)) { + cpu_write(0x103,0x53); + hle_setscroll(); + hle_loadbootfiles(); + } + cpu_write(0x102,0xAC); //action on reset + nes->cpu.flags.i = 0; +} diff --git a/apps/nesemu2/src/mappers/fds/calls/vram.c b/apps/nesemu2/src/mappers/fds/calls/vram.c new file mode 100644 index 00000000..4458bd86 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/calls/vram.c @@ -0,0 +1,391 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/fds/calls.h" +#include "mappers/fds/hle.h" + +static void writeppu(u32 addr,u8 data) +{ + if(addr >= 0x3F00) + ppu_pal_write(addr & 0x1F,data); + else + ppu_memwrite(addr,data); +} + +/* +vram fill +--------- +A is HI VRAM addr (LO VRAM addr is always 0) +X is fill value +Y is iteration count (256 written bytes per iteration). if A is $20 or + greater (indicating name table VRAM), iteration count is always 4, + and this data is used for attribute fill data. +*/ +HLECALL(vramfill) +{ + int i; + u8 data,count; + + //bios saves these here + cpu_write(0,nes->cpu.a); + cpu_write(1,nes->cpu.x); + cpu_write(2,nes->cpu.y); + + //reset ppu toggle + cpu_read(0x2002); + + //write ppu control (increment 2007 writes by 1) + cpu_write(0xFF,cpu_read(0xFF) & 0xFB); + + //write to ppu ctrl reg + cpu_write(0x2000,cpu_read(0xFF)); + + //set vram address + cpu_write(0x2006,nes->cpu.a); + cpu_write(0x2006,0); + + //data to fill with + data = nes->cpu.x; + + //fill vram + if(nes->cpu.a < 0x20) { + count = nes->cpu.y + 1; + } + + //fill nametable + else { + count = 4; + } + + log_hle("vramfill: fill at $%04X, %d iterations\n",nes->cpu.a << 8,count); + + while(count--) { + for(i=0;i<256;i++) { + writeppu(SCROLL++,data); + SCROLL &= 0x3FFF; + } + } + + //see if we fill attributes now + if(nes->cpu.a >= 0x20) { + cpu_write(0x2006,nes->cpu.a + 3); + cpu_write(0x2006,0xC0); + for(i=0;i<0x40;i++) { + writeppu(SCROLL++,nes->cpu.y); + SCROLL &= 0x3FFF; + } + } +} + +HLECALL(vramstructwrite) +{ + u8 data,len,tmp,i; + u32 srcaddr,destaddr,tmp32; + + //setup vram increment to 1 + data = cpu_read(0xFF); + data &= ~4; + cpu_write(0xFF,data); + cpu_write(0x2000,data); + + //get parameter following the calling jsr, it is the address of the ppu data string + srcaddr = hle_getparam(0); + + //fix the return address + hle_fixretaddr(1); + + log_hle("vramstructwrite: param = $%04X\n",srcaddr); + + //process! + for(;;) { + + data = cpu_read(srcaddr++); + + //ternimate! + if(data >= 0x80) { + log_hle("vramstructwrite: end opcode @ $%04X = $%02X\n",srcaddr-1,data); + break; + } + + //jsr! + else if(data == 0x4C) { + //save old address + tmp32 = srcaddr; + + //get new address + srcaddr = cpu_read(tmp32 + 0); + srcaddr |= cpu_read(tmp32 + 1) << 8; + tmp32 += 2; + + //write return address to stack + cpu_write(nes->cpu.sp-- | 0x100,(u8)(tmp32 >> 8)); + cpu_write(nes->cpu.sp-- | 0x100,(u8)tmp32); + log_hle("vramstructwrite: jsr: old addr, new addr = $%04X, $%04X\n",tmp32-3,srcaddr); + } + + //rts! + else if(data == 0x60) { + log_hle("vramstructwrite: rts @ $%04X\n",srcaddr-1); + //read return address from the stack + srcaddr = cpu_read(++nes->cpu.sp | 0x100); + srcaddr |= cpu_read(++nes->cpu.sp | 0x100) << 8; + } + + //data! + else { + + //get destination address + destaddr = (data << 8) | cpu_read(srcaddr++); + + //read size/flags byte + data = cpu_read(srcaddr++); + len = (data & 0x3F) == 0 ? 64 : (data & 0x3F); + + log_hle("vramstructwrite: %s data (addr = $%04X, len = $%02X)\n",(data & 0x40) ? "filling" : "copying",destaddr,len); + + //setup increment + tmp = cpu_read(0xFF) | 4; + if((data & 0x80) == 0) { + tmp &= ~4; + } + cpu_write(0xFF,tmp); + cpu_write(0x2000,tmp); + + //reset the toggle and write the destination address + cpu_read(0x2002); + cpu_write(0x2006,(u8)(destaddr >> 8)); + cpu_write(0x2006,(u8)destaddr); + + //fill/copy data + for(i=0;i= 0x80) { + break; + } + + //write dest address + cpu_write(0x2006,data); + cpu_write(0x2006,cpu_read(srcaddr++)); + + //get length + data = cpu_read(srcaddr++); + + //write data + for(i=0;icpu.y; + u8 bufsize,buflen,bufpos; + u32 srcaddr; + + srcaddr = hle_getparam(0); + hle_fixretaddr(1); + + log_hle("preparevramstring: destaddr = $%04X, srcaddr = $%04X, len = %d\n",nes->cpu.x | (nes->cpu.a << 8),srcaddr,len); + + //get max buffer size + bufsize = cpu_read(0x300); + + //get size of data already in the buffer + buflen = cpu_read(0x301); + + log_hle(" -- bufsize = $%02X, buflen = $%02X\n",bufsize,buflen); + + //see if data is too big + if((int)(buflen + len + 3) > (int)bufsize) { + nes->cpu.a = 1; + return; + } + + //get position to begin writing in + bufpos = buflen; + + //new size of the buffer + buflen += len + 3; + + //bios doesnt modify this byte? +// cpu_write(0x300,len); + cpu_write(0x301,buflen); + cpu_write(0x302 + bufpos,nes->cpu.a); + cpu_write(0x303 + bufpos,nes->cpu.x); + cpu_write(0x304 + bufpos,nes->cpu.y); + for(i=0;icpu.a = 0xFF; +} + +HLECALL(preparevramstrings) +{ + log_printf("preparevramstrings not implemented\n"); +} + +HLECALL(getvrambufferbyte) +{ + log_printf("preparevramstrings not implemented\n"); +} + +HLECALL(loadtileset) +{ + int units = nes->cpu.x; + u16 ppuaddr = (nes->cpu.y << 8) | (nes->cpu.a & 0xF0); + u8 fill,mode = nes->cpu.a; + u32 i,addr; + + addr = hle_getparam(0); + hle_fixretaddr(1); + + log_hle("loadtileset: ppuaddr = $%04X, cpuaddr = $%04X, units = %d, mode = %d\n",ppuaddr,addr,units,(mode & 0xC) >> 2); + +// if(mode & 0xC) +// addr &= ~0xF; + + //read from ppu + if(mode & 2) { + while(units--) { + log_hle("loadtileset: read: ppu,cpu addr = $%04X,$%04X, mode = %X\n",ppuaddr,addr,mode & 0xC); +// fill = (mode & 1) ? 0xFF : 0x00; + switch(mode & 0xC) { + case 0x0: + for(i=0;i<16;i++) cpu_write(addr++,ppu_memread(ppuaddr++)); + break; + case 0x4: + for(i=0;i<8;i++) cpu_write(addr++,ppu_memread(ppuaddr++)); + for(i=0;i<8;i++) ppu_memread(ppuaddr++); + break; + case 0x8: + for(i=0;i<8;i++) ppu_memread(ppuaddr++); + for(i=0;i<8;i++) cpu_write(addr++,ppu_memread(ppuaddr++)); + break; + case 0xC: + log_hle("loadtileset: reading and mode 3 not supported\n"); + ppuaddr += 8; + break; + } + } + } + + //write to ppu + else { + while(units--) { + log_hle("loadtileset: write: ppu,cpu addr = $%04X,$%04X, mode = %X\n",ppuaddr,addr,mode & 0xC); + fill = (mode & 1) ? 0xFF : 0x00; + switch(mode & 0xC) { + case 0x0: + for(i=0;i<16;i++) ppu_memwrite(ppuaddr++,cpu_read(addr++)); + break; + case 0x4: + for(i=0;i<8;i++) ppu_memwrite(ppuaddr++,cpu_read(addr++)); + for(i=0;i<8;i++) ppu_memwrite(ppuaddr++,fill); + break; + case 0x8: + for(i=0;i<8;i++) ppu_memwrite(ppuaddr++,fill); + for(i=0;i<8;i++) ppu_memwrite(ppuaddr++,cpu_read(addr++)); + break; + case 0xC: + for(i=0;i<8;i++,ppuaddr++) { + u8 data = cpu_read(addr++); + + ppu_memwrite(ppuaddr,data ^ fill); + ppu_memwrite(ppuaddr+8,data); + } + ppuaddr += 8; + break; + } + } + } + +} +/* +static void inc00bya() +{ + u16 tmp; + + tmp = cpu_read(0) | (cpu_read(1) << 8); + tmp += nes->cpu.a; + cpu_write(0,(u8)tmp); + cpu_write(1,(u8)(tmp >> 8)); + cpu_write(2,cpu_read(2) - 1); +} + +static void inc00by8() +{ + nes->cpu.a = 8; + inc00bya(); +} +*/ diff --git a/apps/nesemu2/src/mappers/fds/fds.c b/apps/nesemu2/src/mappers/fds/fds.c new file mode 100644 index 00000000..3eff2251 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/fds.c @@ -0,0 +1,630 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "mappers/mapperinc.h" +#include "mappers/sound/s_FDS.h" +#include "mappers/fds/hle.h" +#include "misc/log.h" +#include "misc/config.h" + +#define SHORTIRQ 100 +#define LONGIRQ 150 + +static apu_external_t fdssound = { + FDSsound_Load, + FDSsound_Unload, + FDSsound_Reset, + FDSsound_Get, + 0 +}; + +static readfunc_t read4; +static writefunc_t write4; +static u8 mirror,newdiskside; +static u8 diskread,writeskip; +static u8 diskirq,timerirq; +static u8 irqenable,ioenable,control,status; +static u16 irqcounter,irqlatch; +static int diskaddr; +static int diskflip; +static readfunc_t read_disk; +static writefunc_t write_disk; + +//hle +static int hlefds = 0; +static char hleident[6] = "HLEFDS"; + +//global (for hle fds bios) +int diskside; + +void hlefds_write(u32 addr,u8 data); + +static void setirq(u8 mask) +{ + status |= mask; + cpu_set_irq(mask); +} + +static void clearirq(u8 mask) +{ + status &= ~mask; + cpu_clear_irq(mask); +} + +static void sync() +{ + mem_setmirroring(mirror); +} + +static u8 fds_read_disk(u32 diskaddr) +{ + return(nes->cart->disk.data[(diskside * 65500) + diskaddr]); +} + +static void fds_write_disk(u32 diskaddr, u8 data) +{ + nes->cart->disk.data[(diskside * 65500) + diskaddr] = data; +} + +static u8 fds_read(u32 addr) +{ + u8 ret = 0; + + //read from nes apu regs + if(addr < 0x4020) + return(read4(addr)); + + //fds read + switch(addr) { + + //status register + case 0x4030: + ret = status; +// log_printf("fds.c: irq ack. (%02X - %02X)\n",nes->cpu.prev_irqstate,nes->cpu.irqstate); + clearirq(IRQ_DISK | IRQ_TIMER); + return(ret); + + //read data register + case 0x4031: + + //if no disk inserted, return 0 + if(diskside == 0xFF) + return(diskread); + + //get byte read from disk +// diskread = nes->cart->disk.data[(diskside * 65500) + diskaddr]; + diskread = read_disk(diskaddr); +// log_printf("fds.c: $4031 read: side = %d, diskaddr = %d, diskdata = $%02X\n",diskside,diskaddr,diskread); + + //increment disk data address +// if(diskaddr < 64999) + diskaddr++; + + //setup disk irq cycles + diskirq = SHORTIRQ; + + //clear irq status + clearirq(IRQ_DISK); + + return(diskread); + + //drive status + case 0x4032: + ret = 0x40; + if(diskside == 0xFF) { + ret |= 5; + } + if((diskside == 0xFF) || ((control & 1) == 0) || (control & 2)) { +// log_printf("fds_read: disk not inserted. (diskside = $%02X, control = $%02X)\n",diskside,control); + ret |= 2; + } +// log_printf("fds.c: read $4032: status = $%02X\n",ret); + return(ret); + + //external connector read + case 0x4033: + return(0x80); + + } + + //hlefds registers/memory + if(addr >= 0x4222 && addr < 0x4300) { + if(hlefds) + return(hlefds_read(addr)); + } + + return((u8)(addr >> 8)); +} + +static void fds_write(u32 addr,u8 data) +{ + if(addr < 0x4020) { + write4(addr,data); + return; + } + +// log_printf("fds.c: write: $%04X = $%02X\n",addr,data); + switch(addr) { + + //irq latch low + case 0x4020: +// log_printf("fds.c: irq write: $%04X = $%02X\n",addr,data); + clearirq(IRQ_TIMER); + irqlatch = (irqlatch & 0xFF00) | data; + break; + + //irq latch high + case 0x4021: +// log_printf("fds.c: irq write: $%04X = $%02X\n",addr,data); + clearirq(IRQ_TIMER); + irqlatch = (irqlatch & 0x00FF) | (data << 8); + break; + + //irq enable + case 0x4022: +// log_printf("fds.c: irq enable: $%04X = $%02X\n",addr,data); + clearirq(IRQ_TIMER); + irqenable = data; + irqcounter = irqlatch; + break; + + //i/o enable + case 0x4023: + ioenable = data; + break; + + //write data + case 0x4024: + if(diskside != 0xFF && (control & 4) == 0 && (ioenable & 1)) { +// clearirq(IRQ_DISK); + if(diskaddr >= 0 && diskaddr < 65500) { + if(writeskip) + writeskip--; + else if(diskaddr >= 2) { +// nes->cart->disk.data[(diskside * 65500) + (diskaddr - 2)] = data; + write_disk(diskaddr - 2, data); +// log_printf("fds.c: writing data to disk %d addr (%d - 2) data $%02X\n",diskside,diskaddr,data); + } + } + } + break; + + //fds control + case 0x4025: +// log_printf("fds.c: $4025 write: %02X (diskaddr == %d)\n", data, diskaddr); + clearirq(IRQ_DISK); + mirror = ((data & 8) >> 3) ^ 1; + mem_setmirroring(mirror); + if(diskside == 0xFF) + break; + if((data & 0x40) == 0) { + if((control & 0x40) && (data & 0x10) == 0) { + diskaddr -= 2; + diskirq = LONGIRQ; + } + if(diskaddr < 0) + diskaddr = 0; + } + if(data & 2) { + diskaddr = 0; + diskirq = LONGIRQ; +// log_printf("fds.c: transfer reset! $4025.1 set\n"); + } + if((data & 4) == 0) { + writeskip = 2; +// log_printf("fds.c: write mode! writeskip = 2\n"); + } + if(data & 0x40) { + diskirq = LONGIRQ; +// log_printf("fds.c: read/write start! diskirq = %d\n",diskirq); + } + control = data; + break; + + //external connector + case 0x4026: + break; + + //hlefds registers + case 0x4220: + case 0x4221: + case 0x4222: + case 0x4223: + case 0x4224: + case 0x4225: + if(hlefds) + hlefds_write(addr,data); + break; + } +} + +static void fds_reset(int hard) +{ + u8 *bios; + + //setup for fds + read_disk = fds_read_disk; + write_disk = fds_write_disk; + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + mem_setreadfunc(4, fds_read); + mem_setwritefunc(4, fds_write); + mem_setwramsize(32); + mem_setvramsize(8); + mem_setwram32(6,0); + mem_setprg8(0xE,0); + mem_setvram8(0,0); + if(hard) { + diskside = 0xFF; + } + ioenable = 0; + control = 0; + status = 0x80; + irqlatch = 0; + irqcounter = 0; + irqenable = 0; + diskirq = 0; + writeskip = 0; + diskflip = 0; + apu_setexternal(&fdssound); + + //get pointer to bios rom + bios = mem_getreadptr(0xE); + + //turn off + hlefds = 0; + + //see if we have the hlefds bios loaded + if(memcmp(bios,hleident,6) == 0) { + log_printf("fds.c: reset: hlefds bios found, version %d.%d\n",bios[6],bios[7]); + hlefds = 1; + } + else if(config_get_bool("nes.fds.hle")) { + log_printf("fds.c: reset: hle supplementing original bios\n"); + hlefds = 2; + } + + sync(); +} + +void hlefds_cpucycle2(); + +static void fds_cpucycle() +{ + if(hlefds == 2) + hlefds_intercept(); + else if(hlefds == 1) + hlefds_cpucycle(); + + //for disk flipping + if(diskflip) { + diskflip--; + if(diskflip == 0) + diskside = newdiskside; + } + + //timer irq + if((irqenable & 2) && irqcounter) { + irqcounter--; + if(irqcounter == 0) { + if(irqenable & 1) + irqcounter = irqlatch; + else { + irqenable &= 1; + } + setirq(IRQ_TIMER); +// log_printf("fds.c: IRQ! timer! cycle = %d, line = %d, frame %d\n",LINECYCLES,SCANLINE,FRAMES); + } + } + + //disk irq + if(diskirq) { + diskirq--; + if(diskirq == 0 && (control & 0x80)) { + setirq(IRQ_DISK); +// log_printf("fds.c: IRQ! disk! line = %d, cycle = %d\n",SCANLINE,LINECYCLES); + } + } +} + +static void fds_state(int mode, u8 *data) +{ + int olddiskside = diskside; + + CFG_U8(diskside); + if(diskside != olddiskside) { + diskflip = (341 * 262 / 3) * 60; //60 frames + newdiskside = diskside; + diskside = 0xFF; + } + STATE_INT(diskside); + STATE_U8(mirror); + STATE_U8(diskread); + STATE_U8(writeskip); + STATE_U8(diskirq); + STATE_U8(timerirq); + STATE_U8(irqenable); + STATE_U8(ioenable); + STATE_U8(control); + STATE_U8(status); + STATE_U16(irqcounter); + STATE_U16(irqlatch); + STATE_INT(diskaddr); + STATE_INT(diskflip); + sync(); +} + +MAPPER(B_FDS, fds_reset, 0, fds_cpucycle, fds_state); + +static int prgbase; +static u8 vectors[16] = { + 0,0,0,0, + 0x4C, 0x64, 0xE1, 0x00, 0x00, 0x00, + 0x6C, 0xFC, 0xDF, 0x00, 0x00, 0x00, +}; + +static u8 gdram[1024 * 1024]; +static u8 gdbank = 0; +static u8 gdchrbank = 0; +static u8 gdmode = 0xFF; +static u8 gdmirror = 0; +static u8 rampages[4]; //8k pages +static u8 reg4411 = 0; + +static u8 doctor_read_disk(u32 diskaddr) +{ + u8 ret = nes->cart->disk.data[(diskside * 80000) + diskaddr]; + +// log_printf("doctor read: %d = %02X\n", (diskside * 80000) + diskaddr, ret); + return(ret); +} + +static void doctor_write_disk(u32 diskaddr, u8 data) +{ + nes->cart->disk.data[(diskside * 80000) + diskaddr] = data; +} + +static void gdsync() +{ + mem_setmirroring(gdmirror); +} + +static u8 doctor_read(u32 addr) +{ + u8 ret = 0; + + if (addr < 0x4100) { + ret = fds_read(addr); + return(ret); + } + + switch (addr >> 12) { + + case 0x4: + switch (addr) { + + case 0x42FF: + break; + + case 0x43FF: + break; + case 0x4410: + break; + case 0x4411: + ret = reg4411; + break; + + case 0x4FF0: + case 0x4FF1: + case 0x4FF2: + case 0x4FF3: + case 0x4FF4: + case 0x4FF5: + case 0x4FF6: + case 0x4FF7: + case 0x4FF8: + case 0x4FF9: + case 0x4FFA: + case 0x4FFB: + case 0x4FFC: + case 0x4FFD: + case 0x4FFE: + case 0x4FFF: + ret = vectors[addr & 0xF]; + break; + } + + log_printf("doctor read: %04X = %02X\n", addr, ret); + break; + + case 0x5: + log_printf("doctor read: %04X\n", addr); + break; + + case 0x6: + case 0x7: + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xC: + case 0xD: + case 0xE: + case 0xF: + //log_printf("doctor read: %04X\n", addr); + + //bios read + if (gdmode == 0xFF) { + if (addr >= 0xE000) + ret = nes->cart->prg.data[prgbase + (addr & 0x1FFF)]; + else + ret = gdram[gdbank * 0x8000 + (addr & 0x7FFF)]; + } + + //gdram read + else { + u32 offset; + + if (addr >= 0x8000 && addr < 0xC000) { + offset = (rampages[0] * 0x4000) + (addr & 0x3FFF); + } + else { + offset = (rampages[1] * 0x4000) + (addr & 0x3FFF); + } + ret = gdram[offset]; + +/* if (addr >= 0xC000) + ret = gdram[(addr & 0x3FFF) + 0x1C000]; + else if (addr >= 0x8000) + ret = gdram[(addr & 0x3FFF) + (rampages[0] * 0x4000)];*/ + } + + break; + } + + return(ret); +} + +static void doctor_write(u32 addr, u8 data) +{ + if (addr < 0x4100) { + fds_write(addr, data); + return; + } + + switch (addr >> 12) { + + case 0x4: + switch (addr) { + + //start game? + case 0x42FF: + gdmode = data >> 5; + gdmirror = ~(data >> 4) & 1; + + rampages[0] = 0; + rampages[1] = 7; + + gdsync(); + +// doctor_write(0x8000, 0); + + { + memfile_t *mf = memfile_open("gdram.dump", "wb"); + + memfile_write(gdram, 0x8000, 8, mf); + memfile_close(mf); + +// mf = memfile_open("gdisks.dump", "wb"); +// memfile_write(nes->cart->disk.data, nes->cart->disk.size, 1, mf); +// memfile_close(mf); + } + + break; + + //selects 32kb gdram bank + case 0x43FF: + gdbank = data >> 4; + gdchrbank = data >> 4; + break; + + case 0x4410: + break; + case 0x4411: + reg4411 = data; + break; + + //enable game doctor + case 0x4FFF: + prgbase = 0x2000; + break; + } + + log_printf("doctor write: %04X = %02X\n", addr, data); + break; + + case 0x5: + log_printf("doctor write: %04X = %02X\n", addr, data); + break; + + case 0x6: + case 0x7: + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xC: + case 0xD: + case 0xE: + case 0xF: +// log_printf("doctor write: %04X = %02X (pc = %04X)\n", addr, data, nes->cpu.pc); + + switch (gdmode) { + case 0: + rampages[0] = data & 7; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + break; + default: + if (addr < 0xE000) { + if(reg4411 != 4) + gdram[gdbank * 0x8000 + (addr & 0x7FFF)] = data; + } + break; + } + break; + } +} + +static void doctor_reset(int hard) +{ + int i; + + fds_reset(hard); + for (i = 4; i < 16; i++) { + mem_unsetcpu4(i); + mem_setreadfunc(i, doctor_read); + mem_setwritefunc(i, doctor_write); + } + prgbase = 0x0000; + gdbank = 0; + gdmode = 0xFF; + read_disk = doctor_read_disk; + write_disk = doctor_write_disk; +} + +static void doctor_cpucycle() +{ + fds_cpucycle(); +} + +static void doctor_state(int mode, u8 *data) +{ + fds_state(mode, data); +} + +MAPPER(B_DOCTOR, doctor_reset, 0, doctor_cpucycle, doctor_state); diff --git a/apps/nesemu2/src/mappers/fds/fds.h b/apps/nesemu2/src/mappers/fds/fds.h new file mode 100644 index 00000000..40a4879d --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/fds.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __fds__fds_h__ +#define __fds__fds_h__ + +extern int diskside; + +#endif diff --git a/apps/nesemu2/src/mappers/fds/hle.c b/apps/nesemu2/src/mappers/fds/hle.c new file mode 100644 index 00000000..b6a11d92 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/hle.c @@ -0,0 +1,411 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "mappers/fds/fds.h" +#include "mappers/fds/calls.h" +#include "nes/nes.h" + +typedef void (*hle_call_t)(); + +typedef struct hle_call_info_s { + + //group the call belongs to (negative = disable) + s8 group; + + //hle call index ($FF = dont use) + u8 hle; + + //entry point in the bios + u16 addr; + + //name of call + char *name; + + //block to code for function (terminate with 0 or 2) + //if first byte is 0, use default method of calling (call in this code, insert rts into 6502 code) + //if last byte is 0, terminate code replacing + //if last byte is 2, call the hle function them terminate + u8 code[8]; + +} hle_call_info_t; + +static hle_call_t hle_calls[64] = { + + //$00 - disk related calls + hle_loadfiles, + hle_writefile, + hle_appendfile, + hle_getdiskinfo, + hle_checkfilecount, + hle_adjustfilecount, + hle_setfilecount1, + hle_setfilecount, + + //$08 - disk helper calls + hle_xferdone, + hle_checkdiskheader, + hle_getnumfiles, + hle_checkblocktype, + hle_filematchtest, + hle_loaddata, + 0, + 0, + + //$10 - pad calls + hle_readpads, + hle_orpads, + hle_downpads, + hle_readordownpads, + hle_readdownverifypads, + hle_readordownverifypads, + hle_readdownexppads, + hle_readkeyboard, + + //$18 - ppu vram related calls + hle_vramfill, + hle_vramstructwrite, + hle_writevrambuffer, + hle_readvrambuffer, + hle_preparevramstring, + hle_preparevramstrings, + hle_getvrambufferbyte, + hle_loadtileset, + + //$20 - ??? + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + + //$28 - ppu related calls + hle_enpf, + hle_dispf, + hle_enobj, + hle_disobj, + hle_enpfobj, + hle_dispfobj, + hle_setscroll, + hle_spritedma, + + //$30 - utility calls + hle_counterlogic, + hle_random, + hle_fetchdirectptr, + hle_jumpengine, + hle_memfill, + hle_pixel2nam, + hle_nam2pixel, + hle_gethcparam, + + //$38 - misc calls + hle_delay132, + hle_delayms, + hle_vintwait, + hle_unk_EC22, + + //$3C - vectors + hle_nmi, + hle_irq, + hle_reset, + 0, +}; + +//can be optimized: use hle call index as index of entry in this table +static hle_call_info_t hle_call_table[] = { + + //vectors + {1, 0x3C, 0xe18b, "NMI", {0x4C,0x80,0x42,2}}, //jmp $4280 + {1, 0x3D, 0xe1c7, "IRQ", {0x4C,0x88,0x42,2}}, //jmp $4288 + {1, 0x3E, 0xee24, "RESET", {0x6C,0xFC,0xDF,2}}, //jmp ($DFFC) + + //disk + {2, 0x00, 0xe1f8, "LoadFiles", {0}}, + {2, 0x02, 0xe237, "AppendFile", {0}}, + {2, 0x01, 0xe239, "WriteFile", {0}}, + {2, 0xFF, 0xe2b7, "CheckFileCount", {0}}, + {2, 0xFF, 0xe2bb, "AdjustFileCount", {0}}, + {2, 0xFF, 0xe301, "SetFileCount1", {0}}, + {2, 0xFF, 0xe305, "SetFileCount", {0}}, + {2, 0xFF, 0xe32a, "GetDiskInfo", {0}}, + + //pads + {3, 0x10, 0xe9eb, "ReadPads", {0}}, + {3, 0x11, 0xea0d, "OrPads", {0}}, + {3, 0x12, 0xea1a, "ReadDownPads", {0}}, + {3, 0x13, 0xea1f, "ReadOrDownPads", {0}}, + {3, 0x14, 0xea36, "ReadDownVerifyPads", {0}}, + {3, 0x15, 0xea4c, "ReadOrDownVerifyPads", {0}}, + {3, 0x16, 0xea68, "ReadDownExpPads", {0}}, + {3, 0x17, 0xeb13, "ReadKeyboard", {0}}, + + //vram + {4, 0x18, 0xea84, "VRAMFill", {0}}, + {4, 0x19, 0xe7bb, "VRAMStructWrite", {0}}, + {4, 0x1A, 0xe86a, "WriteVRAMBuffer", {0}}, + {4, 0x1B, 0xe8b3, "ReadVRAMBuffer", {0}}, + {4, 0x1C, 0xe8d2, "PrepareVRAMString", {0}}, + {4, 0x1D, 0xe8e1, "PrepareVRAMStrings", {0}}, + {4, 0x1E, 0xe94f, "GetVRAMBufferByte", {0}}, + {4, 0x1F, 0xebaf, "LoadTileset", {0}}, + + //ppu + {5, 0x28, 0xe185, "EnPF", {0}}, + {5, 0x29, 0xe17e, "DisPF", {0}}, + {5, 0x2A, 0xe178, "EnObj", {0}}, + {5, 0x2B, 0xe171, "DisObj", {0}}, + {5, 0x2C, 0xe16b, "EnPFObj", {0}}, + {5, 0x2D, 0xe161, "DisPFObj", {0}}, + {5, 0x2E, 0xeaea, "SetScroll", {0}}, + {5, 0x2F, 0xe9c8, "SpriteDMA", {0}}, + + //util + {6, 0x30, 0xe9d3, "CounterLogic", {0}}, + {6, 0x31, 0xe9b1, "Random", {0}}, + {6, 0x32, 0xe844, "FetchDirectPtr", {0}}, + {6, 0x33, 0xeafd, "JumpEngine", {0x4C,0x90,0x42,2}}, //jmp $4290 + {6, 0x34, 0xead2, "MemFill", {0}}, + {6, 0x35, 0xe97d, "Pixel2NamConv", {0}}, + {6, 0x36, 0xe997, "Nam2PixelConv", {0}}, + {6, 0xFF, 0xe3e7, "GetHCPwNWPchk", {0}}, + {6, 0xFF, 0xe3ea, "GetHCPwWPchk", {0}}, + {6, 0x37, 0xe3eb, "GetHCParam", {0}}, + + //misc + {7, 0x38, 0xe149, "Delay132", {0}}, + {7, 0x39, 0xe153, "Delayms", {0}}, + {7, 0x3A, 0xe1b2, "VINTWait", {0xD0,0xFE,2}}, //bne forever + {-7, 0x3B, 0xec22, "unk_EC22", {0}}, + + //disk helpers + {8, 0x08, 0xe778, "XferDone", {0}}, + {8, 0x09, 0xe445, "CheckDiskHeader", {0}}, + {8, 0x0A, 0xe484, "GetNumFiles", {0}}, + {8, 0x0B, 0xe68f, "CheckBlkType", {0}}, + {8, 0x0C, 0xe4a0, "FileMatchTest", {0}}, + {8, 0x0D, 0xe4f9, "LoadData", {0}}, + + //end + {-1, 0xFF, 0x0000, 0, {0}}, + +}; + +//four hle registers +u8 hleregs[4]; + +//128 bytes of temporary memory +u8 hlemem[128]; + +//log hle calls +int hlelog = 1; + +//get parameter from the bytes after the jsr +//n = parameter index (0 is the first, 1 is the second, etc) +u32 hle_getparam(int n) +{ + u32 tmp,retaddr,addr; + + //current stack address + tmp = 0x100 + nes->cpu.sp; + + //get the return address off the stack + retaddr = cpu_read(tmp + 1); + retaddr |= cpu_read(tmp + 2) << 8; + + //offset to required address + retaddr += n * 2; + + //read input parameter + addr = cpu_read(retaddr + 1); + addr |= cpu_read(retaddr + 2) << 8; + return(addr); +} + +//fixup return address on stack +//n = number of parameters call used +void hle_fixretaddr(int n) +{ + u32 tmp,retaddr; + + //current stack address + tmp = 0x100 + nes->cpu.sp; + + //get the return address off the stack + retaddr = cpu_read(tmp + 1); + retaddr |= cpu_read(tmp + 2) << 8; + + //offset the return address + retaddr += n * 2; + + //write new return address to the stack + cpu_write(tmp + 1,(u8)retaddr); + cpu_write(tmp + 2,(u8)(retaddr >> 8)); +} + +u8 hlefds_read(u32 addr) +{ +// log_hle("hlefds_read: register read! $%04X\n",addr); + + //return from the registers + switch(addr) { + case 0x4222: + case 0x4223: + case 0x4224: + case 0x4225: + return(hleregs[addr - 0x4222]); + } + + //read from temporary memory + if(addr >= 0x4280 && addr < 0x4300) + return(hlemem[addr & 0x7F]); + + //not handled + return(0); +} + +void hlefds_write(u32 addr,u8 data) +{ + hle_call_t func = 0; + //int index = -1; + +// log_hle("hlefds_write: register write! $%04X = $%02X ($%02X $%02X $%02X $%02X)\n",addr,data,hleregs[0],hleregs[1],hleregs[2],hleregs[3]); + switch(addr) { + case 0x4220: + data = hleregs[0]; + case 0x4221: + data &= 0x3F; + if((func = hle_calls[data]) == 0) { + log_printf("hlefds_write: hle call $%02X not implemented yet\n",data); + return; + } + func(); + break; + case 0x4222: + case 0x4223: + case 0x4224: + case 0x4225: + hleregs[addr - 0x4222] = data; + return; + } + + //write to temporary memory + if(addr >= 0x4280 && addr < 0x4300) { + log_printf("hlefds_write: temporary memory is read-only!\n"); +// hlemem[addr & 0x7F] = data; + } +} + +#define READPAGE(addr) nes->cpu.readpages[(addr) >> 10][(addr) & 0x3FF] + +extern int showdisasm; + +//intercept calls into the real fds bios and replace with the hle calls +void hlefds_intercept() +{ + static u16 lastopaddr = 0; + int i,j; + u8 byte; + +// showdisasm = 1; + + //if the opaddr changes, we are reading an opcode + if(nes->cpu.opaddr >= 0xE000 && nes->cpu.opaddr != lastopaddr) { + + //save op address + lastopaddr = nes->cpu.opaddr; + + for(i=0;hle_call_table[i].name;i++) { + + //if this call is disabled, skip it + if(hle_call_table[i].group < 0) + continue; + + //check for match and if we have matching hle function + if(nes->cpu.opaddr == hle_call_table[i].addr && hle_call_table[i].hle != 0xFF) { + + log_printf("intercepting at $%04X (%s) with hle call $%02X\n",nes->cpu.opaddr,hle_call_table[i].name,hle_call_table[i].hle); + + //if first byte of 'code' is 0, use default calling method + if(hle_call_table[i].code[0] == 0) { + + //insert NOP + RTS + READPAGE(nes->cpu.opaddr+0) = 0xEA; + READPAGE(nes->cpu.opaddr+1) = 0x60; + + //call the hle function + hlefds_write(0x4221,hle_call_table[i].hle); + } + + //insert code for call + else { + + //NOP + READPAGE(nes->cpu.opaddr) = 0xEA; + + //copy the code data + for(j=0;;j++) { + byte = hle_call_table[i].code[j]; + if(byte == 0) + break; + else if(byte == 2) { + hlefds_write(0x4221,hle_call_table[i].hle); + break; + } + READPAGE(nes->cpu.opaddr + 1 + j) = byte; + } + } + + //done + return; + } + } + } +} + +void hlefds_cpucycle() +{ + static u16 lastopaddr = 0; + int i,found = 0; + +// showdisasm = 1; + if(nes->cpu.opaddr >= 0xE000 && lastopaddr < 0xE000) { + for(i=0;hle_call_table[i].name;i++) { + if(hle_call_table[i].addr == nes->cpu.opaddr) { + int t = hle_call_table[i].group; + char *types[9] = {"???","VECTOR","DISK","PAD","PPU","VRAM","UTIL","MISC","DISK2"}; + + t = abs(t); + t = (t > 8) ? 0 : t; + found = 1; + log_printf("hle.c: bios %s: calling $%04X ('%s') (pixel %d, line %d, frame %d) (A:%02X X:%02X Y:%02X SP:%02X)\n",types[t],hle_call_table[i].addr,hle_call_table[i].name,LINECYCLES,SCANLINE,FRAMES,nes->cpu.a,nes->cpu.x,nes->cpu.y,nes->cpu.sp); + } + } + if(found == 0) { + log_printf("hle.c: bios: calling $%04X (unknown) from $%04X (pixel %d, line %d, frame %d) (A:%02X X:%02X Y:%02X SP:%02X)\n",nes->cpu.opaddr,lastopaddr,LINECYCLES,SCANLINE,FRAMES,nes->cpu.a,nes->cpu.x,nes->cpu.y,nes->cpu.sp); + } + } + lastopaddr = nes->cpu.opaddr; +} diff --git a/apps/nesemu2/src/mappers/fds/hle.h b/apps/nesemu2/src/mappers/fds/hle.h new file mode 100644 index 00000000..04f6a313 --- /dev/null +++ b/apps/nesemu2/src/mappers/fds/hle.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __fds__hle_h__ +#define __fds__hle_h__ + +#define log_hle if(hlelog) log_printf + +extern u8 hleregs[]; +extern u8 hlemem[]; +extern int hlelog; + +//helper functions +u32 hle_getparam(int n); +void hle_fixretaddr(int n); + +//nes mapper functions +u8 hlefds_read(u32 addr); +void hlefds_write(u32 addr,u8 data); +void hlefds_cpucycle(); + +//for replacing real bios calls with the hle calls +void hlefds_intercept(); + +#endif diff --git a/apps/nesemu2/src/mappers/ines.c b/apps/nesemu2/src/mappers/ines.c new file mode 100644 index 00000000..ff55b47c --- /dev/null +++ b/apps/nesemu2/src/mappers/ines.c @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mapperinc.h" + +struct ines_boardid_s { + int num; + int boardid; +}; + +#define INES_BOARD_START() static struct ines_boardid_s boards[] = { +#define INES_BOARD_END() {0,-1}}; +#define INES_BOARD(n,b) {n,b}, + +INES_BOARD_START() + INES_BOARD(0, B_NINTENDO_NROM) + INES_BOARD(1, B_NINTENDO_SxROM) + INES_BOARD(2, B_NINTENDO_UxROM) + INES_BOARD(3, B_NINTENDO_CNROM) + INES_BOARD(4, B_NINTENDO_TxROM) + INES_BOARD(5, B_NINTENDO_ExROM) + INES_BOARD(7, B_NINTENDO_AxROM) + INES_BOARD(9, B_NINTENDO_PxROM) + INES_BOARD(10, B_NINTENDO_FxROM) + INES_BOARD(11, B_COLORDREAMS) + INES_BOARD(12, B_REXSOFT_DBZ5) + INES_BOARD(13, B_NINTENDO_CPROM) + INES_BOARD(15, B_BMC_CONTRA_FUNCTION_16) + INES_BOARD(18, B_JALECO_SS88006) + INES_BOARD(19, B_NAMCOT_163) + INES_BOARD(21, B_KONAMI_VRC4A) + INES_BOARD(22, B_KONAMI_VRC2A) + INES_BOARD(23, B_KONAMI_VRC2B) + INES_BOARD(24, B_KONAMI_VRC6A) + INES_BOARD(25, B_KONAMI_VRC4B) + INES_BOARD(26, B_KONAMI_VRC6B) + INES_BOARD(28, B_TEPPLES) + INES_BOARD(33, B_TAITO_TC0190FMC) + INES_BOARD(34, B_NINTENDO_BxROM) + INES_BOARD(36, B_TXC_STRIKEWOLF) + INES_BOARD(37, B_NINTENDO_PAL_ZZ) + INES_BOARD(38, B_UNL_BITCORP) + INES_BOARD(41, B_NTDEC_CALTRON6IN1) + INES_BOARD(42, B_BTL_BIOMIRACLEA) + INES_BOARD(44, B_BMC_BIG7IN1) + INES_BOARD(45, B_BMC_SUPERHIKXIN1) + INES_BOARD(46, B_RUMBLESTATION) + INES_BOARD(47, B_NINTENDO_QJ) + INES_BOARD(48, B_TAITO_TC0190FMC_PAL16R4) + INES_BOARD(49, B_BMC_SUPERHIK4IN1) + INES_BOARD(50, B_BTL_SMB2B) + INES_BOARD(51, B_BMC_BALLGAMES11IN1) + INES_BOARD(52, B_BMC_MARIO7IN1) + INES_BOARD(56, B_KAISER_KS202) + INES_BOARD(57, B_BMC_SUPERGK) + INES_BOARD(60, B_BMC_RESET4IN1) + INES_BOARD(62, B_BMC_SUPER700IN1) + INES_BOARD(64, B_TENGEN_RAMBO_1) + INES_BOARD(65, B_IREM_H3001) + INES_BOARD(66, B_NINTENDO_GxROM_MxROM) + INES_BOARD(67, B_SUNSOFT_3) + INES_BOARD(68, B_SUNSOFT_4) + INES_BOARD(69, B_SUNSOFT_5B) + INES_BOARD(71, B_CAMERICA_BF9093) + INES_BOARD(73, B_KONAMI_VRC3) + INES_BOARD(75, B_KONAMI_VRC1) + INES_BOARD(78, B_JALECO_JF16) + INES_BOARD(79, B_AVE_NINA_006) + INES_BOARD(80, B_TAITO_X1_005) + INES_BOARD(82, B_TAITO_X1_017) + INES_BOARD(85, B_KONAMI_VRC7A) + INES_BOARD(88, B_NAMCOT_34x3) + INES_BOARD(89, B_SUNSOFT_2) + INES_BOARD(90, B_JYCOMPANY) + INES_BOARD(93, B_SUNSOFT_2) + INES_BOARD(94, B_NINTENDO_UN1ROM) + INES_BOARD(95, B_NAMCOT_3425) + INES_BOARD(97, B_IREM_TAM_S1) + INES_BOARD(101, B_UNL_UY) + INES_BOARD(104, B_CAMERICA_GOLDENFIVE) + INES_BOARD(105, B_NINTENDO_EVENT) + INES_BOARD(107, B_MAGICSERIES) + INES_BOARD(112, B_NTDEC_ASDER) + INES_BOARD(113, B_HES) + INES_BOARD(114, B_UNL_SUPERLIONKING) + INES_BOARD(115, B_KASING) + INES_BOARD(116, B_REXSOFT_SL1632) + INES_BOARD(119, B_NINTENDO_TQROM) +// INES_BOARD(123, B_UNL_H2288) + INES_BOARD(132, B_TXC_22211A) + INES_BOARD(133, B_SA_72008) //?? + INES_BOARD(137, B_SACHEN_8259D) + INES_BOARD(138, B_SACHEN_8259B) + INES_BOARD(139, B_SACHEN_8259C) + INES_BOARD(140, B_JALECO_JF11) + INES_BOARD(141, B_SACHEN_8259A) + INES_BOARD(143, B_SACHEN_TCA01) + INES_BOARD(144, B_DEATHRACE) + INES_BOARD(145, B_SA_72007) + INES_BOARD(146, B_SA_0161M) + INES_BOARD(147, B_SACHEN_TCU01) + INES_BOARD(148, B_SA_0037) + INES_BOARD(149, B_SA_0036) +// INES_BOARD(150, B_SACHEN_74LS374N) +// INES_BOARD(150, B_SA_72008) + INES_BOARD(154, B_NAMCOT_3453) + INES_BOARD(155, B_NINTENDO_SxROM_MMC1A) + INES_BOARD(156, B_OPENCORP) + INES_BOARD(162, B_UNL_FS304) + INES_BOARD(163, B_NANJING) + INES_BOARD(164, B_WAIXING_FFV) + INES_BOARD(165, B_WAIXING_SH2) + INES_BOARD(168, B_UNL_RACERMATE) + INES_BOARD(172, B_TXC_22211B) + INES_BOARD(173, B_TXC_22211C) + INES_BOARD(180, B_NINTENDO_UNROM_74HC08) + INES_BOARD(182, B_HOSENKAN) + INES_BOARD(184, B_SUNSOFT_1) + INES_BOARD(185, B_NINTENDO_CNROM_CP) + INES_BOARD(189, B_TXC_TW) + INES_BOARD(193, B_NTDEC_TC112) + INES_BOARD(194, B_WAIXING_TYPE_D) + INES_BOARD(200, B_BMC_36IN1) + INES_BOARD(201, B_BMC_21IN1) + INES_BOARD(202, B_BMC_150IN1) + INES_BOARD(203, B_BMC_35IN1) + INES_BOARD(205, B_BMC_15IN1) + INES_BOARD(206, B_NINTENDO_DxROM) + INES_BOARD(207, B_TAITO_X1_005A) + INES_BOARD(209, B_JYCOMPANY_NTCTL) + INES_BOARD(211, B_JYCOMPANY_NTCTL) + INES_BOARD(218, B_MAGICFLOOR) + INES_BOARD(225, B_BMC_72IN1) + INES_BOARD(226, B_BMC_76IN1) + INES_BOARD(227, B_BMC_1200IN1) + INES_BOARD(228, B_ACTIVE) + INES_BOARD(229, B_BMC_31IN1) + INES_BOARD(230, B_BMC_22IN1) + INES_BOARD(231, B_BMC_20IN1) + INES_BOARD(232, B_CAMERICA_BF9096) + INES_BOARD(233, B_BMC_SUPER42IN1) + INES_BOARD(234, B_AVE_D1012) + INES_BOARD(235, B_BMC_GOLDENGAME260IN1) + INES_BOARD(240, B_CNE_SHLZ) + INES_BOARD(241, B_TXC_MXMDHTWO) + INES_BOARD(242, B_WAIXING_ZS) + INES_BOARD(243, B_SACHEN_74LS374N) + INES_BOARD(244, B_CNE_DECATHLON) + INES_BOARD(245, B_WAIXING_TYPE_H) + INES_BOARD(246, B_CNE_FSB) + INES_BOARD(250, B_NITRA) + INES_BOARD(252, B_WAIXING_SGZ) + INES_BOARD(254, B_BTL_PIKACHUY2K) +INES_BOARD_END() + +int mapper_get_mapperid_ines(int num) +{ + int i; + + for(i=0;boards[i].boardid != -1;i++) { + if(num == boards[i].num) + return(boards[i].boardid); + } + return(B_UNSUPPORTED); +} diff --git a/apps/nesemu2/src/mappers/ines20.c b/apps/nesemu2/src/mappers/ines20.c new file mode 100644 index 00000000..393f12d2 --- /dev/null +++ b/apps/nesemu2/src/mappers/ines20.c @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mapperinc.h" + +struct ines20_boardid_s { + int num,sub; + int boardid; +}; + +#define INES20_BOARD_START() static struct ines20_boardid_s boards[] = { +#define INES20_BOARD_END() {0,0,-1}}; +#define INES20_BOARD(n,s,b) {n,s,b}, + +INES20_BOARD_START() + INES20_BOARD(0, 1, B_NINTENDO_NROM_SRAM) //nrom + save ram + INES20_BOARD(0, 15, B_NES_DISK_SYSTEM) + INES20_BOARD(4, 1, B_NINTENDO_HxROM) + INES20_BOARD(21, 9, B_KONAMI_VRC4A) + INES20_BOARD(21, 14, B_KONAMI_VRC4C) + INES20_BOARD(23, 10, B_KONAMI_VRC4E) + INES20_BOARD(25, 1, B_KONAMI_VRC4B) + INES20_BOARD(25, 3, B_KONAMI_VRC4D) + INES20_BOARD(34, 1, B_AVE_NINA_001) +// INES20_BOARD(34, 2, B_UNION_BOND) + INES20_BOARD(71, 1, B_CAMERICA_BF9097) + INES20_BOARD(78, 3, B_IREM_HOLYDIVER) +INES20_BOARD_END() + +int mapper_get_mapperid_ines20(int num,int sub) +{ + int i; + + //no submapper, just use the ines function + if(sub == 0) { + return(mapper_get_mapperid_ines(num)); + } + for(i=0;boards[i].boardid != -1;i++) { + if((num == boards[i].num) && (sub == boards[i].sub)) + return(boards[i].boardid); + } + return(B_UNSUPPORTED); +} diff --git a/apps/nesemu2/src/mappers/mapperid.h b/apps/nesemu2/src/mappers/mapperid.h new file mode 100644 index 00000000..7cb2cfd1 --- /dev/null +++ b/apps/nesemu2/src/mappers/mapperid.h @@ -0,0 +1,268 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mapperid_h__ +#define __mapperid_h__ + +enum boardid_e { + + //unsupported board + B_UNSUPPORTED = -1, + + //licensed + //nintendo + B_NINTENDO_NROM = 0, + B_NINTENDO_AxROM, + B_NINTENDO_BxROM, + B_NINTENDO_CNROM, + B_NINTENDO_CNROM_CP, + B_NINTENDO_CPROM, + B_NINTENDO_DxROM, + B_NINTENDO_ExROM, + B_NINTENDO_FxROM, + B_NINTENDO_GxROM_MxROM, + B_NINTENDO_HxROM, + B_NINTENDO_PxROM, + B_NINTENDO_SxROM_MMC1A, + B_NINTENDO_SxROM_MMC1B, + B_NINTENDO_SxROM_MMC1C, + B_NINTENDO_TxROM, + B_NINTENDO_UxROM, + B_NINTENDO_TQROM, + B_NINTENDO_UN1ROM, + B_NINTENDO_UNROM_74HC08, + B_NINTENDO_EVENT, + B_NINTENDO_QJ, + B_NINTENDO_PAL_ZZ, + B_NINTENDO_NROM_SRAM, + + //konami + B_KONAMI_VRC1, + B_KONAMI_VRC2A, + B_KONAMI_VRC2B, + B_KONAMI_VRC2C, + B_KONAMI_VRC3, + B_KONAMI_VRC4A, + B_KONAMI_VRC4B, + B_KONAMI_VRC4C, + B_KONAMI_VRC4D, + B_KONAMI_VRC4E, + B_KONAMI_VRC6A, + B_KONAMI_VRC6B, + B_KONAMI_VRC7A, + B_KONAMI_VRC7B, + + //jaleco + B_JALECO_SS88006, + B_JALECO_JF11, + B_JALECO_JF16, + + //namcot + B_NAMCOT_3425, + B_NAMCOT_34x3, + B_NAMCOT_3446, + B_NAMCOT_3453, + B_NAMCOT_129, + B_NAMCOT_163, + B_NAMCOT_175, + B_NAMCOT_340, + + //bandai + B_BANDAI_KARAOKE, + + //irem + B_IREM_H3001, + B_IREM_TAM_S1, + B_IREM_HOLYDIVER, + + //taito + B_TAITO_X1_017, + B_TAITO_X1_005, + B_TAITO_X1_005A, + B_TAITO_TC0190FMC, + B_TAITO_TC0190FMC_PAL16R4, + + //sunsoft + B_SUNSOFT_1, + B_SUNSOFT_2, + B_SUNSOFT_3, + B_SUNSOFT_4, + B_SUNSOFT_5B, + + //bootleg multicarts + B_BMC_15IN1, + B_BMC_36IN1, + B_BMC_70IN1, + B_BMC_70IN1B, + B_BMC_BIG7IN1, + B_BMC_FK23C, + B_BMC_CONTRA_FUNCTION_16, + B_BMC_MARIO7IN1, + B_BMC_SUPER700IN1, + B_BMC_76IN1, + B_BMC_72IN1, + B_BMC_21IN1, + B_BMC_35IN1, + B_BMC_150IN1, + B_BMC_SUPERHIK4IN1, + B_BMC_SUPERHIKXIN1, + B_BMC_BALLGAMES11IN1, + B_BMC_SUPER42IN1, + B_BMC_1200IN1, + B_BMC_20IN1, + B_BMC_22IN1, + B_BMC_31IN1, + B_BMC_RESET4IN1, + B_BMC_65IN1, + B_BMC_GOLDENGAME260IN1, + B_BMC_SUPERGK, + + //bootlegs + B_BTL_BIOMIRACLEA, + B_BTL_MARIO1_MALEE2, + B_BTL_SMB2B, + B_BTL_PIKACHUY2K, + + //unlicensed + B_UNL_H2288, + B_UNL_RACERMATE, + B_UNL_UY, + B_UNL_SUPERLIONKING, + B_UNL_BITCORP, + B_UNL_FS304, + + //colordreams + B_COLORDREAMS, + B_DEATHRACE, + + //others + B_MAGICSERIES, + B_RUMBLESTATION, + B_HES, + B_ACTIVE, + B_OPENCORP, + B_NITRA, + B_KASING, + B_NANJING, + B_HOSENKAN, + B_JYCOMPANY, + B_JYCOMPANY_NTCTL, + B_COOLBOY, + + //c&e + B_CNE_FSB, + B_CNE_SHLZ, + B_CNE_DECATHLON, + + //colordreams + few others + B_74x377, + + //tengen + B_TENGEN_MIMIC_1, + B_TENGEN_RAMBO_1, + + //txc corporation + B_TXC_22211A, + B_TXC_22211B, + B_TXC_22211C, + B_TXC_MXMDHTWO, + B_TXC_TW, + B_TXC_STRIKEWOLF, + + //sachen + B_SACHEN_TCA01, + B_SACHEN_TCU01, + B_SACHEN_74LS374N, + B_SACHEN_74LS374NA, + B_SACHEN_8259A, + B_SACHEN_8259B, + B_SACHEN_8259C, + B_SACHEN_8259D, + B_SA_0036, + B_SA_0037, + B_SA_72007, + B_SA_72008, + B_SA_0161M, + + //subor + B_SUBOR_STUDYGAME, + + //camerica + B_CAMERICA_BF9093, + B_CAMERICA_BF9096, + B_CAMERICA_BF9097, + B_CAMERICA_GOLDENFIVE, + + //ffe + B_FFE_MAPPER6, + B_FFE_MAPPER8, + B_FFE_MAPPER17, + + //ave + B_AVE_NINA_001, + B_AVE_NINA_006, + B_AVE_D1012, + + //kaiser + B_KAISER_KS202, + + //waixing + B_WAIXING_SGZ, + B_WAIXING_FFV, + B_WAIXING_SH2, + B_WAIXING_ZS, + B_WAIXING_TYPE_D, + B_WAIXING_TYPE_H, + + //ntdec + B_NTDEC_TC112, + B_NTDEC_CALTRON6IN1, + B_NTDEC_ASDER, + + //rexsoft + B_REXSOFT_DBZ5, + B_REXSOFT_SL1632, + + //homebrew + B_TEPPLES, + B_DRIPGAME, + B_MAGICFLOOR, + B_NES_DISK_SYSTEM, + + //for getting a count of supported boards + B_BOARDEND, + + //mapping for default chips used + B_NINTENDO_SxROM = B_NINTENDO_SxROM_MMC1B, + B_KONAMI_VRC2 = B_KONAMI_VRC2B, + B_KONAMI_VRC4 = B_KONAMI_VRC4B, + + //extra support + B_NSF = 0x10000000, + B_FDS = 0x20000000, + B_HLEFDS = B_FDS | 1, + B_DOCTOR = B_FDS | 2, + B_DRPCJR = 0x30000000, + B_GENIE = 0x40000000, + B_TYPEMASK = 0xF0000000, + +}; + +#endif diff --git a/apps/nesemu2/src/mappers/mapperinc.h b/apps/nesemu2/src/mappers/mapperinc.h new file mode 100644 index 00000000..1a19b88a --- /dev/null +++ b/apps/nesemu2/src/mappers/mapperinc.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mapperinc_h__ +#define __mapperinc_h__ + +#define C_MMCNUM 0xF0 +#define C_MMCREV 0x0F + +#define MAPPER(boardid,reset,ppucycle,cpucycle,state) \ + mapper_t mapper##boardid = {boardid,reset,ppucycle,cpucycle,state} + +#include "mappers/mappers.h" +#include "mappers/mapperid.h" +#include "nes/nes.h" +#include "nes/memory.h" +#include "nes/state/state.h" +#include "misc/log.h" + +#endif diff --git a/apps/nesemu2/src/mappers/mappers.c b/apps/nesemu2/src/mappers/mappers.c new file mode 100644 index 00000000..739a939f --- /dev/null +++ b/apps/nesemu2/src/mappers/mappers.c @@ -0,0 +1,265 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mapperinc.h" + +#ifdef MAPPER + #undef MAPPER +#endif + +#define MAPPER(n) { \ + extern mapper_t mapper##n; \ + if(mapperid == n) \ + return(&mapper##n); \ + } + +static mapper_t *get_mapper(int mapperid) +{ + //licensed + //nintendo boards + MAPPER(B_NINTENDO_AxROM); + MAPPER(B_NINTENDO_BxROM); + MAPPER(B_NINTENDO_CNROM); + MAPPER(B_NINTENDO_CNROM_CP); + MAPPER(B_NINTENDO_CPROM); + MAPPER(B_NINTENDO_DxROM); + MAPPER(B_NINTENDO_ExROM); + MAPPER(B_NINTENDO_FxROM); + MAPPER(B_NINTENDO_GxROM_MxROM); + MAPPER(B_NINTENDO_HxROM); + MAPPER(B_NINTENDO_NROM); + MAPPER(B_NINTENDO_NROM_SRAM); + MAPPER(B_NINTENDO_PxROM); + MAPPER(B_NINTENDO_SxROM_MMC1A); + MAPPER(B_NINTENDO_SxROM_MMC1B); + MAPPER(B_NINTENDO_SxROM_MMC1C); + MAPPER(B_NINTENDO_TxROM); + MAPPER(B_NINTENDO_TQROM); + MAPPER(B_NINTENDO_UxROM); + MAPPER(B_NINTENDO_UN1ROM); + MAPPER(B_NINTENDO_UNROM_74HC08); + MAPPER(B_NINTENDO_EVENT); + MAPPER(B_NINTENDO_QJ); + MAPPER(B_NINTENDO_PAL_ZZ); + + //konami + MAPPER(B_KONAMI_VRC1); + MAPPER(B_KONAMI_VRC2A); + MAPPER(B_KONAMI_VRC2B); + MAPPER(B_KONAMI_VRC2C); + MAPPER(B_KONAMI_VRC3); + MAPPER(B_KONAMI_VRC4A); + MAPPER(B_KONAMI_VRC4B); + MAPPER(B_KONAMI_VRC4C); + MAPPER(B_KONAMI_VRC4D); + MAPPER(B_KONAMI_VRC4E); + MAPPER(B_KONAMI_VRC6A); + MAPPER(B_KONAMI_VRC6B); + MAPPER(B_KONAMI_VRC7A); + MAPPER(B_KONAMI_VRC7B); + + //taito + MAPPER(B_TAITO_X1_017); + MAPPER(B_TAITO_X1_005); + MAPPER(B_TAITO_X1_005A); + MAPPER(B_TAITO_TC0190FMC); + + //irem + MAPPER(B_IREM_H3001); + MAPPER(B_IREM_TAM_S1); + MAPPER(B_IREM_HOLYDIVER); + + //jaleco + MAPPER(B_JALECO_SS88006); + MAPPER(B_JALECO_JF11); + MAPPER(B_JALECO_JF16); + + //namcot + MAPPER(B_NAMCOT_34x3); + MAPPER(B_NAMCOT_3453); + MAPPER(B_NAMCOT_3425); +// MAPPER(B_NAMCOT_129); + MAPPER(B_NAMCOT_163); +// MAPPER(B_NAMCOT_175); +// MAPPER(B_NAMCOT_340); + + //sunsoft + MAPPER(B_SUNSOFT_1); + MAPPER(B_SUNSOFT_2); + MAPPER(B_SUNSOFT_3); + MAPPER(B_SUNSOFT_4); + MAPPER(B_SUNSOFT_5B); + + //unlicensed + //tengen + MAPPER(B_TENGEN_RAMBO_1); + MAPPER(B_TENGEN_MIMIC_1); + + //camerica + MAPPER(B_CAMERICA_BF9093); + MAPPER(B_CAMERICA_BF9096); + MAPPER(B_CAMERICA_BF9097); + MAPPER(B_CAMERICA_GOLDENFIVE); + + //txc + MAPPER(B_TXC_22211A); + MAPPER(B_TXC_22211B); + MAPPER(B_TXC_22211C); + MAPPER(B_TXC_TW); + MAPPER(B_TXC_MXMDHTWO); + MAPPER(B_TXC_STRIKEWOLF); + + //sachen + MAPPER(B_SACHEN_TCA01); + MAPPER(B_SACHEN_TCU01); + MAPPER(B_SACHEN_74LS374N); +// MAPPER(B_SACHEN_74LS374NA); + MAPPER(B_SACHEN_8259A); + MAPPER(B_SACHEN_8259B); + MAPPER(B_SACHEN_8259C); + MAPPER(B_SACHEN_8259D); + MAPPER(B_SA_72007); + MAPPER(B_SA_72008); + MAPPER(B_SA_0036); + MAPPER(B_SA_0037); + MAPPER(B_SA_0161M); + + //ave + MAPPER(B_AVE_NINA_001); + MAPPER(B_AVE_NINA_006); + MAPPER(B_AVE_D1012); + + //cne + MAPPER(B_CNE_FSB); + MAPPER(B_CNE_SHLZ); + MAPPER(B_CNE_DECATHLON); + + //kaiser + MAPPER(B_KAISER_KS202); + + //waixing + MAPPER(B_WAIXING_SGZ); + MAPPER(B_WAIXING_FFV); + MAPPER(B_WAIXING_SH2); + MAPPER(B_WAIXING_ZS); + MAPPER(B_WAIXING_TYPE_D); + MAPPER(B_WAIXING_TYPE_H); + + //ntdec + MAPPER(B_NTDEC_TC112); + MAPPER(B_NTDEC_CALTRON6IN1); + MAPPER(B_NTDEC_ASDER); + + //rexsoft + MAPPER(B_REXSOFT_DBZ5); + MAPPER(B_REXSOFT_SL1632); + + //other (bootleg multicarts) + MAPPER(B_BMC_15IN1); + MAPPER(B_BMC_36IN1); + MAPPER(B_BMC_70IN1); + MAPPER(B_BMC_70IN1B); + MAPPER(B_BMC_BIG7IN1); + MAPPER(B_BMC_FK23C); + MAPPER(B_BMC_CONTRA_FUNCTION_16); + MAPPER(B_BMC_MARIO7IN1); + MAPPER(B_BMC_SUPER700IN1); + MAPPER(B_BMC_76IN1); + MAPPER(B_BMC_72IN1); + MAPPER(B_BMC_21IN1); + MAPPER(B_BMC_35IN1); + MAPPER(B_BMC_150IN1); + MAPPER(B_BMC_SUPERHIK4IN1); + MAPPER(B_BMC_SUPERHIKXIN1); + MAPPER(B_BMC_BALLGAMES11IN1); + MAPPER(B_BMC_SUPER42IN1); + MAPPER(B_BMC_1200IN1); + MAPPER(B_BMC_20IN1); + MAPPER(B_BMC_22IN1); + MAPPER(B_BMC_31IN1); + MAPPER(B_BMC_RESET4IN1); + MAPPER(B_BMC_65IN1); + MAPPER(B_BMC_GOLDENGAME260IN1); + MAPPER(B_BMC_SUPERGK); + + //other (bootlegs) + MAPPER(B_BTL_MARIO1_MALEE2); + MAPPER(B_BTL_BIOMIRACLEA); + MAPPER(B_BTL_SMB2B); + MAPPER(B_BTL_PIKACHUY2K); + + //other (unlicensed) + MAPPER(B_UNL_H2288); + MAPPER(B_UNL_RACERMATE); + MAPPER(B_UNL_UY); + MAPPER(B_UNL_SUPERLIONKING); + MAPPER(B_UNL_BITCORP); + MAPPER(B_UNL_FS304); + + //colordreams + MAPPER(B_COLORDREAMS); + MAPPER(B_DEATHRACE); + + //others + MAPPER(B_MAGICSERIES); + MAPPER(B_RUMBLESTATION); + MAPPER(B_HES); + MAPPER(B_ACTIVE); + MAPPER(B_OPENCORP); + MAPPER(B_NITRA); + MAPPER(B_KASING); + MAPPER(B_NANJING); + MAPPER(B_HOSENKAN); + MAPPER(B_JYCOMPANY); + MAPPER(B_JYCOMPANY_NTCTL); + MAPPER(B_COOLBOY); + + //homebrew + MAPPER(B_TEPPLES); + MAPPER(B_DRIPGAME); + MAPPER(B_MAGICFLOOR); + MAPPER(B_NES_DISK_SYSTEM); + + //others + MAPPER(B_FDS); + MAPPER(B_NSF); + MAPPER(B_DOCTOR); + + return(0); +} + +static void null_mapper_cycle() {} +static void null_mapper_state(int m,u8 *d){} + +#define check_null(var,nullfunc) var = ((var == 0) ? nullfunc : var) + +mapper_t *mapper_init(int mapperid) +{ + mapper_t *ret = get_mapper(mapperid); + + if(ret == 0) { + log_printf("mapper_init: get_mapper() failed! mapperid = %d\n",mapperid); + return(0); + } + check_null(ret->ppucycle, null_mapper_cycle); + check_null(ret->cpucycle, null_mapper_cycle); + check_null(ret->state, null_mapper_state); + return(ret); +} diff --git a/apps/nesemu2/src/mappers/mappers.h b/apps/nesemu2/src/mappers/mappers.h new file mode 100644 index 00000000..9abbcf49 --- /dev/null +++ b/apps/nesemu2/src/mappers/mappers.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __mappers_h__ +#define __mappers_h__ + +#include "types.h" + +typedef struct mapper_s { + int boardid; //internal board id + void (*reset)(int); //mapper reset function + void (*ppucycle)(); //ppu cycle handler + void (*cpucycle)(); //cpu cycle handler + void (*state)(int,u8*); //load/save state information +} mapper_t; + +//converting from ines|ines20|unif to internal board id +int mapper_get_mapperid_ines(int num); +int mapper_get_mapperid_ines20(int num,int sub); +int mapper_get_mapperid_unif(char *board); + +//helper for getting supported mapper list +const char *mapper_get_unif_boardname(int idx); + +//initialize mapper and get mapper_t struct +mapper_t *mapper_init(int mapperid); + +#endif diff --git a/apps/nesemu2/src/mappers/nsf/nsf.c b/apps/nesemu2/src/mappers/nsf/nsf.c new file mode 100644 index 00000000..4b7153ea --- /dev/null +++ b/apps/nesemu2/src/mappers/nsf/nsf.c @@ -0,0 +1,287 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "mappers/mapperinc.h" +#include "mappers/sound/s_FDS.h" +#include "mappers/sound/s_FME7.h" +#include "mappers/sound/s_MMC5.h" +#include "mappers/sound/s_N106.h" +#include "mappers/sound/s_VRC6.h" +#include "mappers/sound/s_VRC7.h" +#include "misc/log.h" + +static readfunc_t read4; +static writefunc_t write4; +static readfunc_t oldread; +static writefunc_t oldwrite; +static u8 *bios,*biosbank; +static u8 ram[0x40]; +static u32 playspeed; +static u32 irqcounter,irqlatch; +static u8 irqenable; + +static void sound_init() +{ + FDSsound_Load(); + FME7sound_Load(); + MMC5sound_Load(); + N106sound_Load(); + VRC6sound_Load(); + VRC7sound_Load(); +} + +static u8 sound_read(u32 addr) +{ + switch(addr & 0xF000) { + case 0x4000: + if(addr < 0x4020) + return(read4(addr)); + break; + } + log_printf("nsf.c: sound read $%04X\n",addr); + return(0); +} + +static void sound_write(u32 addr,u8 data) +{ + switch(addr & 0xF000) { + case 0x4000: + if(addr < 0x4020) { + write4(addr,data); + return; + } + break; + case 0x5000: + //bankswitch registers + if(addr >= 0x5FF6) { + log_printf("nsf.c: bankswitch page $%X to bank %d\n",addr & 0xF,data); + + //swap rom + if(data < 0xFE) { + mem_setprg4(addr & 0xF,data); + } + + //swap ram + else { + mem_setwram4(addr & 0xF,data & 1); + } + + return; + } + break; + } + log_printf("nsf.c: sound write $%04X = $%02X\n",addr,data); +} + +static u8 read_bios(u32 addr) +{ + switch(addr & 0xFFC0) { + + //registers + case 0x3400: + switch(addr) { + //do nothing + case 0x3400: + return(0); + + //irq ack + case 0x3401: + cpu_clear_irq(IRQ_MAPPER); + return(irqenable); + } + break; + + //nsf extra ram + case 0x3440: + return(ram[addr & 0x3F]); + + //map the entire nsf header + case 0x3480: + case 0x34C0: + return(nes->cart->data[addr & 0x7F]); + + } + log_printf("nsf.c: read $%04X\n",addr); + return(0); +} + +extern int showdisasm; + +//write nsf bios registers +static void write_bios(u32 addr,u8 data) +{ + switch(addr & 0xFFC0) { + + //registers + case 0x3400: + switch(addr) { + + //nsf bios bankswitch ($3C00) + case 0x3400: + biosbank = bios + (data * 0x400); + break; + + //irq enable + case 0x3401: + irqenable = data & 1; + if(irqenable) + irqcounter = irqlatch; + cpu_clear_irq(IRQ_MAPPER); + break; + + //irq latch low + case 0x3402: + irqlatch = (irqlatch & 0xFF00) | data; + break; + + //irq latch high + case 0x3403: + irqlatch = (irqlatch & 0x00FF) | (data << 8); + break; + + //playspeed low + case 0x3410: + playspeed = (playspeed & 0xFF00) | data; + irqlatch = (u32)(1786840.0f * (double)playspeed / 1000000.0f); + log_printf("irqlatch = %d (playspeed = %d)\n",irqlatch,playspeed); + break; + + //playspeed high + case 0x3411: + playspeed = (playspeed & 0x00FF) | (data << 8); + irqlatch = (u32)(1786840.0f * (double)playspeed / 1000000.0f); + log_printf("irqlatch = %d (playspeed = %d)\n",irqlatch,playspeed); + break; + + //show disassembly + case 0x3420: + showdisasm = data ? 1 : 0; + break; + + //output data to screen + case 0x3421: + log_printf("nsf.c: write: $%04X = $%02X\n",addr,data); + break; + } + break; + + //nsf extra ram + case 0x3440: + ram[addr & 0x3F] = data; + break; + } +} + +static u8 read(u32 addr) +{ + switch(addr & 0xFC00) { + + //handling of the special bios stuff + case 0x3400: return(read_bios(addr)); + case 0x3800: return(bios[addr & 0x3FF]); + case 0x3C00: return(biosbank[addr & 0x3FF]); + + //vector replacement + case 0xFC00: + if(addr >= 0xFFFA) + return(bios[addr & 0xF]); + break; + + } + return(oldread(addr)); +} + +static void write(u32 addr,u8 data) +{ + switch(addr & 0xFC00) { + + //handling of the special bios stuff + case 0x3400: + write_bios(addr,data); + return; + } + oldwrite(addr,data); +} + +static void reset(int hard) +{ + int i; + + //save pointer to bios + bios = nes->cart->wram.data + 8192; + + //init sound chips + sound_init(); + + //save original read/write funcs + oldread = cpu_getreadfunc(); + oldwrite = cpu_getwritefunc(); + cpu_setreadfunc(read); + cpu_setwritefunc(write); + + //save original $4000 read/write funcs + read4 = mem_getreadfunc(4); + write4 = mem_getwritefunc(4); + + //for reading/writing fds/namco/mmc5 + mem_setreadfunc(4,sound_read); + mem_setreadfunc(5,sound_read); + mem_setwritefunc(4,sound_write); + mem_setwritefunc(5,sound_write); + + //set all upper sound write handlers + for(i=8;i<16;i++) { + mem_setwritefunc(i,sound_write); + } + + //setup vram + mem_setvramsize(8); + mem_setvram8(0,0); + + //setup wram + mem_setwramsize(8); + + //set mirroring + mem_setmirroring(MIRROR_H); + + //clear nsf ram + for(i=0;i<0x40;i++) { + ram[i] = 0; + } + + log_printf("nsf.c: reset: loadaddr = $%04X\n",nes->cart->data[8] | (nes->cart->data[9] << 8)); +} + +static void cpucycle() +{ + if(irqenable) { + irqcounter--; + if(irqcounter == 0) { + irqcounter = irqlatch; + cpu_set_irq(IRQ_MAPPER); + } + } +} + +static void state(int mode,u8 *data) +{ +} + +MAPPER(B_NSF,reset,0,cpucycle,state); diff --git a/apps/nesemu2/src/mappers/sound/s_DRIP.c b/apps/nesemu2/src/mappers/sound/s_DRIP.c new file mode 100644 index 00000000..c8e667e4 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_DRIP.c @@ -0,0 +1,142 @@ +#include +#include "mappers/sound/s_DRIP.h" +#include "types.h" + +typedef struct dripsound_s { + u8 FIFO[256], ReadPos, WritePos; + int IsFull, IsEmpty; + int freq, vol; + int timer; + int Pos; +} dripsound_t; + +static dripsound_t chan[2]; + +static int GenerateWave (dripsound_t *ds,int Cycles) +{ + int i,z = 0; + + for(i = 0; i < Cycles; i++) + { + if (ds->IsEmpty) + break; + if (!ds->timer--) + { + ds->timer = ds->freq; + if (ds->ReadPos == ds->WritePos) + ds->IsFull = 0; + ds->Pos = (ds->FIFO[++ds->ReadPos] - 0x80) * ds->vol; + if (ds->ReadPos == ds->WritePos) + ds->IsEmpty = 1; + } + z += ds->Pos; + } + return z / Cycles; +} + +static int Read(dripsound_t *ds,int Addr) +{ + int result = 0; + + if (ds->IsFull) + result |= 0x80; + if (ds->IsEmpty) + result |= 0x40; + return result; +} + +static void Write(dripsound_t *ds,int Addr,int Val) +{ + switch (Addr & 3) + { + case 0x0: + memset(ds->FIFO,0,256); + ds->ReadPos = ds->WritePos = 0; + ds->IsFull = 0; + ds->IsEmpty = 1; + ds->Pos = 0; + ds->timer = ds->freq; + break; + case 0x1: + if (ds->ReadPos == ds->WritePos) + { + ds->IsEmpty = 0; + ds->Pos = (Val - 0x80) * ds->vol; + ds->timer = ds->freq; + } + ds->FIFO[ds->WritePos++] = Val; + if (ds->ReadPos == ds->WritePos) + ds->IsFull = 1; + break; + case 0x2: + ds->freq = (ds->freq & 0xF00) | Val; + break; + case 0x3: + ds->freq = (ds->freq & 0xFF) | ((Val & 0xF) << 8); + ds->vol = (Val & 0xF0) >> 4; + if (!ds->IsEmpty) + ds->Pos = (ds->FIFO[ds->ReadPos] - 0x80) * ds->vol; + break; + } +} + +void DRIPsound_Load(void) +{ +} + +void DRIPsound_Reset(void) +{ + memset(chan,0,sizeof(dripsound_t) * 2); + chan[0].IsEmpty = 1; + chan[1].IsEmpty = 1; +} + +void DRIPsound_Unload(void) +{ +} + +int DRIPsound_Read(int Addr) +{ + if(Addr & 0x800) + return(Read(&chan[1],Addr)); + return(Read(&chan[0],Addr)); +} + +void DRIPsound_Write(int Addr,int Val) +{ + if(Addr & 4) + Write(&chan[1],Addr,Val); + else + Write(&chan[0],Addr,Val); +} + +int DRIPsound_Get(int Cycles) +{ + int out = 0; + out += GenerateWave(&chan[0],Cycles); + out += GenerateWave(&chan[1],Cycles); + return out << 3; +} + +int DRIPsound_SaveLoad(int statetype,int a,unsigned char *d) +{ +/* for (int i = 0; i < 256; i++) + SAVELOAD_BYTE(mode, offset, data, Chan[0].FIFO[i]); + for (int i = 0; i < 256; i++) + SAVELOAD_BYTE(mode, offset, data, Chan[1].FIFO[i]); + SAVELOAD_BYTE(mode, offset, data, Chan[0].ReadPos); + SAVELOAD_BYTE(mode, offset, data, Chan[1].ReadPos); + SAVELOAD_BYTE(mode, offset, data, Chan[0].WritePos); + SAVELOAD_BYTE(mode, offset, data, Chan[1].WritePos); + SAVELOAD_BYTE(mode, offset, data, Chan[0].IsFull); + SAVELOAD_BYTE(mode, offset, data, Chan[1].IsFull); + SAVELOAD_BYTE(mode, offset, data, Chan[0].IsEmpty); + SAVELOAD_BYTE(mode, offset, data, Chan[1].IsEmpty); + SAVELOAD_WORD(mode, offset, data, Chan[0].freq); + SAVELOAD_WORD(mode, offset, data, Chan[1].freq); + SAVELOAD_BYTE(mode, offset, data, Chan[0].vol); + SAVELOAD_BYTE(mode, offset, data, Chan[1].vol); + SAVELOAD_WORD(mode, offset, data, Chan[0].timer); + SAVELOAD_WORD(mode, offset, data, Chan[1].timer);*/ + return(0); +} diff --git a/apps/nesemu2/src/mappers/sound/s_DRIP.h b/apps/nesemu2/src/mappers/sound/s_DRIP.h new file mode 100644 index 00000000..a6781f94 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_DRIP.h @@ -0,0 +1,12 @@ +#ifndef __s_DRIP_h__ +#define __s_DRIP_h__ + +void DRIPsound_Load (void); +void DRIPsound_Reset (void); +void DRIPsound_Unload (void); +int DRIPsound_Read (int); +void DRIPsound_Write (int,int); +int DRIPsound_Get (int); +int DRIPsound_SaveLoad(int statetype,int,unsigned char *); + +#endif diff --git a/apps/nesemu2/src/mappers/sound/s_FDS.c b/apps/nesemu2/src/mappers/sound/s_FDS.c new file mode 100644 index 00000000..db1a1597 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_FDS.c @@ -0,0 +1,325 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_FDS.c $ + * $Id: s_FDS.c 376 2008-06-29 20:58:13Z Quietust $ + */ + +//#include "..\..\interface.h" +#include +#include +#include "types.h" +#include "s_FDS.h" + +// Sound code borrowed from NEZplug 0.9.4.8 + +#define FM_DEPTH 0 /* 0,1,2 */ +#define NES_BASECYCLES (21477270) +#define PGCPS_BITS (32-16-6) +#define EGCPS_BITS (12) +#define VOL_BITS 12 + +#define LOG_BITS 12 +#define LIN_BITS 7 +#define LOG_LIN_BITS 30 + +struct FDS_EG { + u8 spd; + u8 cnt; + u8 mode; + u8 volume; +} eg; +struct FDS_PG { + u32 spdbase; + u32 spd; + u32 freq; +} pg; +struct FDS_WG { + u32 phase; + s8 wave[0x40]; + u8 wavptr; + s8 output; + u8 disable; + u8 disable2; +} wg; + +struct FDS_OP { + struct FDS_EG eg; + struct FDS_PG pg; + struct FDS_WG wg; + u8 bias; + u8 wavebase; + u8 d[2]; +} op[2]; + +typedef struct FDSsound { + struct FDS_OP op[2]; + u32 phasecps; + u32 envcnt; + u32 envspd; + u32 envcps; + u8 envdisable; + u8 d[3]; + u32 lvl; + s32 mastervolumel[4]; + u32 mastervolume; + u32 srate; + u8 reg[0x10]; +} TFDSsound, *PFDSsound; + +static TFDSsound FDSsound; + +static u32 lineartbl[(1 << LIN_BITS) + 1]; +static u32 logtbl[1 << LOG_BITS]; + +/*static u32 LinearToLog(s32 l) +{ + return (l < 0) ? (lineartbl[-l] + 1) : lineartbl[l]; +}*/ + +static s32 LogToLinear(u32 l, u32 sft) +{ + s32 ret; + u32 ofs; + l += sft << (LOG_BITS + 1); + sft = l >> (LOG_BITS + 1); + if (sft >= LOG_LIN_BITS) return 0; + ofs = (l >> 1) & ((1 << LOG_BITS) - 1); + ret = logtbl[ofs] >> sft; + return (l & 1) ? -ret : ret; +} + +static void LogTableInitialize(void) +{ + static volatile u32 initialized = 0; + u32 i; + double a; + if (initialized) return; + initialized = 1; + for (i = 0; i < (1 << LOG_BITS); i++) + { + a = (1 << LOG_LIN_BITS) / pow(2, i / (double)(1 << LOG_BITS)); + logtbl[i] = (u32)a; + } + lineartbl[0] = LOG_LIN_BITS << LOG_BITS; + for (i = 1; i < (1 << LIN_BITS) + 1; i++) + { + u32 ua; + a = (double)((u64)i << (LOG_LIN_BITS - LIN_BITS)); + ua = (u32)((LOG_LIN_BITS - (log(a) / log(2))) * (1 << LOG_BITS)); + lineartbl[i] = ua << 1; + } +} + +static void FDSSoundWGStep(struct FDS_WG *pwg) +{ + if (pwg->disable || pwg->disable2) return; + pwg->output = pwg->wave[(pwg->phase >> (PGCPS_BITS+16)) & 0x3f]; +} + +static void FDSSoundEGStep(struct FDS_EG *peg) +{ + if (peg->mode & 0x80) return; + if (++peg->cnt <= peg->spd) return; + peg->cnt = 0; + if (peg->mode & 0x40) + peg->volume += (peg->volume < 0x1f); + else + peg->volume -= (peg->volume > 0); +} + + +static s32 FDSSoundRender(void) +{ + s32 output; + /* Wave Generator */ + FDSSoundWGStep(&FDSsound.op[1].wg); + FDSSoundWGStep(&FDSsound.op[0].wg); + + /* Frequency Modulator */ + FDSsound.op[1].pg.spd = FDSsound.op[1].pg.spdbase; + if (FDSsound.op[1].wg.disable) + FDSsound.op[0].pg.spd = FDSsound.op[0].pg.spdbase; + else + { + u32 v1; + v1 = 0x10000 + ((s32)FDSsound.op[1].eg.volume) * (((s32)(((u8)FDSsound.op[1].wg.output) & 255)) - 64); + v1 = ((1 << 10) + v1) & 0xfff; + v1 = (FDSsound.op[0].pg.freq * v1) >> 10; + FDSsound.op[0].pg.spd = v1 * FDSsound.phasecps; + } + + /* Accumulator */ + output = FDSsound.op[0].eg.volume; + if (output > 0x20) output = 0x20; + output = (FDSsound.op[0].wg.output * output * FDSsound.mastervolumel[FDSsound.lvl]) >> (VOL_BITS - 4); + + /* Envelope Generator */ + if (!FDSsound.envdisable && FDSsound.envspd) + { + FDSsound.envcnt += FDSsound.envcps; + while (FDSsound.envcnt >= FDSsound.envspd) + { + FDSsound.envcnt -= FDSsound.envspd; + FDSSoundEGStep(&FDSsound.op[1].eg); + FDSSoundEGStep(&FDSsound.op[0].eg); + } + } + + /* Phase Generator */ + FDSsound.op[1].wg.phase += FDSsound.op[1].pg.spd; + FDSsound.op[0].wg.phase += FDSsound.op[0].pg.spd; + return (FDSsound.op[0].pg.freq != 0) ? output : 0; +} + +static void FDSSoundVolume(unsigned int volume) +{ + volume += 196; + FDSsound.mastervolume = (volume << (LOG_BITS - 8)) << 1; + FDSsound.mastervolumel[0] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 2; + FDSsound.mastervolumel[1] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 4 / 3; + FDSsound.mastervolumel[2] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 2 / 2; + FDSsound.mastervolumel[3] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 8 / 10; +} + +static const u8 wave_delta_table[8] = { + 0,(1 << FM_DEPTH),(2 << FM_DEPTH),(4 << FM_DEPTH), + 0,256 - (4 << FM_DEPTH),256 - (2 << FM_DEPTH),256 - (1 << FM_DEPTH), +}; + +static u32 DivFix(u32 p1, u32 p2, u32 fix) +{ + u32 ret; + ret = p1 / p2; + p1 = p1 % p2; + while (fix--) + { + p1 += p1; + ret += ret; + if (p1 >= p2) + { + p1 -= p2; + ret++; + } + } + return ret; +} + +void FDSsound_Load (void) +{ + memset(&FDSsound, 0, sizeof(TFDSsound)); + FDSsound.srate = 44100; + FDSsound.envcps = DivFix(NES_BASECYCLES, 12 * FDSsound.srate, EGCPS_BITS + 5 - 9 + 1); + FDSsound.envspd = 0xe8 << EGCPS_BITS; + FDSsound.envdisable = 1; + FDSsound.phasecps = DivFix(NES_BASECYCLES, 12 * FDSsound.srate, PGCPS_BITS); + LogTableInitialize(); + FDSSoundVolume(0); +} + +void FDSsound_Reset (void) +{ + int i; + for (i = 0; i < 0x40; i++) + { + FDSsound.op[0].wg.wave[i] = (i < 0x20) ? 0x1F : -0x20; + FDSsound.op[1].wg.wave[i] = 64; + } +} + +void FDSsound_Unload (void) +{ +} + +int FDSsound_Read (int Addr) +{ + if ((0x4040 <= Addr) && (Addr <= 0x407F)) + return FDSsound.op[0].wg.wave[Addr & 0x3F] + 0x20; + if (0x4090 == Addr) + return FDSsound.op[0].eg.volume | 0x40; + if (0x4092 == Addr) /* 4094? */ + return FDSsound.op[1].eg.volume | 0x40; + return -1; +} + +void FDSsound_Write (int Addr, int Val) +{ + if (0x4040 <= Addr && Addr <= 0x407F) + { + FDSsound.op[0].wg.wave[Addr - 0x4040] = ((int)(Val & 0x3F)) - 0x20; + } + else if (0x4080 <= Addr && Addr <= 0x408F) + { + struct FDS_OP *pop = &FDSsound.op[(Addr & 4) >> 2]; + FDSsound.reg[Addr - 0x4080] = Val; + switch (Addr & 0xf) + { + case 0: + case 4: + pop->eg.mode = Val & 0xc0; + if (pop->eg.mode & 0x80) + { + pop->eg.volume = (Val & 0x3f); + } + else + { + pop->eg.spd = Val & 0x3f; + } + break; + case 5: + FDSsound.op[1].bias = Val & 255; + break; + case 2: case 6: + pop->pg.freq &= 0x00000F00; + pop->pg.freq |= (Val & 0xFF) << 0; + pop->pg.spdbase = pop->pg.freq * FDSsound.phasecps; + break; + case 3: + FDSsound.envdisable = Val & 0x40; + case 7: + pop->pg.freq &= 0x000000FF; + pop->pg.freq |= (Val & 0x0F) << 8; + pop->pg.spdbase = pop->pg.freq * FDSsound.phasecps; + pop->wg.disable = Val & 0x80; + if (pop->wg.disable) + { + pop->wg.phase = 0; + pop->wg.wavptr = 0; + pop->wavebase = 0; + } + break; + case 8: + if (FDSsound.op[1].wg.disable) + { + s32 idx = Val & 7; + if (idx == 4) + { + FDSsound.op[1].wavebase = 0; + } + FDSsound.op[1].wavebase += wave_delta_table[idx]; + FDSsound.op[1].wg.wave[FDSsound.op[1].wg.wavptr + 0] = (FDSsound.op[1].wavebase + FDSsound.op[1].bias + 64) & 255; + FDSsound.op[1].wavebase += wave_delta_table[idx]; + FDSsound.op[1].wg.wave[FDSsound.op[1].wg.wavptr + 1] = (FDSsound.op[1].wavebase + FDSsound.op[1].bias + 64) & 255; + FDSsound.op[1].wg.wavptr = (FDSsound.op[1].wg.wavptr + 2) & 0x3f; + } + break; + case 9: + FDSsound.lvl = (Val & 3); + FDSsound.op[0].wg.disable2 = Val & 0x80; + break; + case 10: + FDSsound.envspd = Val << EGCPS_BITS; + break; + } + } +} + +int FDSsound_Get (int numCycles) +{ + return FDSSoundRender() >> 9; // current code does not run per-cycle +} + +int FDSsound_SaveLoad (int mode, int x, unsigned char *data) +{ + return x; +} diff --git a/apps/nesemu2/src/mappers/sound/s_FDS.h b/apps/nesemu2/src/mappers/sound/s_FDS.h new file mode 100644 index 00000000..057d2625 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_FDS.h @@ -0,0 +1,19 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_FDS.h $ + * $Id: s_FDS.h 376 2008-06-29 20:58:13Z Quietust $ + */ + +#ifndef S_FDS_H +#define S_FDS_H + +void FDSsound_Load (void); +void FDSsound_Reset (void); +void FDSsound_Unload (void); +int FDSsound_Read (int); +void FDSsound_Write (int,int); +int FDSsound_Get (int); +int FDSsound_SaveLoad (int,int,unsigned char *); + +#endif /* S_FDS_H */ diff --git a/apps/nesemu2/src/mappers/sound/s_FME7.c b/apps/nesemu2/src/mappers/sound/s_FME7.c new file mode 100644 index 00000000..bff49ef9 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_FME7.c @@ -0,0 +1,148 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_FME7.c $ + * $Id: s_FME7.c 376 2008-06-29 20:58:13Z Quietust $ + */ + +//#include "..\..\interface.h" +#include +#include "s_FME7.h" +#include "types.h" + +// Sunsoft FME-7, based on the AY-8910 + +typedef struct FME7sqr +{ + union + { + struct + { + unsigned freq :12; + unsigned : 4; + unsigned volume : 4; + unsigned envmode : 1; + unsigned : 3; + }; + struct + { + unsigned byte0 : 8; + unsigned byte1 : 8; + unsigned byte2 : 8; + }; + }; + u8 CurP; + s32 LCtr; +} TFME7sqr, *PFME7sqr; + +typedef struct FME7sound +{ + union + { + struct + { + unsigned tone : 3; + unsigned : 3; + unsigned : 2; + unsigned envelope :16; + unsigned envhold : 1; + unsigned envaltr : 1; + unsigned envattk : 1; + unsigned envcont : 1; + unsigned : 4; + }; + struct + { + unsigned byte7 : 8; + unsigned byteB : 8; + unsigned byteC : 8; + unsigned byteD : 8; + }; + }; + u8 select; + TFME7sqr Sqr[3]; +} TFME7sound, *PFME7sound; + +static TFME7sound FME7sound; + +static int FME7_DoSquare (PFME7sqr ChanData, int Cycles) +{ + ChanData->LCtr -= Cycles; + while (ChanData->LCtr <= 0) + { + ChanData->CurP++; + ChanData->CurP &= 0x1F; + ChanData->LCtr += ChanData->freq + 1; + } + return ChanData->volume * ((ChanData->CurP & 0x10) ? 3 : -3); +} + +void FME7sound_Load (void) +{ + memset(&FME7sound,0,sizeof(FME7sound)); +} + +void FME7sound_Reset (void) +{ + FME7sound.Sqr[0].LCtr = 1; + FME7sound.Sqr[1].LCtr = 1; + FME7sound.Sqr[2].LCtr = 1; +} + +void FME7sound_Unload (void) +{ +} + +void FME7sound_Write (int Addr, int Val) +{ + switch (Addr & 0xE000) + { + case 0xC000: FME7sound.select = Val & 0xF; break; + case 0xE000: switch (FME7sound.select) + { + case 0x0: FME7sound.Sqr[0].byte0 = Val; break; + case 0x1: FME7sound.Sqr[0].byte1 = Val; break; + case 0x2: FME7sound.Sqr[1].byte0 = Val; break; + case 0x3: FME7sound.Sqr[1].byte1 = Val; break; + case 0x4: FME7sound.Sqr[2].byte0 = Val; break; + case 0x5: FME7sound.Sqr[2].byte1 = Val; break; + case 0x7: FME7sound.byte7 = Val; break; + case 0x8: FME7sound.Sqr[0].byte2 = Val; break; + case 0x9: FME7sound.Sqr[1].byte2 = Val; break; + case 0xA: FME7sound.Sqr[2].byte2 = Val; break; + case 0xB: FME7sound.byteB = Val; break; + case 0xC: FME7sound.byteC = Val; break; + case 0xD: FME7sound.byteD = Val; break; + } break; + } +} + +int FME7sound_Get (int Cycles) +{ + int z = 0; + if (!(FME7sound.tone & 1)) z += FME7_DoSquare(&FME7sound.Sqr[0],Cycles); + if (!(FME7sound.tone & 2)) z += FME7_DoSquare(&FME7sound.Sqr[1],Cycles); + if (!(FME7sound.tone & 4)) z += FME7_DoSquare(&FME7sound.Sqr[2],Cycles); + return z << 6; +} + +int FME7sound_SaveLoad (int mode, int x, unsigned char *data) +{ +/* SAVELOAD_BYTE(mode,x,data,FME7sound.select); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[0].byte0); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[0].byte1); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[0].byte2); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[0].CurP); + SAVELOAD_LONG(mode,x,data,FME7sound.Sqr[0].LCtr); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[1].byte0); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[1].byte1); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[1].byte2); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[1].CurP); + SAVELOAD_LONG(mode,x,data,FME7sound.Sqr[1].LCtr); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[2].byte0); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[2].byte1); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[2].byte2); + SAVELOAD_BYTE(mode,x,data,FME7sound.Sqr[2].CurP); + SAVELOAD_LONG(mode,x,data,FME7sound.Sqr[2].LCtr); +*/ return x; +} diff --git a/apps/nesemu2/src/mappers/sound/s_FME7.h b/apps/nesemu2/src/mappers/sound/s_FME7.h new file mode 100644 index 00000000..df125c71 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_FME7.h @@ -0,0 +1,18 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_FME7.h $ + * $Id: s_FME7.h 376 2008-06-29 20:58:13Z Quietust $ + */ + +#ifndef S_FME7_H +#define S_FME7_H + +void FME7sound_Load (void); +void FME7sound_Reset (void); +void FME7sound_Unload (void); +void FME7sound_Write (int,int); +int FME7sound_Get (int); +int FME7sound_SaveLoad (int statetype,int,unsigned char *); + +#endif /* S_FME7_H */ diff --git a/apps/nesemu2/src/mappers/sound/s_MMC5.c b/apps/nesemu2/src/mappers/sound/s_MMC5.c new file mode 100644 index 00000000..b3698f7c --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_MMC5.c @@ -0,0 +1,213 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_MMC5.c $ + * $Id: s_MMC5.c 376 2008-06-29 20:58:13Z Quietust $ + */ + +//#include "..\..\interface.h" +#include +#include "s_MMC5.h" +#include "nes/state/state.h" + +#define TRUE 1 +#define FALSE 0 + +// Nintendo MMC5, based on the NES APU + +typedef struct MMC5Sqr +{ + unsigned char volume, envelope, wavehold, duty; + unsigned long freq; + int Vol; + unsigned char CurD; + int Timer; + int EnvCtr, Envelope; + int Enabled, ValidFreq, Active; + int EnvClk; + int Cycles; + signed long Pos; + unsigned long FrameCycles; +} TMMC5sqr, *PMMC5sqr; + +typedef struct MMC5sound +{ + TMMC5sqr Sqr0, Sqr1; + unsigned char byte0, byte2, byte3, byte4, byte6, byte7, byte10, byte11, byte15; + int PCM; +} TMMC5sound, *PMMC5sound; + +static TMMC5sound MMC5sound; + +#define QUARTER_FRAME_LEN 7457 + +static const unsigned char LengthCounts[32] = { + 0x0A,0xFE, + 0x14,0x02, + 0x28,0x04, + 0x50,0x06, + 0xA0,0x08, + 0x3C,0x0A, + 0x0E,0x0C, + 0x1A,0x0E, + + 0x0C,0x10, + 0x18,0x12, + 0x30,0x14, + 0x60,0x16, + 0xC0,0x18, + 0x48,0x1A, + 0x10,0x1C, + 0x20,0x1E +}; +const signed char Duties[4][8] = { + {-4,+4,-4,-4,-4,-4,-4,-4}, + {-4,+4,+4,-4,-4,-4,-4,-4}, + {-4,+4,+4,+4,+4,-4,-4,-4}, + {+4,-4,-4,+4,+4,+4,+4,+4} +}; + +static void MMC5Square_CheckActive (PMMC5sqr Chan) +{ + if ((Chan->ValidFreq = (Chan->freq >= 0x8)) && (Chan->Timer)) + { + Chan->Active = TRUE; + Chan->Pos = Duties[Chan->duty][Chan->CurD] * Chan->Vol; + } + else + { + Chan->Active = FALSE; + Chan->Pos = 0; + } +} +static void MMC5Square_Write (PMMC5sqr Chan, int Reg, int Val) +{ + switch (Reg) + { + case 0: Chan->volume = Val & 0xF; + Chan->envelope = Val & 0x10; + Chan->wavehold = Val & 0x20; + Chan->duty = (Val >> 6) & 0x3; + Chan->Vol = Chan->envelope ? Chan->volume : Chan->Envelope; + break; + case 2: Chan->freq &= 0x700; + Chan->freq |= Val; + break; + case 3: Chan->freq &= 0xFF; + Chan->freq |= (Val & 0x7) << 8; + if (Chan->Enabled) + Chan->Timer = LengthCounts[(Val >> 3) & 0x1F] << 1; + Chan->CurD = 0; + Chan->EnvClk = TRUE; + break; + case 4: if (!(Chan->Enabled = Val ? TRUE : FALSE)) + Chan->Timer = 0; + break; + } + MMC5Square_CheckActive(Chan); +} +static int MMC5_GenerateSquare (PMMC5sqr Chan, int Cycles) +{ + if (!Chan->Active) + return 0; + Chan->Cycles -= Cycles; + while (Chan->Cycles <= 0) + { + Chan->Cycles += (Chan->freq + 1) << 1; + Chan->CurD = (Chan->CurD + 1) & 0x7; + if (Chan->Active) + Chan->Pos = Duties[Chan->duty][Chan->CurD] * Chan->Vol; + } + + Chan->FrameCycles -= Cycles; + while (Chan->FrameCycles <= 0) + { + Chan->FrameCycles += QUARTER_FRAME_LEN; + if (Chan->EnvClk) + { + Chan->EnvClk = FALSE; + Chan->Envelope = 0xF; + Chan->EnvCtr = Chan->volume + 1; + } + else if (!--Chan->EnvCtr) + { + Chan->EnvCtr = Chan->volume + 1; + if (Chan->Envelope) + Chan->Envelope--; + else Chan->Envelope = Chan->wavehold ? 0xF : 0x0; + } + + if (Chan->Timer && !Chan->wavehold) + Chan->Timer--; + } + Chan->Vol = Chan->envelope ? Chan->volume : Chan->Envelope; + MMC5Square_CheckActive(Chan); + return Chan->Pos; +} + +void MMC5sound_Load (void) +{ + memset(&MMC5sound,0,sizeof(TMMC5sound)); +} + +void MMC5sound_Reset (int ResetType) +{ + MMC5sound.Sqr0.Cycles = 1; + MMC5sound.Sqr1.Cycles = 1; +} + +void MMC5sound_Unload (void) +{ +} + +void MMC5sound_Write (int Addr, int Val) +{ + switch (Addr) + { + case 0x5000: MMC5Square_Write(&MMC5sound.Sqr0,0,MMC5sound.byte0 = Val); break; + case 0x5002: MMC5Square_Write(&MMC5sound.Sqr0,2,MMC5sound.byte2 = Val); break; + case 0x5003: MMC5Square_Write(&MMC5sound.Sqr0,3,MMC5sound.byte3 = Val); break; + case 0x5004: MMC5Square_Write(&MMC5sound.Sqr1,0,MMC5sound.byte4 = Val); break; + case 0x5006: MMC5Square_Write(&MMC5sound.Sqr1,2,MMC5sound.byte6 = Val); break; + case 0x5007: MMC5Square_Write(&MMC5sound.Sqr1,3,MMC5sound.byte7 = Val); break; + case 0x5010: MMC5sound.byte10 = Val; +// EMU->DbgOut("MMC5 - $5010 set to %02X",Val); + break; + case 0x5011: MMC5sound.PCM = MMC5sound.byte11 = Val; + break; + case 0x5015: MMC5sound.byte15 = Val; + MMC5Square_Write(&MMC5sound.Sqr0,4,Val & 0x01); + MMC5Square_Write(&MMC5sound.Sqr1,4,Val & 0x02); + break; + } +} + +int MMC5sound_Read (int Addr) +{ + int read = -1; + switch (Addr) + { + case 0x5010: read = MMC5sound.PCM; break; + case 0x5015: read = ((MMC5sound.Sqr0.Timer) ? 1 : 0) | ((MMC5sound.Sqr1.Timer) ? 2 : 0); break; + } + return read; +} + +int MMC5sound_Get (int Cycles) +{ + int z = 0; + if (MMC5sound.Sqr0.Enabled) z += MMC5_GenerateSquare(&MMC5sound.Sqr0,Cycles); + if (MMC5sound.Sqr1.Enabled) z += MMC5_GenerateSquare(&MMC5sound.Sqr1,Cycles); + z += MMC5sound.PCM; + return z << 6; +} + +int MMC5sound_SaveLoad(int mode, int x, unsigned char *data) +{ + if (mode == STATE_SAVE) + memcpy(data+x,&MMC5sound,sizeof(MMC5sound)); + else if (mode == STATE_LOAD) + memcpy(&MMC5sound,data+x,sizeof(MMC5sound)); + x += sizeof(MMC5sound); + return x; +} diff --git a/apps/nesemu2/src/mappers/sound/s_MMC5.h b/apps/nesemu2/src/mappers/sound/s_MMC5.h new file mode 100644 index 00000000..c58ede28 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_MMC5.h @@ -0,0 +1,19 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_MMC5.h $ + * $Id: s_MMC5.h 376 2008-06-29 20:58:13Z Quietust $ + */ + +#ifndef S_MMC5_H +#define S_MMC5_H + +void MMC5sound_Load (void); +void MMC5sound_Reset (int); +void MMC5sound_Unload (void); +void MMC5sound_Write (int,int); +int MMC5sound_Read (int); +int MMC5sound_Get (int); +int MMC5sound_SaveLoad (int,int,unsigned char *); + +#endif /* S_MMC5_H */ diff --git a/apps/nesemu2/src/mappers/sound/s_N106.c b/apps/nesemu2/src/mappers/sound/s_N106.c new file mode 100644 index 00000000..8e523bde --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_N106.c @@ -0,0 +1,172 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_N106.c $ + * $Id: s_N106.c 376 2008-06-29 20:58:13Z Quietust $ + */ + +//#include "..\..\interface.h" +#include +#include "types.h" +#include "s_N106.h" + +// Namco 106 + +struct N106chan +{ + u8 freql, freqm, freqh; + u32 freq; + u8 len; + u8 addr; + u8 volume; + u8 CurP; + u8 CurA; + s32 LCtr; +} Ch[8]; + +static struct TN106sound +{ + u8 data[0x80]; + struct N106chan Ch[8]; + u8 chans; + u8 addr; + u8 inc; +} N106sound; + +void N106sound_Load (void) +{ + memset(&N106sound,0,sizeof(N106sound)); +} + +void N106sound_Reset (void) +{ + int i; + for (i = 0; i < 8; i++) + N106sound.Ch[i].len = 0x10; + N106sound.chans = 8; + N106sound.inc = 0x80; +} + +void N106sound_Unload (void) +{ +} + +void N106sound_Write (int Addr, int Val) +{ + switch (Addr & 0xF800) + { + case 0xF800: + N106sound.addr = Val & 0x7F; + N106sound.inc = Val & 0x80; + break; + case 0x4800: + N106sound.data[N106sound.addr] = Val; + if (N106sound.addr & 0x40) + { + struct N106chan *Chan = &N106sound.Ch[(N106sound.addr & 0x3F) >> 3]; + switch (N106sound.addr & 0x7) + { + case 0: Chan->freql = Val; + Chan->freq = Chan->freql | (Chan->freqm << 8) | (Chan->freqh << 16); + break; + case 2: Chan->freqm = Val; + Chan->freq = Chan->freql | (Chan->freqm << 8) | (Chan->freqh << 16); + break; + case 4: Chan->freqh = Val & 3; + Chan->freq = Chan->freql | (Chan->freqm << 8) | (Chan->freqh << 16); + if (Chan->len != 0x20 - (Val & 0x1C)) + { + Chan->len = 0x20 - (Val & 0x1C); + Chan->CurA = 0; + } + break; + case 6: Chan->addr = Val; + break; + case 7: Chan->volume = Val & 0xF; + if (Chan == &N106sound.Ch[7]) + N106sound.chans = 1 + ((Val >> 4) & 0x7); + break; + } + } + if (N106sound.inc) + { + N106sound.addr++; + N106sound.addr &= 0x7F; + } + break; + } +} + +int N106sound_Read (int Addr) +{ + int data = N106sound.data[N106sound.addr]; + + if (N106sound.inc) + { + N106sound.addr++; + N106sound.addr &= 0x7F; + } + + return data; +} + +static int N106_GenerateWave (struct N106chan *Chan, int Cycles) +{ + s32 freq; + + if (!Chan->freq) + return 0; + freq = (0xF0000 * N106sound.chans) / Chan->freq; + Chan->LCtr += Cycles; + while (Chan->LCtr > freq) + { + u8 addr; + Chan->CurA++; + while (Chan->CurA >= Chan->len) + Chan->CurA -= Chan->len; + addr = Chan->addr + Chan->CurA; + Chan->CurP = N106sound.data[addr >> 1]; + if (addr & 1) + Chan->CurP >>= 4; + else Chan->CurP &= 0xF; + Chan->LCtr -= freq; + } + return (Chan->CurP - 0x8) * Chan->volume; +} +int N106sound_Get (int Cycles) +{ + int out = 0; + int i; + for (i = 8 - N106sound.chans; i < 8; i++) + out += N106_GenerateWave(&N106sound.Ch[i],Cycles); + return out << 5; +} + +int N106sound_SaveLoad (int statetype, int x, unsigned char *data) +{ + int i; + switch (statetype) + { + case 0: //save + for (i = 0; i < 0x80; i++) + data[x++] = N106sound.data[i]; + data[x++] = N106sound.addr | N106sound.inc; + break; + case 1: //load + N106sound_Write(0xF800,0x80); + for (i = 0; i < 0x80; i++) + N106sound_Write(0x4800,data[x++]); + N106sound_Write(0xF800,data[x++]); + break; + case 2: //size of state data + x += 0x81; + break; + } + for (i = 0; i < 8; i++) + { +// SAVELOAD_BYTE(mode,x,data,N106sound.Ch[i].CurP); +// SAVELOAD_BYTE(mode,x,data,N106sound.Ch[i].CurA); +// SAVELOAD_LONG(mode,x,data,N106sound.Ch[i].LCtr); + } + return x; +} diff --git a/apps/nesemu2/src/mappers/sound/s_N106.h b/apps/nesemu2/src/mappers/sound/s_N106.h new file mode 100644 index 00000000..44be10c6 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_N106.h @@ -0,0 +1,19 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_N106.h $ + * $Id: s_N106.h 376 2008-06-29 20:58:13Z Quietust $ + */ + +#ifndef S_N106_H +#define S_N106_H + +void N106sound_Load (void); +void N106sound_Reset (void); +void N106sound_Unload (void); +int N106sound_Read (int); +void N106sound_Write (int,int); +int N106sound_Get (int); +int N106sound_SaveLoad (int statetype,int,unsigned char *); + +#endif /* S_N106_H */ diff --git a/apps/nesemu2/src/mappers/sound/s_VRC6.c b/apps/nesemu2/src/mappers/sound/s_VRC6.c new file mode 100644 index 00000000..23f1408a --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_VRC6.c @@ -0,0 +1,158 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_VRC6.c $ + * $Id: s_VRC6.c 376 2008-06-29 20:58:13Z Quietust $ + */ + +#include + +//#include "..\..\interface.h" +#include "s_VRC6.h" +#include "types.h" + +// Konami VRC6 + +struct VRC6sqr +{ + union + { + struct + { + unsigned volume : 4; + unsigned duty : 3; + unsigned digital: 1; + unsigned freq :12; + unsigned : 3; + unsigned enabled: 1; + }; + struct + { + unsigned byte0 : 8; + unsigned byte1 : 8; + unsigned byte2 : 8; + }; + }; + u8 CurP; + s32 LCtr; +} Sq0, Sq1; + +struct VRC6saw +{ + union + { + struct + { + unsigned volume : 6; + unsigned : 2; + unsigned freq :12; + unsigned : 3; + unsigned enabled: 1; + }; + struct + { + unsigned byte0 : 8; + unsigned byte1 : 8; + unsigned byte2 : 8; + }; + }; + u8 CurP; + u8 Acc; + s32 LCtr; +} Saw; + +static struct TVRC6sound +{ + struct VRC6sqr Sq0, Sq1; + struct VRC6saw Saw; +} VRC6sound; + +static int VRC6_GenerateSaw (struct VRC6saw *ChanData, int Cycles) +{ + ChanData->LCtr -= Cycles; + while (ChanData->LCtr <= 0) + { + ChanData->Acc++; + ChanData->Acc %= 14; + if (ChanData->Acc == 0) + ChanData->CurP = 0; + else if (!(ChanData->Acc & 1)) + ChanData->CurP += ChanData->volume; + ChanData->LCtr += ChanData->freq + 1; + } + return ((ChanData->CurP >> 3) - 0x10) << 1; +} + +static int VRC6_GenerateSquare (struct VRC6sqr *ChanData, int Cycles) +{ + ChanData->LCtr -= Cycles; + while (ChanData->LCtr <= 0) + { + ChanData->CurP++; + ChanData->CurP &= 0xF; + ChanData->LCtr += ChanData->freq + 1; + } + return (((ChanData->digital) || (ChanData->CurP <= ChanData->duty)) ? 1 : -1) * ChanData->volume; +} + +void VRC6sound_Load (void) +{ + memset(&VRC6sound,0,sizeof(VRC6sound)); +} + +void VRC6sound_Reset (void) +{ + VRC6sound.Sq0.LCtr = 1; + VRC6sound.Sq1.LCtr = 1; + VRC6sound.Saw.LCtr = 1; +} + +void VRC6sound_Unload (void) +{ +} + +void VRC6sound_Write (int Addr, int Val) +{ + switch (Addr) + { + case 0x9000: VRC6sound.Sq0.byte0 = Val; break; + case 0x9001: VRC6sound.Sq0.byte1 = Val; break; + case 0x9002: VRC6sound.Sq0.byte2 = Val; break; + case 0xA000: VRC6sound.Sq1.byte0 = Val; break; + case 0xA001: VRC6sound.Sq1.byte1 = Val; break; + case 0xA002: VRC6sound.Sq1.byte2 = Val; break; + case 0xB000: VRC6sound.Saw.byte0 = Val; break; + case 0xB001: VRC6sound.Saw.byte1 = Val; break; + case 0xB002: VRC6sound.Saw.byte2 = Val; break; + } +} + +int VRC6sound_Get (int Cycles) +{ + int z = 0; + if (VRC6sound.Sq0.enabled) z += VRC6_GenerateSquare(&VRC6sound.Sq0,Cycles); + if (VRC6sound.Sq1.enabled) z += VRC6_GenerateSquare(&VRC6sound.Sq1,Cycles); + if (VRC6sound.Saw.enabled) z += VRC6_GenerateSaw(&VRC6sound.Saw,Cycles); + return z << 8; +} + +int VRC6sound_SaveLoad (int mode, int x, unsigned char *data) +{ +/* SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq0.byte0); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq0.byte1); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq0.byte2); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq0.CurP); + SAVELOAD_LONG(mode,x,data,VRC6sound.Sq0.LCtr); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq1.byte0); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq1.byte1); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq1.byte2); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Sq1.CurP); + SAVELOAD_LONG(mode,x,data,VRC6sound.Sq1.LCtr); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Saw.byte0); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Saw.byte1); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Saw.byte2); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Saw.CurP); + SAVELOAD_BYTE(mode,x,data,VRC6sound.Saw.Acc); + SAVELOAD_LONG(mode,x,data,VRC6sound.Saw.LCtr); +*/ return x; +} diff --git a/apps/nesemu2/src/mappers/sound/s_VRC6.h b/apps/nesemu2/src/mappers/sound/s_VRC6.h new file mode 100644 index 00000000..baa45f5a --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_VRC6.h @@ -0,0 +1,18 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_VRC6.h $ + * $Id: s_VRC6.h 376 2008-06-29 20:58:13Z Quietust $ + */ + +#ifndef S_VRC6_H +#define S_VRC6_H + +void VRC6sound_Load (void); +void VRC6sound_Reset (void); +void VRC6sound_Unload (void); +void VRC6sound_Write (int,int); +int VRC6sound_Get (int); +int VRC6sound_SaveLoad (int,int,unsigned char *); + +#endif /* S_VRC6_H */ diff --git a/apps/nesemu2/src/mappers/sound/s_VRC7.c b/apps/nesemu2/src/mappers/sound/s_VRC7.c new file mode 100644 index 00000000..b0338765 --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_VRC7.c @@ -0,0 +1,1488 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_VRC7.c $ + * $Id: s_VRC7.c 376 2008-06-29 20:58:13Z Quietust $ + */ + +/*********************************************************************************** + + emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001 + + 2001 01-08 : Version 0.10 -- 1st version. + 2001 01-15 : Version 0.20 -- semi-public version. + 2001 01-16 : Version 0.30 -- 1st public version. + 2001 01-17 : Version 0.31 -- Fixed bassdrum problem. + : Version 0.32 -- LPF implemented. + 2001 01-18 : Version 0.33 -- Fixed the drum problem, refine the mix-down method. + -- Fixed the LFO bug. + 2001 01-24 : Version 0.35 -- Fixed the drum problem, + support undocumented EG behavior. + 2001 02-02 : Version 0.38 -- Improved the performance. + Fixed the hi-hat and cymbal model. + Fixed the default percussive datas. + Noise reduction. + Fixed the feedback problem. + 2001 03-03 : Version 0.39 -- Fixed some drum bugs. + Improved the performance. + 2001 03-04 : Version 0.40 -- Improved the feedback. + Change the default table size. + Clock and Rate can be changed during play. + 2001 06-24 : Version 0.50 -- Improved the hi-hat and the cymbal tone. + Added VRC7 patch (OPLL_reset_patch is changed). + Fixed OPLL_reset() bug. + Added OPLL_setMask, OPLL_getMask and OPLL_toggleMask. + Added OPLL_writeIO. + 2001 09-28 : Version 0.51 -- Removed the noise table. + 2002 01-28 : Version 0.52 -- Added Stereo mode. + 2002 02-07 : Version 0.53 -- Fixed some drum bugs. + 2002 02-20 : Version 0.54 -- Added the best quality mode. + 2002 03-02 : Version 0.55 -- Removed OPLL_init & OPLL_close. + 2002 05-30 : Version 0.60 -- Fixed HH&CYM generator and all voice datas. + + 2003 01-24 : Modified by xodnizel to remove code not needed for the VRC7, among other things. + + References: + fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). + fmopl.c(fixed) -- (C) 2002 Jarek Burczynski. + s_opl.c -- 2001 written by Mamiya (NEZplug development). + fmgen.cpp -- 1999,2000 written by cisc. + fmpac.ill -- 2000 created by NARUTO. + MSX-Datapack + YMU757 data sheet + YM2143 data sheet + +**************************************************************************************/ +#include "misc/log.h" +#include "s_VRC7.h" +#include +#include +#include +#include +#include "types.h" + +#define PI 3.14159265358979323846 + +enum {OPLL_VRC7_TONE=0}; + +/* voice data */ +typedef struct { + u32 TL,FB,EG,ML,AR,DR,SL,RR,KR,KL,AM,PM,WF; +} OPLL_PATCH; + +/* slot */ +typedef struct { + OPLL_PATCH patch; + + s32 type; /* 0 : modulator 1 : carrier */ + + /* OUTPUT */ + s32 feedback; + s32 output[2]; /* Output value of slot */ + + /* for Phase Generator (PG) */ + u16 *sintbl; /* Wavetable */ + u32 phase; /* Phase */ + u32 dphase; /* Phase increment amount */ + u32 pgout; /* output */ + + /* for Envelope Generator (EG) */ + s32 fnum; /* F-Number */ + s32 block; /* Block */ + s32 volume; /* Current volume */ + s32 sustine; /* Sustine 1 = ON, 0 = OFF */ + u32 tll; /* Total Level + Key scale level*/ + u32 rks; /* Key scale offset (Rks) */ + s32 eg_mode; /* Current state */ + u32 eg_phase; /* Phase */ + u32 eg_dphase; /* Phase increment amount */ + u32 egout; /* output */ + +} OPLL_SLOT; + +/* Mask */ +#define OPLL_MASK_CH(x) (1<<(x)) + +/* opll */ +typedef struct { + + u32 adr; + s32 out; + +#ifndef EMU2413_COMPACTION + u32 realstep; + u32 oplltime; + u32 opllstep; + s32 prev, next; +#endif + + /* Register */ + u8 LowFreq[6]; + u8 HiFreq[6]; + u8 InstVol[6]; + + u8 CustInst[8]; + + s32 slot_on_flag[6 * 2]; + + /* Pitch Modulator */ + u32 pm_phase; + s32 lfo_pm; + + /* Amp Modulator */ + s32 am_phase; + s32 lfo_am; + + u32 quality; + + /* Channel Data */ + s32 patch_number[6]; + s32 key_status[6]; + + /* Slot */ + OPLL_SLOT slot[6 * 2]; + + u32 mask; + +} OPLL; + +/* Create Object */ +OPLL *OPLL_new(u32 clk, u32 rate); +void OPLL_delete(OPLL *); + +/* Setup */ +void OPLL_reset(OPLL *); +void OPLL_set_rate(OPLL *opll, u32 r); +void OPLL_set_quality(OPLL *opll, u32 q); + +/* Port/Register access */ +void OPLL_writeIO(OPLL *, u32 reg, u32 val); +void OPLL_writeReg(OPLL *, u32 reg, u32 val); + +/* Synthsize */ +s16 OPLL_calc(OPLL *); + +/* Misc */ +void OPLL_forceRefresh(OPLL *); + +/* Channel Mask */ +u32 OPLL_setMask(OPLL *, u32 mask); +u32 OPLL_toggleMask(OPLL *, u32 mask); + + +static const unsigned char default_inst[15][8] = { + {0x03,0x21,0x04,0x06,0x8D,0xF2,0x42,0x17}, + {0x13,0x41,0x05,0x0E,0x99,0x96,0x63,0x12}, + {0x31,0x11,0x10,0x0A,0xF0,0x9C,0x32,0x02}, + {0x21,0x61,0x1D,0x07,0x9F,0x64,0x20,0x27}, + {0x22,0x21,0x1E,0x06,0xF0,0x76,0x08,0x28}, + {0x02,0x01,0x06,0x00,0xF0,0xF2,0x03,0x95}, + {0x21,0x61,0x1C,0x07,0x82,0x81,0x16,0x07}, + {0x23,0x21,0x1A,0x17,0xEF,0x82,0x25,0x15}, + {0x25,0x11,0x1F,0x00,0x86,0x41,0x20,0x11}, + {0x85,0x01,0x1F,0x0F,0xE4,0xA2,0x11,0x12}, + {0x07,0xC1,0x2B,0x45,0xB4,0xF1,0x24,0xF4}, + {0x61,0x23,0x11,0x06,0x96,0x96,0x13,0x16}, + {0x01,0x02,0xD3,0x05,0x82,0xA2,0x31,0x51}, + {0x61,0x22,0x0D,0x02,0xC3,0x7F,0x24,0x05}, + {0x21,0x62,0x0E,0x00,0xA1,0xA0,0x44,0x17} +}; + +/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.)*/ +#define PG_BITS 9 +#define PG_WIDTH (1<>(b)) + +/* Leave the lower b bit(s). */ +#define LOWBITS(c,b) ((c)&((1<<(b))-1)) + +/* Expand x which is s bits to d bits. */ +#define EXPAND_BITS(x,s,d) ((x)<<((d)-(s))) + +/* Expand x which is s bits to d bits and fill expanded bits '1' */ +#define EXPAND_BITS_X(x,s,d) (((x)<<((d)-(s)))|((1<<((d)-(s)))-1)) + +/* Adjust envelope speed which depends on sampling rate. */ +#define rate_adjust(x) (rate==49716?x:(u32)((double)(x)*clk/72/rate + 0.5)) /* added 0.5 to round the value*/ + +#define MOD(o,x) (&(o)->slot[(x)<<1]) +#define CAR(o,x) (&(o)->slot[((x)<<1)|1]) + +#define BIT(s,b) (((s)>>(b))&1) + +/* Input clock */ +static u32 clk = 844451141; +/* Sampling rate */ +static u32 rate = 3354932; + +/* WaveTable for each envelope amp */ +static u16 fullsintable[PG_WIDTH]; +static u16 halfsintable[PG_WIDTH]; + +static u16 *waveform[2] = { fullsintable, halfsintable }; + +/* LFO Table */ +static s32 pmtable[PM_PG_WIDTH]; +static s32 amtable[AM_PG_WIDTH]; + +/* Phase delta for LFO */ +static u32 pm_dphase; +static u32 am_dphase; + +/* dB to Liner table */ +static s16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2]; + +/* Liner to Log curve conversion table (for Attack rate). */ +static u16 AR_ADJUST_TABLE[1 << EG_BITS]; + +/* Definition of envelope mode */ +enum +{ SETTLE, ATTACK, DECAY, SUSHOLD, SUSTINE, ERELEASE, FINISH }; + +/* Phase incr table for Attack */ +static u32 dphaseARTable[16][16]; +/* Phase incr table for Decay and ERELEASE */ +static u32 dphaseDRTable[16][16]; + +/* KSL + TL Table */ +static u32 tllTable[16][8][1 << TL_BITS][4]; +static s32 rksTable[2][8][2]; + +/* Phase incr table for PG */ +static u32 dphaseTable[512][8][16]; + +/*************************************************** + + Create tables + +****************************************************/ +__inline static s32 +Min (s32 i, s32 j) +{ + if (i < j) + return i; + else + return j; +} + +/* Table for AR to LogCurve. */ +static void +makeAdjustTable (void) +{ + s32 i; + + AR_ADJUST_TABLE[0] = (1 << EG_BITS); + for (i = 1; i < 128; i++) + AR_ADJUST_TABLE[i] = (u16) ((double) (1 << EG_BITS) - 1 - (1 << EG_BITS) * log (i) / log (128)); +} + + +/* Table for dB(0 -- (1<= DB_MUTE) DB2LIN_TABLE[i] = 0; + DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (s16) (-DB2LIN_TABLE[i]); + } +} + +/* Liner(+0.0 - +1.0) to dB((1<> (20 - DP_BITS)); +} + +static void +makeTllTable (void) +{ +#define dB2(x) ((x)*2) + + static double kltable[16] = { + dB2 (0.000), dB2 (9.000), dB2 (12.000), dB2 (13.875), dB2 (15.000), dB2 (16.125), dB2 (16.875), dB2 (17.625), + dB2 (18.000), dB2 (18.750), dB2 (19.125), dB2 (19.500), dB2 (19.875), dB2 (20.250), dB2 (20.625), dB2 (21.000) + }; + + s32 tmp; + s32 fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) + for (block = 0; block < 8; block++) + for (TL = 0; TL < 64; TL++) + for (KL = 0; KL < 4; KL++) + { + if (KL == 0) + { + tllTable[fnum][block][TL][KL] = TL2EG (TL); + } + else + { + tmp = (s32) (kltable[fnum] - dB2 (3.000) * (7 - block)); + if (tmp <= 0) + tllTable[fnum][block][TL][KL] = TL2EG (TL); + else + tllTable[fnum][block][TL][KL] = (u32) ((tmp >> (3 - KL)) / EG_STEP) + TL2EG (TL); + } + } +} + +#ifdef USE_SPEC_ENV_SPEED +static double attacktime[16][4] = { + {0, 0, 0, 0}, + {1730.15, 1400.60, 1153.43, 988.66}, + {865.08, 700.30, 576.72, 494.33}, + {432.54, 350.15, 288.36, 247.16}, + {216.27, 175.07, 144.18, 123.58}, + {108.13, 87.54, 72.09, 61.79}, + {54.07, 43.77, 36.04, 30.90}, + {27.03, 21.88, 18.02, 15.45}, + {13.52, 10.94, 9.01, 7.72}, + {6.76, 5.47, 4.51, 3.86}, + {3.38, 2.74, 2.25, 1.93}, + {1.69, 1.37, 1.13, 0.97}, + {0.84, 0.70, 0.60, 0.54}, + {0.50, 0.42, 0.34, 0.30}, + {0.28, 0.22, 0.18, 0.14}, + {0.00, 0.00, 0.00, 0.00} +}; + +static double decaytime[16][4] = { + {0, 0, 0, 0}, + {20926.60, 16807.20, 14006.00, 12028.60}, + {10463.30, 8403.58, 7002.98, 6014.32}, + {5231.64, 4201.79, 3501.49, 3007.16}, + {2615.82, 2100.89, 1750.75, 1503.58}, + {1307.91, 1050.45, 875.37, 751.79}, + {653.95, 525.22, 437.69, 375.90}, + {326.98, 262.61, 218.84, 187.95}, + {163.49, 131.31, 109.42, 93.97}, + {81.74, 65.65, 54.71, 46.99}, + {40.87, 32.83, 27.36, 23.49}, + {20.44, 16.41, 13.68, 11.75}, + {10.22, 8.21, 6.84, 5.87}, + {5.11, 4.10, 3.42, 2.94}, + {2.55, 2.05, 1.71, 1.47}, + {1.27, 1.27, 1.27, 1.27} +}; +#endif + +/* Rate Table for Attack */ +static void +makeDphaseARTable (void) +{ + s32 AR, Rks, RM, RL; +#ifdef USE_SPEC_ENV_SPEED + u32 attacktable[16][4]; + + for (RM = 0; RM < 16; RM++) + for (RL = 0; RL < 4; RL++) + { + if (RM == 0) + attacktable[RM][RL] = 0; + else if (RM == 15) + attacktable[RM][RL] = EG_DP_WIDTH; + else + attacktable[RM][RL] = (u32) ((double) (1 << EG_DP_BITS) / (attacktime[RM][RL] * 3579545 / 72000)); + + } +#endif + + for (AR = 0; AR < 16; AR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = AR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (AR) + { + case 0: + dphaseARTable[AR][Rks] = 0; + break; + case 15: + dphaseARTable[AR][Rks] = 0;/*EG_DP_WIDTH;*/ + break; + default: +#ifdef USE_SPEC_ENV_SPEED + dphaseARTable[AR][Rks] = rate_adjust (attacktable[RM][RL]); +#else + dphaseARTable[AR][Rks] = (u32)rate_adjust ((3 * (RL + 4) << (RM + 1))); +#endif + break; + } + } +} + +/* Rate Table for Decay and ERELEASE */ +static void +makeDphaseDRTable (void) +{ + s32 DR, Rks, RM, RL; + +#ifdef USE_SPEC_ENV_SPEED + u32 decaytable[16][4]; + + for (RM = 0; RM < 16; RM++) + for (RL = 0; RL < 4; RL++) + if (RM == 0) + decaytable[RM][RL] = 0; + else + decaytable[RM][RL] = (u32) ((double) (1 << EG_DP_BITS) / (decaytime[RM][RL] * 3579545 / 72000)); +#endif + + for (DR = 0; DR < 16; DR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = DR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (DR) + { + case 0: + dphaseDRTable[DR][Rks] = 0; + break; + default: +#ifdef USE_SPEC_ENV_SPEED + dphaseDRTable[DR][Rks] = rate_adjust (decaytable[RM][RL]); +#else + dphaseDRTable[DR][Rks] = (u32)rate_adjust ((RL + 4) << (RM - 1)); +#endif + break; + } + } +} + +static void +makeRksTable (void) +{ + + s32 fnum8, block, KR; + + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) + for (KR = 0; KR < 2; KR++) + { + if (KR != 0) + rksTable[fnum8][block][KR] = (block << 1) + fnum8; + else + rksTable[fnum8][block][KR] = block >> 1; + } +} + +/************************************************************ + + Calc Parameters + +************************************************************/ + +__inline static u32 +calc_eg_dphase (OPLL_SLOT * slot) +{ + + switch (slot->eg_mode) + { + case ATTACK: + return dphaseARTable[slot->patch.AR][slot->rks]; + + case DECAY: + return dphaseDRTable[slot->patch.DR][slot->rks]; + + case SUSHOLD: + return 0; + + case SUSTINE: + return dphaseDRTable[slot->patch.RR][slot->rks]; + + case ERELEASE: + if (slot->sustine) + return dphaseDRTable[5][slot->rks]; + else if (slot->patch.EG) + return dphaseDRTable[slot->patch.RR][slot->rks]; + else + return dphaseDRTable[7][slot->rks]; + + case FINISH: + return 0; + + default: + return 0; + } +} + +/************************************************************* + + OPLL internal interfaces + +*************************************************************/ + +#define UPDATE_PG(S) (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch.ML] +#define UPDATE_TLL(S)\ +(((S)->type==0)?\ +((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->patch.TL][(S)->patch.KL]):\ +((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->volume][(S)->patch.KL])) +#define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum)>>8][(S)->block][(S)->patch.KR] +#define UPDATE_WF(S) (S)->sintbl = waveform[(S)->patch.WF] +#define UPDATE_EG(S) (S)->eg_dphase = calc_eg_dphase(S) +#define UPDATE_ALL(S)\ + UPDATE_PG(S);\ + UPDATE_TLL(S);\ + UPDATE_RKS(S);\ + UPDATE_WF(S); \ + UPDATE_EG(S) /* EG should be updated last. */ + + +/* Slot key on */ +__inline static void +slotOn (OPLL_SLOT * slot) +{ + slot->eg_mode = ATTACK; + slot->eg_phase = 0; + slot->phase = 0; +} + +/* Slot key on without reseting the phase */ +__inline static void +slotOn2 (OPLL_SLOT * slot) +{ + slot->eg_mode = ATTACK; + slot->eg_phase = 0; +} + +/* Slot key off */ +__inline static void +slotOff (OPLL_SLOT * slot) +{ + if (slot->eg_mode == ATTACK) + slot->eg_phase = EXPAND_BITS (AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS); + slot->eg_mode = ERELEASE; +} + +/* Channel key on */ +__inline static void +keyOn (OPLL * opll, s32 i) +{ + if (!opll->slot_on_flag[i * 2]) + slotOn (MOD(opll,i)); + if (!opll->slot_on_flag[i * 2 + 1]) + slotOn (CAR(opll,i)); + opll->key_status[i] = 1; +} + +/* Channel key off */ +__inline static void +keyOff (OPLL * opll, s32 i) +{ + if (opll->slot_on_flag[i * 2 + 1]) + slotOff (CAR(opll,i)); + opll->key_status[i] = 0; +} + +/* Set sustine parameter */ +__inline static void +setSustine (OPLL * opll, s32 c, s32 sustine) +{ + CAR(opll,c)->sustine = sustine; + if (MOD(opll,c)->type) + MOD(opll,c)->sustine = sustine; +} + +/* Volume : 6bit ( Volume register << 2 ) */ +__inline static void +setVolume (OPLL * opll, s32 c, s32 volume) +{ + CAR(opll,c)->volume = volume; +} + +__inline static void +setSlotVolume (OPLL_SLOT * slot, s32 volume) +{ + slot->volume = volume; +} + +/* Set F-Number ( fnum : 9bit ) */ +__inline static void +setFnumber (OPLL * opll, s32 c, s32 fnum) +{ + CAR(opll,c)->fnum = fnum; + MOD(opll,c)->fnum = fnum; +} + +/* Set Block data (block : 3bit ) */ +__inline static void +setBlock (OPLL * opll, s32 c, s32 block) +{ + CAR(opll,c)->block = block; + MOD(opll,c)->block = block; +} + +__inline static void update_key_status (OPLL * opll) +{ + int ch; + + for (ch = 0; ch < 6; ch++) + opll->slot_on_flag[ch * 2] = opll->slot_on_flag[ch * 2 + 1] = (opll->HiFreq[ch]) & 0x10; +} + +/*********************************************************** + + Initializing + +***********************************************************/ + +static void +OPLL_SLOT_reset (OPLL_SLOT * slot, int type) +{ + slot->type = type; + slot->sintbl = waveform[0]; + slot->phase = 0; + slot->dphase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->feedback = 0; + slot->eg_mode = SETTLE; + slot->eg_phase = EG_DP_WIDTH; + slot->eg_dphase = 0; + slot->rks = 0; + slot->tll = 0; + slot->sustine = 0; + slot->fnum = 0; + slot->block = 0; + slot->volume = 0; + slot->pgout = 0; + slot->egout = 0; +} + +static void +internal_refresh (void) +{ + makeDphaseTable (); + makeDphaseARTable (); + makeDphaseDRTable (); + pm_dphase = (u32) rate_adjust (PM_SPEED * PM_DP_WIDTH / (clk / 72)); + am_dphase = (u32) rate_adjust (AM_SPEED * AM_DP_WIDTH / (clk / 72)); +} + +static void +maketables (u32 c, u32 r) +{ + if (c != clk) + { + clk = c; + makePmTable (); + makeAmTable (); + makeDB2LinTable (); + makeAdjustTable (); + makeTllTable (); + makeRksTable (); + makeSinTable (); + //makeDefaultPatch (); + } + + if (r != rate) + { + rate = r; + internal_refresh (); + } +} + +OPLL *OPLL_new (u32 clk2, u32 rate2) +{ + OPLL *opll; + + maketables (clk2, rate2); + + opll = (OPLL *) calloc (sizeof (OPLL), 1); + if (opll == NULL) + return NULL; + + opll->mask = 0; + + OPLL_reset (opll); + + return opll; +} + + +void +OPLL_delete (OPLL * opll) +{ + free (opll); +} + +/* Reset whole of OPLL except patch datas. */ +void +OPLL_reset (OPLL * opll) +{ + s32 i; + + if (!opll) + return; + + opll->adr = 0; + opll->out = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->mask = 0; + + for (i = 0; i < 12; i++) + OPLL_SLOT_reset(&opll->slot[i], i%2); + + for (i = 0; i < 6; i++) + { + opll->key_status[i] = 0; + //setPatch (opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg (opll, i, 0); + +#ifndef EMU2413_COMPACTION + opll->realstep = (u32) ((1 << 31) / rate); + opll->opllstep = (u32) ((1 << 31) / (clk / 72)); + opll->oplltime = 0; +#endif +} + +/* Force Refresh (When external program changes some parameters). */ +void +OPLL_forceRefresh (OPLL * opll) +{ + s32 i; + + if (opll == NULL) + return; + + for (i = 0; i < 12; i++) + { + UPDATE_PG (&opll->slot[i]); + UPDATE_RKS (&opll->slot[i]); + UPDATE_TLL (&opll->slot[i]); + UPDATE_WF (&opll->slot[i]); + UPDATE_EG (&opll->slot[i]); + } +} + +void +OPLL_set_rate (OPLL * opll, u32 r) +{ + if (opll->quality) + rate = 49716; + else + rate = r; + internal_refresh (); + rate = r; +} + +void +OPLL_set_quality (OPLL * opll, u32 q) +{ + opll->quality = q; + OPLL_set_rate (opll, rate); +} + +/********************************************************* + + Generate wave data + +*********************************************************/ +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */ +#if ( SLOT_AMP_BITS - PG_BITS ) > 0 +#define wave2_2pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS )) +#else +#define wave2_2pi(e) ( (e) << ( PG_BITS - SLOT_AMP_BITS )) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */ +#if ( SLOT_AMP_BITS - PG_BITS - 1 ) == 0 +#define wave2_4pi(e) (e) +#elif ( SLOT_AMP_BITS - PG_BITS - 1 ) > 0 +#define wave2_4pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 1 )) +#else +#define wave2_4pi(e) ( (e) << ( 1 + PG_BITS - SLOT_AMP_BITS )) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */ +#if ( SLOT_AMP_BITS - PG_BITS - 2 ) == 0 +#define wave2_8pi(e) (e) +#elif ( SLOT_AMP_BITS - PG_BITS - 2 ) > 0 +#define wave2_8pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 2 )) +#else +#define wave2_8pi(e) ( (e) << ( 2 + PG_BITS - SLOT_AMP_BITS )) +#endif + + + +/* Update AM, PM unit */ +static void +update_ampm (OPLL * opll) +{ + opll->pm_phase = (opll->pm_phase + pm_dphase) & (PM_DP_WIDTH - 1); + opll->am_phase = (opll->am_phase + am_dphase) & (AM_DP_WIDTH - 1); + opll->lfo_am = amtable[HIGHBITS (opll->am_phase, AM_DP_BITS - AM_PG_BITS)]; + opll->lfo_pm = pmtable[HIGHBITS (opll->pm_phase, PM_DP_BITS - PM_PG_BITS)]; +} + +/* PG */ +__inline static void +calc_phase (OPLL_SLOT * slot, s32 lfo) +{ + if (slot->patch.PM) + slot->phase += (slot->dphase * lfo) >> PM_AMP_BITS; + else + slot->phase += slot->dphase; + + slot->phase &= (DP_WIDTH - 1); + + slot->pgout = HIGHBITS (slot->phase, DP_BASE_BITS); +} + +/* EG */ +static void +calc_envelope (OPLL_SLOT * slot, s32 lfo) +{ +#define S2E(x) (SL2EG((s32)(x/SL_STEP))<<(EG_DP_BITS-EG_BITS)) + + static u32 SL[16] = { + S2E (0.0), S2E (3.0), S2E (6.0), S2E (9.0), S2E (12.0), S2E (15.0), S2E (18.0), S2E (21.0), + S2E (24.0), S2E (27.0), S2E (30.0), S2E (33.0), S2E (36.0), S2E (39.0), S2E (42.0), S2E (48.0) + }; + + u32 egout; + + switch (slot->eg_mode) + { + + case ATTACK: + egout = AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)]; + slot->eg_phase += slot->eg_dphase; + if((EG_DP_WIDTH & slot->eg_phase)||(slot->patch.AR==15)) + { + egout = 0; + slot->eg_phase = 0; + slot->eg_mode = DECAY; + UPDATE_EG (slot); + } + break; + + case DECAY: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (slot->eg_phase >= SL[slot->patch.SL]) + { + if (slot->patch.EG) + { + slot->eg_phase = SL[slot->patch.SL]; + slot->eg_mode = SUSHOLD; + UPDATE_EG (slot); + } + else + { + slot->eg_phase = SL[slot->patch.SL]; + slot->eg_mode = SUSTINE; + UPDATE_EG (slot); + } + } + break; + + case SUSHOLD: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + if (slot->patch.EG == 0) + { + slot->eg_mode = SUSTINE; + UPDATE_EG (slot); + } + break; + + case SUSTINE: + case ERELEASE: + egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (egout >= (1 << EG_BITS)) + { + slot->eg_mode = FINISH; + egout = (1 << EG_BITS) - 1; + } + break; + + case FINISH: + egout = (1 << EG_BITS) - 1; + break; + + default: + egout = (1 << EG_BITS) - 1; + break; + } + + if (slot->patch.AM) + egout = EG2DB (egout + slot->tll) + lfo; + else + egout = EG2DB (egout + slot->tll); + + if (egout >= DB_MUTE) + egout = DB_MUTE - 1; + + slot->egout = egout; +} + +/* CARRIOR */ +__inline static s32 +calc_slot_car (OPLL_SLOT * slot, s32 fm) +{ + slot->output[1] = slot->output[0]; + + if (slot->egout >= (DB_MUTE - 1)) + { + slot->output[0] = 0; + } + else + { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout]; + } + + return (slot->output[1] + slot->output[0]) >> 1; +} + +/* MODULATOR */ +__inline static s32 +calc_slot_mod (OPLL_SLOT * slot) +{ + s32 fm; + + slot->output[1] = slot->output[0]; + + if (slot->egout >= (DB_MUTE - 1)) + { + slot->output[0] = 0; + } + else if (slot->patch.FB != 0) + { + fm = wave2_4pi (slot->feedback) >> (7 - slot->patch.FB); + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout + fm)&(PG_WIDTH-1)] + slot->egout]; + } + else + { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; + } + + slot->feedback = (slot->output[1] + slot->output[0]) >> 1; + + return slot->feedback; + +} + +static __inline s16 calc (OPLL * opll) +{ + s32 inst = 0, out = 0; + s32 i; + + update_ampm (opll); + + for (i = 0; i < 12; i++) + { + calc_phase(&opll->slot[i],opll->lfo_pm); + calc_envelope(&opll->slot[i],opll->lfo_am); + } + + for (i = 0; i < 6; i++) + if (!(opll->mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH)) + inst += calc_slot_car (CAR(opll,i), calc_slot_mod(MOD(opll,i))); + + out = inst; + return (s16) out; +} + +#ifdef EMU2413_COMPACTION +s16 +OPLL_calc (OPLL * opll) +{ + return calc (opll); +} +#else +s16 +OPLL_calc (OPLL * opll) +{ + if (!opll->quality) + return calc (opll); + + while (opll->realstep > opll->oplltime) + { + opll->oplltime += opll->opllstep; + opll->prev = opll->next; + opll->next = calc (opll); + } + + opll->oplltime -= opll->realstep; + opll->out = (s16) (((double) opll->next * (opll->opllstep - opll->oplltime) + + (double) opll->prev * opll->oplltime) / opll->opllstep); + + return (s16) opll->out; +} +#endif + +u32 +OPLL_setMask (OPLL * opll, u32 mask) +{ + u32 ret; + + if (opll) + { + ret = opll->mask; + opll->mask = mask; + return ret; + } + else + return 0; +} + +u32 +OPLL_toggleMask (OPLL * opll, u32 mask) +{ + u32 ret; + + if (opll) + { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } + else + return 0; +} + +/**************************************************** + + I/O Ctrl + +*****************************************************/ + +static void setInstrument(OPLL * opll, unsigned int i, unsigned int inst) +{ + const u8 *src; + OPLL_PATCH *modp, *carp; + + opll->patch_number[i]=inst; + + if(inst) + src=default_inst[inst-1]; + else + src=opll->CustInst; + + modp=&MOD(opll,i)->patch; + carp=&CAR(opll,i)->patch; + + modp->AM=(src[0]>>7)&1; + modp->PM=(src[0]>>6)&1; + modp->EG=(src[0]>>5)&1; + modp->KR=(src[0]>>4)&1; + modp->ML=(src[0]&0xF); + + carp->AM=(src[1]>>7)&1; + carp->PM=(src[1]>>6)&1; + carp->EG=(src[1]>>5)&1; + carp->KR=(src[1]>>4)&1; + carp->ML=(src[1]&0xF); + + modp->KL=(src[2]>>6)&3; + modp->TL=(src[2]&0x3F); + + carp->KL = (src[3] >> 6) & 3; + carp->WF = (src[3] >> 4) & 1; + + modp->WF = (src[3] >> 3) & 1; + + modp->FB = (src[3]) & 7; + + modp->AR = (src[4]>>4)&0xF; + modp->DR = (src[4]&0xF); + + carp->AR = (src[5]>>4)&0xF; + carp->DR = (src[5]&0xF); + + modp->SL = (src[6]>>4)&0xF; + modp->RR = (src[6]&0xF); + + carp->SL = (src[7]>>4)&0xF; + carp->RR = (src[7]&0xF); +} + + +void +OPLL_writeReg (OPLL * opll, u32 reg, u32 data) +{ + + s32 i, v, ch; + + data = data & 0xff; + reg = reg & 0x3f; + + switch (reg) + { + case 0x00: + opll->CustInst[0]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_PG (MOD(opll,i)); + UPDATE_RKS (MOD(opll,i)); + UPDATE_EG (MOD(opll,i)); + } + } + break; + + case 0x01: + opll->CustInst[1]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_PG (CAR(opll,i)); + UPDATE_RKS (CAR(opll,i)); + UPDATE_EG (CAR(opll,i)); + } + } + break; + + case 0x02: + opll->CustInst[2]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_TLL(MOD(opll,i)); + } + } + break; + + case 0x03: + opll->CustInst[3]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_WF(MOD(opll,i)); + UPDATE_WF(CAR(opll,i)); + } + } + break; + + case 0x04: + opll->CustInst[4]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_EG (MOD(opll,i)); + } + } + break; + + case 0x05: + opll->CustInst[5]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_EG(CAR(opll,i)); + } + } + break; + + case 0x06: + opll->CustInst[6]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_EG (MOD(opll,i)); + } + } + break; + + case 0x07: + opll->CustInst[7]=(u8)data; + for (i = 0; i < 6; i++) + { + if (opll->patch_number[i] == 0) + { + setInstrument(opll, i, 0); + UPDATE_EG (CAR(opll,i)); + } + } + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + ch = reg - 0x10; + opll->LowFreq[ch]=(u8)data; + setFnumber (opll, ch, data + ((opll->HiFreq[ch] & 1) << 8)); + UPDATE_ALL (MOD(opll,ch)); + UPDATE_ALL (CAR(opll,ch)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + ch = reg - 0x20; + opll->HiFreq[ch]=(u8)data; + + setFnumber (opll, ch, ((data & 1) << 8) + opll->LowFreq[ch]); + setBlock (opll, ch, (data >> 1) & 7); + setSustine (opll, ch, (data >> 5) & 1); + if (data & 0x10) + keyOn (opll, ch); + else + keyOff (opll, ch); + UPDATE_ALL (MOD(opll,ch)); + UPDATE_ALL (CAR(opll,ch)); + update_key_status (opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + opll->InstVol[reg-0x30]=(u8)data; + i = (data >> 4) & 15; + v = data & 15; + setInstrument(opll, reg-0x30, i); + setVolume (opll, reg - 0x30, v << 2); + UPDATE_ALL (MOD(opll,reg - 0x30)); + UPDATE_ALL (CAR(opll,reg - 0x30)); + break; + + default: + break; + + } +} + +// Konami VRC7, based on the YM2413 + +OPLL *OPL = NULL; + +void VRC7sound_Load (void) +{ + if (OPL != NULL) + { +// MessageBox(hWnd,_T("YM2413 already created!"),_T("VRC7"),MSGBOX_FLAGS); + log_printf("YM2413 already created!\n"); + return; + } + else + { + OPL = OPLL_new(3579545,44100); + if (OPL == NULL) + { +// MessageBox(hWnd,_T("Unable to create YM2413!"),_T("VRC7"),MSGBOX_FLAGS); + log_printf("Unable to create YM2413!\n"); + return; + } + } +} + +void VRC7sound_Reset (void) +{ +} + +void VRC7sound_Unload (void) +{ + if (OPL) + { + OPLL_delete(OPL); + OPL = NULL; + } +// else MessageBox(hWnd,_T("Unable to destroy YM2413!"),_T("VRC7"),MSGBOX_FLAGS); +} + +void VRC7sound_Write (int Addr, int Val) +{ + static unsigned char addr = 0; + switch (Addr & 0xF030) + { + case 0x9010: addr = Val; break; + case 0x9030: OPLL_writeReg(OPL,addr,Val); break; + } +} + +int VRC7sound_Get (int numCycles) +{ + return OPLL_calc(OPL) << 3; // currently don't use numCycles +} + +int VRC7sound_SaveLoad (int mode, int x, unsigned char *data) +{ +/* if (mode == STATE_SAVE) + { + memcpy(data+x,OPL,sizeof(OPLL)); + x += sizeof(OPLL); + } + else if (mode == STATE_LOAD) + { + int i; + memcpy(OPL,data+x,sizeof(OPLL)); + x += sizeof(OPLL); + for (i = 0; i < 6; i++) + { + UPDATE_ALL(MOD(OPL,i)); + UPDATE_ALL(CAR(OPL,i)); + } + } + else if (mode == STATE_SIZE) + x += sizeof(OPLL); + else MessageBox(hWnd,_T("Invalid save/load type!"),_T(__FILE__),MB_OK); +*/ return x; +} diff --git a/apps/nesemu2/src/mappers/sound/s_VRC7.h b/apps/nesemu2/src/mappers/sound/s_VRC7.h new file mode 100644 index 00000000..e9f8cade --- /dev/null +++ b/apps/nesemu2/src/mappers/sound/s_VRC7.h @@ -0,0 +1,18 @@ +/* Nintendulator Mapper DLLs + * Copyright (C) 2002-2008 QMT Productions + * + * $URL: file:///c:/svnroot/Mappers/src/Hardware/Sound/s_VRC7.h $ + * $Id: s_VRC7.h 376 2008-06-29 20:58:13Z Quietust $ + */ + +#ifndef S_VRC7_H +#define S_VRC7_H + +void VRC7sound_Load (void); +void VRC7sound_Reset (void); +void VRC7sound_Unload (void); +void VRC7sound_Write (int,int); +int VRC7sound_Get (int); +int VRC7sound_SaveLoad (int,int,unsigned char *); + +#endif /* S_VRC7_H */ diff --git a/apps/nesemu2/src/mappers/unif.c b/apps/nesemu2/src/mappers/unif.c new file mode 100644 index 00000000..39443bd2 --- /dev/null +++ b/apps/nesemu2/src/mappers/unif.c @@ -0,0 +1,203 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "mapperinc.h" +#include "misc/slre/slre.h" + +struct unif_board_s { + const char *name; + int boardid; +}; + +#define UNIF_BOARD_START() static struct unif_board_s boards[] = { +#define UNIF_BOARD_END() {0,-1}}; +#define UNIF_BOARD(s,b) {s,b}, + +UNIF_BOARD_START() + + //licensed + //nintendo boards + UNIF_BOARD(".+?(-)A[MNO](|1)ROM", B_NINTENDO_AxROM) + UNIF_BOARD(".+?(-)CNROM", B_NINTENDO_CNROM) + UNIF_BOARD(".+?(-)CPROM", B_NINTENDO_CPROM) + UNIF_BOARD(".+?(-)D[ER](|1)ROM", B_NINTENDO_DxROM) + UNIF_BOARD(".+?(-)E[KLTW]ROM", B_NINTENDO_ExROM) + UNIF_BOARD(".+?(-)F[JK]ROM", B_NINTENDO_FxROM) + UNIF_BOARD(".+?(-)(GN|MH)ROM", B_NINTENDO_GxROM_MxROM) + UNIF_BOARD(".+?(-)HKROM", B_NINTENDO_HxROM) + UNIF_BOARD(".+?(-)(N|R)ROM(|-.*?)", B_NINTENDO_NROM) + UNIF_BOARD(".+?(-)P(EEO|N)ROM", B_NINTENDO_PxROM) + UNIF_BOARD(".+?(-)S[ABCEFGHIJKLNOU](|W)(|1|EP)ROM",B_NINTENDO_SxROM) + UNIF_BOARD(".+?(-)T[^Q](|1|EP)ROM", B_NINTENDO_TxROM) + UNIF_BOARD(".+?(-)TQ(|EP)ROM", B_NINTENDO_TQROM) + UNIF_BOARD(".+?(-)U[NO](|EP)ROM", B_NINTENDO_UxROM) + UNIF_BOARD(".+?(-)UN1ROM", B_NINTENDO_UN1ROM) + UNIF_BOARD("NES-EVENT", B_NINTENDO_EVENT) + UNIF_BOARD("NES-QJ", B_NINTENDO_QJ) + + //jaleco + UNIF_BOARD("JALECO-JF-0[1234]", B_NINTENDO_NROM) + UNIF_BOARD("JALECO-JF-11", B_JALECO_JF11) + UNIF_BOARD("JALECO-JF-(15|18|39)", B_NINTENDO_UxROM) //verify JALECO-JF-39 + UNIF_BOARD("JALECO-JF-16", B_JALECO_JF16) + UNIF_BOARD("JALECO-JF-2[34579]", B_JALECO_SS88006) + UNIF_BOARD("JALECO-JF-3[012345678]", B_JALECO_SS88006) + UNIF_BOARD("JALECO-JF-4[01]", B_JALECO_SS88006) + + //konami boards + UNIF_BOARD("KONAMI-VRC-1", B_KONAMI_VRC1) + UNIF_BOARD("KONAMI-VRC-2", B_KONAMI_VRC2) + UNIF_BOARD("KONAMI-VRC-2A", B_KONAMI_VRC2A) + UNIF_BOARD("KONAMI-VRC-2B", B_KONAMI_VRC2B) + UNIF_BOARD("KONAMI-VRC-3", B_KONAMI_VRC3) + UNIF_BOARD("KONAMI-VRC-4", B_KONAMI_VRC4B) + UNIF_BOARD("KONAMI-VRC-4A", B_KONAMI_VRC4A) + UNIF_BOARD("KONAMI-VRC-4B", B_KONAMI_VRC4B) + UNIF_BOARD("KONAMI-VRC-4C", B_KONAMI_VRC4C) + UNIF_BOARD("KONAMI-VRC-4D", B_KONAMI_VRC4D) + UNIF_BOARD("KONAMI-VRC-4E", B_KONAMI_VRC4E) + UNIF_BOARD("KONAMI-VRC-6", B_KONAMI_VRC6B) + UNIF_BOARD("KONAMI-VRC-6A", B_KONAMI_VRC6A) + UNIF_BOARD("KONAMI-VRC-6B", B_KONAMI_VRC6B) + UNIF_BOARD("KONAMI-VRC-7", B_KONAMI_VRC7B) + UNIF_BOARD("KONAMI-VRC-7A", B_KONAMI_VRC7A) + UNIF_BOARD("KONAMI-VRC-7B", B_KONAMI_VRC7B) + UNIF_BOARD("KONAMI-74*139/74", B_NINTENDO_CNROM) //appears to be CNROM + + //namcot + UNIF_BOARD("NAMCOT-3414", B_NAMCOT_34x3) + UNIF_BOARD("NAMCOT-34[345]3", B_NAMCOT_34x3) + UNIF_BOARD("NAMCOT-3425", B_NAMCOT_3425) + + //sunsoft + UNIF_BOARD("SUNSOFT-1", B_SUNSOFT_1) + UNIF_BOARD("SUNSOFT-2", B_SUNSOFT_2) + UNIF_BOARD("SUNSOFT-3", B_SUNSOFT_3) + UNIF_BOARD("SUNSOFT-4", B_SUNSOFT_4) + UNIF_BOARD("SUNSOFT-5[AB]", B_SUNSOFT_5B) + + //irem + UNIF_BOARD("IREM-TAM-S1", B_IREM_TAM_S1) + UNIF_BOARD("IREM-HOLYDIVER", B_IREM_HOLYDIVER) + UNIF_BOARD("IREM-H3001", B_IREM_H3001) + + //taito + UNIF_BOARD("TAITO-X1-005", B_TAITO_X1_005) + UNIF_BOARD("TAITO-X1-017", B_TAITO_X1_017) + + //unlicensed + //camerica + UNIF_BOARD("CAMERICA-BF9093", B_CAMERICA_BF9093) + UNIF_BOARD("CAMERICA-ALGN", B_CAMERICA_BF9093) + UNIF_BOARD("CODEMASTERS-NR8N", B_CAMERICA_BF9093) + UNIF_BOARD("CAMERICA-BF9096", B_CAMERICA_BF9096) + UNIF_BOARD("CAMERICA-BF9097", B_CAMERICA_BF9097) + + //tengen + UNIF_BOARD("TENGEN-8000(02|04|30)", B_TENGEN_MIMIC_1) + UNIF_BOARD("TENGEN-800003", B_NINTENDO_NROM) + UNIF_BOARD("TENGEN-80003[27]", B_TENGEN_RAMBO_1) + UNIF_BOARD("TENGEN-800042", B_SUNSOFT_4) //if it works, let it + + //txc + UNIF_BOARD("UNL-22211(|-A)", B_TXC_22211A) + UNIF_BOARD("UNL-22211-B", B_TXC_22211B) + UNIF_BOARD("UNL-22211-C", B_TXC_22211C) + + //ave + UNIF_BOARD("AVE-NINA-01", B_AVE_NINA_001) + UNIF_BOARD("AVE-NINA-0[36]", B_AVE_NINA_006) + + //sachen + UNIF_BOARD("UNL-SACHEN-74LS374N", B_SACHEN_74LS374N) + UNIF_BOARD("UNL-SACHEN-8259", B_SACHEN_8259A) + UNIF_BOARD("UNL-SACHEN-8259A", B_SACHEN_8259A) + UNIF_BOARD("UNL-SACHEN-8259B", B_SACHEN_8259B) + UNIF_BOARD("UNL-SACHEN-8259C", B_SACHEN_8259C) + UNIF_BOARD("UNL-SACHEN-8259D", B_SACHEN_8259D) + UNIF_BOARD("UNL-SA-72007", B_SA_72007) + UNIF_BOARD("UNL-SA-72008", B_SA_72008) + UNIF_BOARD("UNL-SA-0036", B_SA_0036) + UNIF_BOARD("UNL-SA-0037", B_SA_0037) + UNIF_BOARD("UNL-SA-016-1M", B_SA_0161M) + + //bootleg multicarts + UNIF_BOARD("BMC-70IN1", B_BMC_70IN1) + UNIF_BOARD("BMC-70IN1B", B_BMC_70IN1B) + UNIF_BOARD("BMC-SUPER700IN1", B_BMC_SUPER700IN1) + UNIF_BOARD("BMC-SUPER42IN1", B_BMC_76IN1) + UNIF_BOARD("BMC-FK23C", B_BMC_FK23C) + UNIF_BOARD("BMC-260IN1", B_BMC_GOLDENGAME260IN1) + + //bootlegs + UNIF_BOARD("BTL-MARIO1-MALEE2", B_BTL_MARIO1_MALEE2) + UNIF_BOARD("BTL-BIOMIRACLEA", B_BTL_BIOMIRACLEA) + + //multicarts + UNIF_BOARD("MLT-CALTRON6IN1", B_NTDEC_CALTRON6IN1) + UNIF_BOARD("MLT-ACTION52", B_ACTIVE) + + //other + UNIF_BOARD("UNL-RACERMATE", B_UNL_RACERMATE) + UNIF_BOARD("UNL-H2288", B_UNL_H2288) + UNIF_BOARD("UNL-FS304", B_UNL_FS304) + UNIF_BOARD("UNL-KASING", B_KASING) + UNIF_BOARD("UNL-SUPERLIONKING", B_UNL_SUPERLIONKING) + UNIF_BOARD("COOLBOY", B_COOLBOY) + + //homebrew + UNIF_BOARD("UNL-DRIPGAME", B_DRIPGAME) + + //unofficial, need verification or real board name + UNIF_BOARD("BMC-65IN1", B_BMC_65IN1) + +UNIF_BOARD_END() + +int mapper_get_mapperid_unif(char *str) +{ + int ret,i; + const char *err; + char tmpstr[4][256]; + + ret = B_UNSUPPORTED; + for(i=0;boards[i].boardid != -1;i++) { + err = slre_match(SLRE_CASE_INSENSITIVE,boards[i].name,str,strlen(str), + SLRE_STRING,sizeof(tmpstr[0]),tmpstr[0], + SLRE_STRING,sizeof(tmpstr[1]),tmpstr[1], + SLRE_STRING,sizeof(tmpstr[2]),tmpstr[2], + SLRE_STRING,sizeof(tmpstr[3]),tmpstr[3]); + if(err == NULL) { + if(ret != B_UNSUPPORTED) + log_printf("mapper_get_mapperid_unif: duplicate match for board '%s'\n",str); + ret = boards[i].boardid; + } + else if(stricmp((char *)err,"no match") != 0) + log_printf("mapper_get_mapperid_unif: slre error: %s ('%s' -- '%s')\n",err,boards[i].name,str); + } + return(ret); +} + +const char *mapper_get_unif_boardname(int idx) +{ + if(boards[idx].boardid == -1 || boards[idx].name == 0) + return(0); + return(boards[idx].name); +} diff --git a/apps/nesemu2/src/misc/config.c b/apps/nesemu2/src/misc/config.c new file mode 100644 index 00000000..1fe8090e --- /dev/null +++ b/apps/nesemu2/src/misc/config.c @@ -0,0 +1,366 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/config.h" +#include "misc/vars.h" +#include "misc/memutil.h" +#include "misc/log.h" +#include "misc/paths.h" +#include "system/main.h" +#include "system/system.h" +#include "version.h" + +static vars_t *configvars = 0; + +//this path stuff needs to be moved +static void mkdirr(char *path) +{ +#if 0 + char *tmp = mem_strdup(path); + char *p = tmp; + int num = 0; + + //normalize the path string + paths_normalize(tmp); + + log_printf("mkdirr: creating directory '%s'\n",path); + + //make all the directories between the root and the desired directory + for(num=0,p=tmp;(p = strchr(p,PATH_SEPERATOR));num++) { + if(num == 0) { + p++; + continue; + } + *p = 0; + mkdir(tmp); + chmod(tmp,0755); + *p = PATH_SEPERATOR; + p++; + } + + //now create the directory we want + mkdir(path); + chmod(path,0755); + + //free tmp string + mem_free(tmp); +#endif +} + +static void makepath(char *str) +{ + //test if the path exists already, if not create it + if(access(str,0) != 0) + mkdirr(str); +} + +//this function looks around for a configuration file. it checks: +// 1. current working directory +// 2. $HOME directory +// 3. same directory the executable is in +//if it isnt found, it defaults the executable directory +static int findconfig(char *dest) +{ + char *cwd = system_getcwd(); + char *home = getenv("HOME"); + + //first see if the configfilename string isnt empty + if(strcmp(dest,"") != 0) { + return(0); + } + + //now look in the current working directory + sprintf(dest,"%s%c%s",cwd,PATH_SEPERATOR,CONFIG_FILENAME); + log_printf("looking for configuration at '%s'\n",dest); + if(access(dest,06) == 0) { + return(0); + } + + //check the users home directory + if(home) { + sprintf(dest,"%s%c.nesemu2%c%s",home,PATH_SEPERATOR,PATH_SEPERATOR,CONFIG_FILENAME); + log_printf("looking for configuration at '%s'\n",dest); + if(access(dest,06) == 0) { + return(0); + } + } + +#ifdef WIN32 + //win32 it is ok to store in the same directory as executable + //now check the executable directory + sprintf(dest,"%s%c%s",exepath,PATH_SEPERATOR,CONFIG_FILENAME); + log_printf("looking for configuration at '%s'\n",dest); + if(access(dest,06) == 0) { + return(0); + } + + //set default configuration filename + sprintf(dest,"%s%c%s",exepath,PATH_SEPERATOR,CONFIG_FILENAME); + +#else + //linux it is not ok to store in the same directory as executable (/usr/bin or something) + if(home) { + sprintf(dest,"%s%c.nesemu2%c%s",home,PATH_SEPERATOR,PATH_SEPERATOR,CONFIG_FILENAME); + } + else { + sprintf(dest,"%s%c%s",cwd,PATH_SEPERATOR,CONFIG_FILENAME); + log_printf("system_findconfig: HOME environment var not set! using current directory.\n"); + } +#endif + + return(1); +} + +//initialize the configuration defaults +static vars_t *config_get_defaults() +{ + vars_t *ret = vars_create(); + char *str; + + vars_set_int (ret,F_CONFIG,"video.framelimit", 1); + vars_set_int (ret,F_CONFIG,"video.fullscreen", 0); + vars_set_int (ret,F_CONFIG,"video.scale", 1); + vars_set_string(ret,F_CONFIG,"video.filter", "none"); + + vars_set_string(ret,F_CONFIG,"input.port0", "joypad0"); + vars_set_string(ret,F_CONFIG,"input.port1", "joypad1"); + vars_set_string(ret,F_CONFIG,"input.expansion", "none"); + + vars_set_int (ret,F_CONFIG,"input.joypad0.a", 'j'); + vars_set_int (ret,F_CONFIG,"input.joypad0.b", 'k'); + vars_set_int (ret,F_CONFIG,"input.joypad0.select", 'u'); + vars_set_int (ret,F_CONFIG,"input.joypad0.start", 'i'); + vars_set_int (ret,F_CONFIG,"input.joypad0.up", 'w'); + vars_set_int (ret,F_CONFIG,"input.joypad0.down", 's'); + vars_set_int (ret,F_CONFIG,"input.joypad0.left", 'a'); + vars_set_int (ret,F_CONFIG,"input.joypad0.right", 'd'); + + vars_set_int (ret,F_CONFIG,"input.joypad1.a", '1'); + vars_set_int (ret,F_CONFIG,"input.joypad1.b", '2'); + vars_set_int (ret,F_CONFIG,"input.joypad1.select", '3'); + vars_set_int (ret,F_CONFIG,"input.joypad1.start", '4'); + vars_set_int (ret,F_CONFIG,"input.joypad1.up", '5'); + vars_set_int (ret,F_CONFIG,"input.joypad1.down", '6'); + vars_set_int (ret,F_CONFIG,"input.joypad1.left", '7'); + vars_set_int (ret,F_CONFIG,"input.joypad1.right", '8'); + + vars_set_string(ret,F_CONFIG,"input.zapper.trigger", "mb0"); + + vars_set_int (ret,F_CONFIG,"sound.enabled", 1); + +#ifdef WIN32 + vars_set_string(ret,F_CONFIG,"path.data", "%exepath%/data"); + vars_set_string(ret,F_CONFIG,"path.user", "%path.data%"); +#else + vars_set_string(ret,F_CONFIG,"path.data", "/usr/share/nesemu2"); + vars_set_string(ret,F_CONFIG,"path.user", "%home%/.nesemu2"); +#endif + + vars_set_string(ret,F_CONFIG,"path.bios", "%path.data%/bios"); + vars_set_string(ret,F_CONFIG,"path.patch", "%path.data%/patch"); + vars_set_string(ret,F_CONFIG,"path.palette", "%path.data%/palette"); + vars_set_string(ret,F_CONFIG,"path.xml", "%path.data%/xml"); + + vars_set_string(ret,F_CONFIG,"path.save", "%path.user%/save"); + vars_set_string(ret,F_CONFIG,"path.state", "%path.user%/state"); + vars_set_string(ret,F_CONFIG,"path.cheat", "%path.user%/cheat"); + + vars_set_string(ret,F_CONFIG,"palette.source", "generator"); + vars_set_int (ret,F_CONFIG,"palette.hue", -15); + vars_set_int (ret,F_CONFIG,"palette.saturation", 45); + vars_set_string(ret,F_CONFIG,"palette.filename", "roni.pal"); + + vars_set_string(ret,F_CONFIG,"nes.gamegenie.bios", "genie.rom"); + vars_set_int (ret,F_CONFIG,"nes.gamegenie.enabled", 0); + + vars_set_string(ret,F_CONFIG,"nes.fds.bios", "hlefds.bin"); + vars_set_int (ret,F_CONFIG,"nes.fds.hle", 1); + + vars_set_string(ret,F_CONFIG,"nes.nsf.bios", "nsfbios.bin"); + + vars_set_string(ret,F_CONFIG,"nes.region", "ntsc"); + vars_set_int (ret,F_CONFIG,"nes.log_unhandled_io", 0); + vars_set_int (ret,F_CONFIG,"nes.pause_on_load", 0); + + vars_set_int (ret,F_CONFIG,"cartdb.enabled", 1); + vars_set_string(ret,F_CONFIG,"cartdb.filename", "%path.xml%/NesCarts.xml;%path.xml%/NesCarts2.xml"); + + vars_set_string(ret,0,"version",VERSION); + vars_set_string(ret,0,"exepath",exepath); + if((str = getenv("HOME")) != 0) + vars_set_string(ret,0,"home",str); + + return(ret); +} + +int config_init() +{ + vars_t *v; + char tmp[1024]; + + //find configuration file + if(findconfig(configfilename) == 0) + log_printf("main: found configuration at '%s'\n",configfilename); + else + log_printf("main: creating new configuration at '%s'\n",configfilename); + + configvars = config_get_defaults(); + if((v = vars_load(configfilename)) == 0) { + log_printf("config_init: unable to load file, using defaults\n"); + } + else { + //merge the loaded variables with the defaults + vars_merge(configvars,v); + + //destroy the loaded vars + vars_destroy(v); + } + + //make the directories + makepath(config_get_eval_string(tmp,"path.user")); + makepath(config_get_eval_string(tmp,"path.save")); + makepath(config_get_eval_string(tmp,"path.state")); + makepath(config_get_eval_string(tmp,"path.cheat")); + + return(0); +} + +void config_kill() +{ + vars_t *v = configvars; + + if(v == 0) { + log_printf("config_kill: internal error! configvars = 0.\n"); + return; + } + vars_save(v,configfilename); + vars_destroy(v); +} + +//gets config string variable with variables expanded +char *config_get_eval_string(char *dest,char *name) +{ + char *tmp,*p,*p2; + char varname[64]; + int pos; + + //get string + if((p = config_get_string(name)) == 0) + return(0); + + //make a copy of the string + tmp = mem_strdup(p); + + strcpy(dest,tmp); + + for(pos=0,p=tmp;*p;p++) { + + //see if we find a '%' + if(*p == '%') { + + //clear area to hold var name + memset(varname,0,64); + + //see if it is missing the '%' + if((p2 = strchr(p + 1,'%')) == 0) { + log_printf("config_get_eval_string: missing ending '%', just copying\n"); + } + + //not missing, replace with variable data + else { + //skip over the '%' + p++; + + //terminate the substring + *p2 = 0; + + //copy substring to varname array + strcpy(varname,p); + + //set new position in the string we parsing + p = p2 + 1; + + if(strcmp(varname,name) == 0) { + log_printf("config_get_eval_string: variable cannot reference itself (var '%s')\n",varname); + } + else { + char *tmp2 = (char*)mem_alloc(1024); + + p2 = var_get_eval_string(tmp2,varname); + if(p2 == 0) { + log_printf("config_get_eval_string: variable '%s' referenced non-existant variable '%s', using '.'\n",name,varname); + dest[pos++] = '.'; + dest[pos] = 0; + } + else { + while(*p2) { + dest[pos++] = *p2++; + dest[pos] = 0; + } + } + mem_free(tmp2); + } + } + } + + //copy the char + dest[pos++] = *p; + dest[pos] = 0; + } + + //normalize the path + paths_normalize(dest); + + //free tmp string and return + mem_free(tmp); + return(dest); +} + +//get config var (wraps the vars_get_*() functions) +char *config_get_string(char *name) { return(vars_get_string(configvars,name,"")); } +int config_get_int(char *name) { return(vars_get_int (configvars,name,0)); } +int config_get_bool(char *name) { return(vars_get_bool (configvars,name,0)); } +double config_get_double(char *name) { return(vars_get_double(configvars,name,0.0f)); } + +//set config var (wraps the vars_get_*() functions) +void config_set_string(char *name,char *data) { vars_set_string(configvars,F_CONFIG,name,data); } +void config_set_int(char *name,int data) { vars_set_int (configvars,F_CONFIG,name,data); } +void config_set_bool(char *name,int data) { vars_set_bool (configvars,F_CONFIG,name,data); } +void config_set_double(char *name,double data) { vars_set_double(configvars,F_CONFIG,name,data); } + +//set var (wraps the vars_get_*() functions) +void var_set_string(char *name,char *data) { vars_set_string(configvars,0,name,data); } +void var_set_int(char *name,int data) { vars_set_int (configvars,0,name,data); } +void var_set_bool(char *name,int data) { vars_set_bool (configvars,0,name,data); } +void var_set_double(char *name,double data) { vars_set_double(configvars,0,name,data); } + +void var_unset(char *name) +{ + vars_delete_var(configvars,name); +} + +//semi-kludge for the 'set' command +var_t *config_get_head() +{ + return(configvars->vars); +} diff --git a/apps/nesemu2/src/misc/config.h b/apps/nesemu2/src/misc/config.h new file mode 100644 index 00000000..6893db6b --- /dev/null +++ b/apps/nesemu2/src/misc/config.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __config_h__ +#define __config_h__ + +#include "misc/vars.h" + +int config_init(); +void config_kill(); +void config_load(); +void config_save(); + +//gets config string variable with variables expanded +char *config_get_eval_string(char *dest,char *name); + +//get config var (wraps the vars_get_*() functions) +char *config_get_string(char *name); +int config_get_int(char *name); +int config_get_bool(char *name); +double config_get_double(char *name); + +//set config var (wraps the vars_get_*() functions) +void config_set_string(char *name,char *data); +void config_set_int(char *name,int data); +void config_set_bool(char *name,int data); +void config_set_double(char *name,double data); + +//get var +#define var_get_eval_string config_get_eval_string +#define var_get_string config_get_string +#define var_get_int config_get_int +#define var_get_bool config_get_bool +#define var_get_double config_get_double + +//set var (wraps the vars_get_*() functions) +void var_set_string(char *name,char *data); +void var_set_int(char *name,int data); +void var_set_bool(char *name,int data); +void var_set_double(char *name,double data); + +//remove var from the list +void var_unset(char *name); + +var_t *config_get_head(); + +#endif diff --git a/apps/nesemu2/src/misc/crc32.c b/apps/nesemu2/src/misc/crc32.c new file mode 100644 index 00000000..b4aa152f --- /dev/null +++ b/apps/nesemu2/src/misc/crc32.c @@ -0,0 +1,70 @@ +/* based on implementation by Finn Yannick Jacobs */ + +#include "crc32.h" + +/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). + * so make sure, you call it before using the other + * functions! + */ +static u32 crc_tab[256]; + +/* crc32gentab() -- to a global crc_tab[256], this one will + * calculate the crcTable for crc32-checksums. + * it is generated to the polynom [..] + */ + +void crc32_gentab() +{ + unsigned long crc,poly; + int i,j; + + poly = 0xEDB88320L; + for(i=0;i<256;i++) { + crc = i; + for(j=8;j>0;j--) { + if(crc & 1) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + } + crc_tab[i] = crc; + } +} + +/* crc32() -- to a given block, this one calculates the + * crc32-checksum until the length is + * reached. the crc32-checksum will be + * the result. + */ +u32 crc32(unsigned char *block,unsigned int length) +{ + register unsigned long crc; + unsigned long i; + + crc = 0xFFFFFFFF; + for(i=0;i> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF]; + } + return(crc ^ 0xFFFFFFFF); +} + +//nestopia's implementation +static u32 crc32_calc(u8 data,u32 crc) +{ + return((crc >> 8) ^ crc_tab[(crc ^ data) & 0xFF]); +} + +u32 crc32_byte(u8 data,u32 crc) +{ + return(crc32_calc(data,crc ^ 0xFFFFFFFF) ^ 0xFFFFFFFF); +} + +u32 crc32_block(u8 *data,u32 length,u32 crc) +{ + unsigned char *end; + + crc ^= 0xFFFFFFFF; + for(end=data+length;data != end;data++) + crc = crc32_calc(*data,crc); + return(crc ^ 0xFFFFFFFF); +} diff --git a/apps/nesemu2/src/misc/crc32.h b/apps/nesemu2/src/misc/crc32.h new file mode 100644 index 00000000..d199f12f --- /dev/null +++ b/apps/nesemu2/src/misc/crc32.h @@ -0,0 +1,11 @@ +#ifndef __crc32_h__ +#define __crc32_h__ + +#include "types.h" + +void crc32_gentab(); +u32 crc32(unsigned char *block,unsigned int length); +u32 crc32_byte(u8 data,u32 crc); +u32 crc32_block(u8 *data,u32 length,u32 crc); + +#endif diff --git a/apps/nesemu2/src/misc/history.c b/apps/nesemu2/src/misc/history.c new file mode 100644 index 00000000..8697043a --- /dev/null +++ b/apps/nesemu2/src/misc/history.c @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "misc/history.h" +#include "misc/memutil.h" +#include "misc/log.h" + +history_t *history_create() +{ + history_t *ret = (history_t*)mem_alloc(sizeof(history_t)); + + memset(ret,0,sizeof(history_t)); + return(ret); +} + +void history_destroy(history_t *h) +{ + history_clear(h); + mem_free(h); +} + +void history_add(history_t *h,char *str) +{ + historyline_t *p = (historyline_t*)mem_alloc(sizeof(historyline_t)); + + p->prev = 0; + p->next = h->lines; + p->str = mem_strdup(str); + if(h->lines == 0) + h->lines = p; + else + h->lines->prev = p; + h->lines = p; + h->cur = 0; +} + +void history_clear(history_t *h) +{ + historyline_t *p,*line = h->lines; + + while(line) { + p = line; + line = line->next; + mem_free(p->str); + mem_free(p); + } +} + +char *history_getcur(history_t *h) +{ + return(h->cur ? h->cur->str : 0); +} + +char *history_getnext(history_t *h) +{ + if(h->cur == 0) + h->cur = h->lines; + else { + if(h->cur->next) + h->cur = h->cur->next; + } + return(history_getcur(h)); +} + +char *history_getprev(history_t *h) +{ + if(h->cur) { + if(h->cur->prev) + h->cur = h->cur->prev; + } + return(history_getcur(h)); +} diff --git a/apps/nesemu2/src/misc/history.h b/apps/nesemu2/src/misc/history.h new file mode 100644 index 00000000..545cb908 --- /dev/null +++ b/apps/nesemu2/src/misc/history.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __history_h__ +#define __history_h__ + +typedef struct historyline_s { + struct historyline_s *prev,*next; + char *str; +} historyline_t; + +typedef struct history_s { + historyline_t *lines; + historyline_t *cur; +} history_t; + +history_t *history_create(); +void history_destroy(history_t *h); +void history_add(history_t *h,char *str); +void history_clear(history_t *h); +char *history_getcur(history_t *h); +char *history_getnext(history_t *h); +char *history_getprev(history_t *h); + +#endif diff --git a/apps/nesemu2/src/misc/log.c b/apps/nesemu2/src/misc/log.c new file mode 100644 index 00000000..1b3a9060 --- /dev/null +++ b/apps/nesemu2/src/misc/log.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include "misc/log.h" +#include "misc/config.h" +#include "misc/paths.h" +#include "misc/strutil.h" +#include "misc/history.h" +#include "system/system.h" +#include "version.h" + +#ifndef MAX_PATH + #define MAX_PATH 1024 +#endif + +#define LOGFILENAME "nesemu2.log" + +static FILE *logfd = 0; +static char logfilename[MAX_PATH] = ""; +static void (*loghook)(char*) = 0; +static history_t history = {0,0}; + +int log_init() +{ + historyline_t *line; + + if(logfd) { + printf("log_init: already initialized\n"); + return(0); + } + + //clear the string + memset(logfilename,0,MAX_PATH); + + //parse the log path + config_get_eval_string(logfilename,"path.user"); + + //append the path seperator + str_appendchar(logfilename,PATH_SEPERATOR); + + //append the bios filename + strcat(logfilename,LOGFILENAME); + + //try to open + if((logfd = fopen(logfilename,"wt")) == 0) { + strcpy(logfilename,LOGFILENAME); + if((logfd = fopen(LOGFILENAME,"wt")) == 0) { + printf("log_init: error opening log file '%s'\n",logfilename); + return(1); + } + } + + //catch up the log file with lines that weren't added + line = history.lines; + while(line->next) { + line = line->next; + } + while(line) { + fputs(line->str,logfd); + line = line->prev; + } + fflush(logfd); + history_clear(&history); + + log_printf("log_init: log initialized. nesemu2 v"VERSION"\n"); + log_printf("log_init: log filename is '%s'.\n",logfilename); + return(0); +} + +void log_kill() +{ + if(logfd) + fclose(logfd); + logfd = 0; +} + +void log_sethook(void (*hook)(char*)) +{ + loghook = hook; +} + +void log_print(char *str) +{ + //output message to stdout + printf("%s",str); + + //output message to hook function + if(loghook) { + loghook(str); + } + + //if log file isnt open, maybe logging is disabled or file isnt opened yet + if(logfd == 0) { + history_add(&history,str); + return; + } + + //write to log file + fputs(str,logfd); + + //flush file + fflush(logfd); +} + +void log_printf(char *str,...) +{ + char buffer[1024]; //buffer to store vsprintf'd message in + va_list argptr; //argument data + + va_start(argptr,str); //get data + vsprintf(buffer,str,argptr); //print to buffer + va_end(argptr); //done! + log_print(buffer); +} diff --git a/apps/nesemu2/src/misc/log.h b/apps/nesemu2/src/misc/log.h new file mode 100644 index 00000000..5d6c2f9f --- /dev/null +++ b/apps/nesemu2/src/misc/log.h @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __log_h__ +#define __log_h__ + +int log_init(); +void log_kill(); +void log_sethook(void (*hook)(char*)); +void log_print(char *str); +void log_printf(char *str,...); + +#endif diff --git a/apps/nesemu2/src/misc/memfile.c b/apps/nesemu2/src/misc/memfile.c new file mode 100644 index 00000000..cd0f9df0 --- /dev/null +++ b/apps/nesemu2/src/misc/memfile.c @@ -0,0 +1,256 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/memfile.h" +#include "misc/memutil.h" +#include "misc/log.h" + +memfile_t *memfile_create() +{ + memfile_t *ret = 0; + + //allocate struct data + ret = (memfile_t*)mem_alloc(sizeof(memfile_t)); + memset(ret,0,sizeof(memfile_t)); + + //return + return(ret); +} + +memfile_t *memfile_open(char *filename,char *mode) +{ + FILE *fp; + memfile_t *ret = 0; + int n; + + //try to open the file + if((fp = fopen(filename,mode)) == 0) { + log_printf("memfile_open: error opening '%s'\n",filename); + return(0); + } + + //create new file + ret = memfile_create(); + + //copy filename and mode + ret->filename = mem_strdup(filename); + ret->mode = mem_strdup(mode); + + //save file handle + ret->handle = (void*)fp; + + //get file size + fseek(fp,0,SEEK_END); + ret->size = (u32)ftell(fp); + fseek(fp,0,SEEK_SET); + + //allocate file data and read the file into it + ret->data = (u8*)mem_alloc(ret->size); + if((n = fread(ret->data,1,ret->size,fp)) != ret->size) { + log_printf("memfile_open: error reading file, wanted %d bytes but read %d\n",ret->size,n); + memfile_close(ret); + return(0); + } + + //if append mode, seek to end + if(strchr(mode,'a')) { + memfile_seek(ret,0,SEEK_END); + } + + //initialize the current position and changed var + ret->curpos = 0; + ret->changed = 0; + + return(ret); +} + +memfile_t *memfile_open_memory(u8 *data,u32 size) +{ + memfile_t *ret; + + ret = memfile_create(); + ret->size = size; + ret->data = (u8*)mem_dup(data,size); + return(ret); +} + +void memfile_close(memfile_t *mf) +{ + FILE *fp = (FILE*)mf->handle; + + //is there is a filename associated with this file, it should already be open + if(mf->filename) { + if(strchr(mf->mode,'w')) { + log_printf("memfile_close: writing changes to '%s'\n",mf->filename); + fseek(fp,0,SEEK_SET); + if(mf->data) { + fwrite(mf->data,1,mf->size,fp); + } + } + fclose(fp); + mem_free(mf->filename); + mem_free(mf->mode); + } + if(mf->data) + mem_free(mf->data); + mem_free(mf); +} + +u32 memfile_size(memfile_t *mf) +{ + return(mf->size); +} + +u32 memfile_tell(memfile_t *mf) +{ + return(mf->curpos); +} + +int memfile_seek(memfile_t *mf,int pos,int mode) +{ + if(mode == SEEK_END) + mf->curpos = mf->size; + else if(mode == SEEK_SET) + mf->curpos = pos; + else if(mode == SEEK_CUR) + mf->curpos += pos; + if(mf->curpos < 0) { + mf->curpos = 0; + log_printf("memfile_seek: trying to seek before beginning of file\n"); + return(1); + } + if(mf->curpos > mf->size) { + mf->curpos = mf->size; + log_printf("memfile_seek: trying to seek past end of file\n"); + return(1); + } + return(0); +} + +void memfile_rewind(memfile_t *mf) +{ + memfile_seek(mf,0,SEEK_SET); +} + +int memfile_eof(memfile_t *mf) +{ + if(mf->curpos == mf->size) + return(1); + return(0); +} + +u32 memfile_read(void *data,int chunksize,int chunks,memfile_t *mf) +{ + int size = chunksize * chunks; + + if(memfile_eof(mf) != 0) { + log_printf("memfile_read: cannot read past eof\n"); + return(0); + } + memcpy(data,mf->data + mf->curpos,size); + mf->curpos += size; + return(chunks); +} + +u32 memfile_write(void *data,int chunksize,int chunks,memfile_t *mf) +{ + u32 size = chunksize * chunks; + u32 newsize = mf->curpos + size; + + if(newsize > mf->size) { + mf->data = mem_realloc(mf->data,newsize); + mf->size = newsize; + } + memcpy(mf->data + mf->curpos,data,size); + mf->curpos += size; + mf->changed++; + return(chunks); +} + +u32 memfile_copy(memfile_t *dest,memfile_t *src,u32 size) +{ + while(size) { + memfile_putc(memfile_getc(src),dest); + size--; + } + return(size); +} + +u32 memfile_fill(memfile_t *dest,u8 ch,u32 size) +{ + while(size) { + memfile_putc(ch,dest); + size--; + } + return(size); +} + +int memfile_getc(memfile_t *mf) +{ + int ret; + + if(memfile_eof(mf) != 0) { + log_printf("memfile_getc: cannot read past eof\n"); + return(-1); + } + ret = (int)mf->data[mf->curpos]; + mf->curpos++; + return(ret); +} + +int memfile_putc(u8 ch,memfile_t *mf) +{ + u32 newsize = mf->curpos + 1; + + if(newsize > mf->size) { + mf->data = mem_realloc(mf->data,newsize); + mf->size = newsize; + } + mf->data[mf->curpos] = ch; + mf->curpos++; + mf->changed++; + return(0); +} + +char *memfile_gets(char *str,int n,memfile_t *mf) +{ + int ch; + char *p = str; + + memset(str,0,n); + if(memfile_eof(mf) != 0) { + log_printf("memfile_gets: cannot read past eof\n"); + return(0); + } + for(;;) { + ch = memfile_getc(mf); + if(ch == -1) { + if(p == str) + return(0); + return(str); + } + *p++ = (char)ch; + if(--n == 0 || ch == '\n' || ch == 0) + break; + } + return(str); +} diff --git a/apps/nesemu2/src/misc/memfile.h b/apps/nesemu2/src/misc/memfile.h new file mode 100644 index 00000000..27cf3b03 --- /dev/null +++ b/apps/nesemu2/src/misc/memfile.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __memfile_h__ +#define __memfile_h__ + +#include "types.h" + +typedef struct memfile_s { + + //file data + u8 *data; + u32 size; + + //current position in the data + int curpos; + + //has the data been modified + u32 changed; + + //filename and mode + char *filename; + char *mode; + + //handle + void *handle; + +} memfile_t; + +memfile_t *memfile_create(); +memfile_t *memfile_open(char *filename,char *mode); +memfile_t *memfile_open_memory(u8 *data,u32 size); +void memfile_close(memfile_t *mf); +u32 memfile_size(memfile_t *mf); +u32 memfile_tell(memfile_t *mf); +int memfile_seek(memfile_t *mf,int pos,int mode); +void memfile_rewind(memfile_t *mf); +int memfile_eof(memfile_t *mf); +u32 memfile_read(void *data,int chunksize,int chunks,memfile_t *mf); +u32 memfile_write(void *data,int chunksize,int chunks,memfile_t *mf); +u32 memfile_copy(memfile_t *dest,memfile_t *src,u32 size); +u32 memfile_fill(memfile_t *dest,u8 ch,u32 size); +int memfile_getc(memfile_t *mf); +int memfile_putc(u8 ch,memfile_t *mf); +char *memfile_gets(char *str,int n,memfile_t *mf); + +#endif diff --git a/apps/nesemu2/src/misc/memutil.c b/apps/nesemu2/src/misc/memutil.c new file mode 100644 index 00000000..878835a1 --- /dev/null +++ b/apps/nesemu2/src/misc/memutil.c @@ -0,0 +1,214 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/memutil.h" +#include "misc/log.h" + +#define MAX_CHUNKS 1024 + +typedef struct memchunk_s { + void *ptr; + size_t size; + char file[256]; + int line; + int flags; +} memchunk_t; + +static memchunk_t chunks[MAX_CHUNKS]; + +static int inited = 0; +static int num_alloc; +static int num_realloc; +static int num_free; +static int max_chunks; +static size_t max_bytes; +static size_t num_bytes; + +static char *bytestr(size_t sz) +{ + static char str[64]; + + if(sz < 1024) + sprintf(str,"%ub",sz); + else if(sz < 1024 * 1024) + sprintf(str,"%.2fkb",(double)sz / 1024.0f); + else if(sz < 1024 * 1024 * 1024) + sprintf(str,"%.2fmb",(double)sz / 1024.0f / 1024.0f); + return(str); +} + +//check if inited already, if not do it +static void checkinited() +{ + if(inited == 0) + memutil_init(); +} + +//count number of used chunks +static void memutil_count() +{ + int i,chunknum = 0; + size_t bytes = 0; + + for(i=0;i max_chunks) ? chunknum : max_chunks; + max_bytes = (bytes > max_bytes) ? bytes : max_bytes; +} + +int memutil_init() +{ + if(inited) + return(0); +// if(chunks == 0) { +// chunks = (memchunk_t*)malloc(sizeof(memchunk_t) * MAX_CHUNKS); + memset(chunks,0,sizeof(memchunk_t)*MAX_CHUNKS); +// } + num_alloc = 0; + num_realloc = 0; + num_free = 0; + num_bytes = 0; + max_chunks = 0; + max_bytes = 0; + inited = 1; + return(0); +} + +void memutil_kill() +{ + int i; + + checkinited(); + for(i=0;i chunks[i].size) + num_bytes += size - chunks[i].size; + chunks[i].size = size; + break; + } + } + if(i == MAX_CHUNKS) { + log_printf("memutil_realloc: trying to realloc memory not found in list in file %s @ line %d\n",file,line); + } + memutil_count(); + return(ret); +} + +void memutil_free(void *ptr,char *file,int line) +{ + int i; + + checkinited(); + num_free++; + for(i=0;i + +int memutil_init(); +void memutil_kill(); +char *memutil_strdup(char *str,char *file,int line); +void *memutil_dup(void *data,size_t size,char *file,int line); +void *memutil_alloc(size_t size,char *file,int line); +void *memutil_realloc(void *ptr,size_t size,char *file,int line); +void memutil_free(void *ptr,char *file,int line); + +#endif diff --git a/apps/nesemu2/src/misc/paths.c b/apps/nesemu2/src/misc/paths.c new file mode 100644 index 00000000..810d6ffc --- /dev/null +++ b/apps/nesemu2/src/misc/paths.c @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/paths.h" +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/config.h" +#include "misc/log.h" +#include "emu/emu.h" +#include "system/main.h" + +char *paths_normalize(char *str) +{ + char *p; + + for(p=str;*p;p++) { + if(*p == '/' || *p == '\\') + *p = PATH_SEPERATOR; + } + return(str); +} + +void paths_makestatefilename(char *romfilename,char *dest,int len) +{ + char *p,*tmp = mem_strdup(romfilename); + + //clear the string + memset(dest,0,len); + + //parse the state path + config_get_eval_string(dest,"path.state"); + + //append the path seperator + str_appendchar(dest,PATH_SEPERATOR); + + //normalize the path seperators + paths_normalize(tmp); + + //find the last path seperator + p = strrchr(tmp,PATH_SEPERATOR); + + //if not found then it is plain filename + p = (p == 0) ? tmp : p + 1; + + //append the rom filename + strcat(dest,p); + + //append the state extension) + strcat(dest,".state"); + + //free the temporary string + mem_free(tmp); +} diff --git a/apps/nesemu2/src/misc/paths.h b/apps/nesemu2/src/misc/paths.h new file mode 100644 index 00000000..c31698b2 --- /dev/null +++ b/apps/nesemu2/src/misc/paths.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __paths_h__ +#define __paths_h__ + +#ifdef WIN32 + #define PATH_SEPERATOR '\\' +#else + #define PATH_SEPERATOR '/' +#endif + +char *paths_normalize(char *str); +void paths_makestatefilename(char *romfilename,char *dest,int len); + +#endif diff --git a/apps/nesemu2/src/misc/slre/slre.c b/apps/nesemu2/src/misc/slre/slre.c new file mode 100644 index 00000000..0a15b37b --- /dev/null +++ b/apps/nesemu2/src/misc/slre/slre.c @@ -0,0 +1,879 @@ +// Copyright (c) 2004-2012 Sergey Lyubka +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include +#include +#include +#include +#include +#include +//#include + +#include "slre.h" + +#ifdef _WIN32 +#define snprintf _snprintf +#endif + +// Compiled regular expression +struct slre { + unsigned char code[256]; + unsigned char data[256]; + int code_size; + int data_size; + int num_caps; // Number of bracket pairs + int anchored; // Must match from string start + enum slre_option options; + const char *error_string; // Error string +}; + +// Captured substring +struct cap { + const char *ptr; // Pointer to the substring + int len; // Substring length +}; + +enum { + END, BRANCH, ANY, EXACT, ANYOF, ANYBUT, OPEN, CLOSE, BOL, EOL, STAR, PLUS, + STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT +}; + +// Commands and operands are all unsigned char (1 byte long). All code offsets +// are relative to current address, and positive (always point forward). Data +// offsets are absolute. Commands with operands: +// +// BRANCH offset1 offset2 +// Try to match the code block that follows the BRANCH instruction +// (code block ends with END). If no match, try to match code block that +// starts at offset1. If either of these match, jump to offset2. +// +// EXACT data_offset data_length +// Try to match exact string. String is recorded in data section from +// data_offset, and has length data_length. +// +// OPEN capture_number +// CLOSE capture_number +// If the user have passed 'struct cap' array for captures, OPEN +// records the beginning of the matched substring (cap->ptr), CLOSE +// sets the length (cap->len) for respective capture_number. +// +// STAR code_offset +// PLUS code_offset +// QUEST code_offset +// *, +, ?, respectively. Try to gobble as much as possible from the +// matched buffer while code block that follows these instructions +// matches. When the longest possible string is matched, +// jump to code_offset +// +// STARQ, PLUSQ are non-greedy versions of STAR and PLUS. + +static const char *meta_characters = "|.^$*+?()[\\"; +static const char *error_no_match = "No match"; + +static void set_jump_offset(struct slre *r, int pc, int offset) { + assert(offset < r->code_size); + if (r->code_size - offset > 0xff) { + r->error_string = "Jump offset is too big"; + } else { + r->code[pc] = (unsigned char) (r->code_size - offset); + } +} + +static void emit(struct slre *r, int code) { + if (r->code_size >= (int) (sizeof(r->code) / sizeof(r->code[0]))) { + r->error_string = "RE is too long (code overflow)"; + } else { + r->code[r->code_size++] = (unsigned char) code; + } +} + +static void store_char_in_data(struct slre *r, int ch) { + if (r->data_size >= (int) sizeof(r->data)) { + r->error_string = "RE is too long (data overflow)"; + } else { + r->data[r->data_size++] = ch; + } +} + +static void exact(struct slre *r, const char **re) { + int old_data_size = r->data_size; + + while (**re != '\0' && (strchr(meta_characters, **re)) == NULL) { + store_char_in_data(r, *(*re)++); + } + + emit(r, EXACT); + emit(r, old_data_size); + emit(r, r->data_size - old_data_size); +} + +static int get_escape_char(const char **re) { + int res; + + switch (*(*re)++) { + case 'n': res = '\n'; break; + case 'r': res = '\r'; break; + case 't': res = '\t'; break; + case '0': res = 0; break; + case 'S': res = NONSPACE << 8; break; + case 's': res = SPACE << 8; break; + case 'd': res = DIGIT << 8; break; + default: res = (*re)[-1]; break; + } + + return res; +} + +static void anyof(struct slre *r, const char **re) { + int esc, old_data_size = r->data_size, op = ANYOF; + + if (**re == '^') { + op = ANYBUT; + (*re)++; + } + + while (**re != '\0') + + switch (*(*re)++) { + case ']': + emit(r, op); + emit(r, old_data_size); + emit(r, r->data_size - old_data_size); + return; + // NOTREACHED + break; + case '\\': + esc = get_escape_char(re); + if ((esc & 0xff) == 0) { + store_char_in_data(r, 0); + store_char_in_data(r, esc >> 8); + } else { + store_char_in_data(r, esc); + } + break; + default: + store_char_in_data(r, (*re)[-1]); + break; + } + + r->error_string = "No closing ']' bracket"; +} + +static void relocate(struct slre *r, int begin, int shift) { + emit(r, END); + memmove(r->code + begin + shift, r->code + begin, r->code_size - begin); + r->code_size += shift; +} + +static void quantifier(struct slre *r, int prev, int op) { + if (r->code[prev] == EXACT && r->code[prev + 2] > 1) { + r->code[prev + 2]--; + emit(r, EXACT); + emit(r, r->code[prev + 1] + r->code[prev + 2]); + emit(r, 1); + prev = r->code_size - 3; + } + relocate(r, prev, 2); + r->code[prev] = op; + set_jump_offset(r, prev + 1, prev); +} + +static void exact_one_char(struct slre *r, int ch) { + emit(r, EXACT); + emit(r, r->data_size); + emit(r, 1); + store_char_in_data(r, ch); +} + +static void fixup_branch(struct slre *r, int fixup) { + if (fixup > 0) { + emit(r, END); + set_jump_offset(r, fixup, fixup - 2); + } +} + +static void compile(struct slre *r, const char **re) { + int op, esc, branch_start, last_op, fixup, cap_no, level; + + fixup = 0; + level = r->num_caps; + branch_start = last_op = r->code_size; + + for (;;) + switch (*(*re)++) { + + case '\0': + (*re)--; + return; + // NOTREACHED + break; + + case '^': + emit(r, BOL); + break; + + case '$': + emit(r, EOL); + break; + + case '.': + last_op = r->code_size; + emit(r, ANY); + break; + + case '[': + last_op = r->code_size; + anyof(r, re); + break; + + case '\\': + last_op = r->code_size; + esc = get_escape_char(re); + if (esc & 0xff00) { + emit(r, esc >> 8); + } else { + exact_one_char(r, esc); + } + break; + + case '(': + last_op = r->code_size; + cap_no = ++r->num_caps; + emit(r, OPEN); + emit(r, cap_no); + + compile(r, re); + if (*(*re)++ != ')') { + r->error_string = "No closing bracket"; + return; + } + + emit(r, CLOSE); + emit(r, cap_no); + break; + + case ')': + (*re)--; + fixup_branch(r, fixup); + if (level == 0) { + r->error_string = "Unbalanced brackets"; + return; + } + return; + // NOTREACHED + break; + + case '+': + case '*': + op = (*re)[-1] == '*' ? STAR: PLUS; + if (**re == '?') { + (*re)++; + op = op == STAR ? STARQ : PLUSQ; + } + quantifier(r, last_op, op); + break; + + case '?': + quantifier(r, last_op, QUEST); + break; + + case '|': + fixup_branch(r, fixup); + relocate(r, branch_start, 3); + r->code[branch_start] = BRANCH; + set_jump_offset(r, branch_start + 1, branch_start); + fixup = branch_start + 2; + r->code[fixup] = 0xff; + break; + + default: + (*re)--; + last_op = r->code_size; + exact(r, re); + break; + } +} + +// Compile regular expression. If success, 1 is returned. +// If error, 0 is returned and slre.error_string points to the error message. +static const char *compile2(struct slre *r, const char *re) { + r->error_string = NULL; + r->code_size = r->data_size = r->num_caps = r->anchored = 0; + + if (*re == '^') { + r->anchored++; + } + + emit(r, OPEN); // This will capture what matches full RE + emit(r, 0); + + while (*re != '\0') { + compile(r, &re); + } + + if (r->code[2] == BRANCH) { + fixup_branch(r, 4); + } + + emit(r, CLOSE); + emit(r, 0); + emit(r, END); + +#if 0 + static void dump(const struct slre *, FILE *); + dump(r, stdout); +#endif + + return r->error_string; +} + +static const char *match(const struct slre *, int, const char *, int, int *, + struct cap *, int caps_size); + +static void loop_greedy(const struct slre *r, int pc, const char *s, int len, + int *ofs) { + int saved_offset, matched_offset; + + saved_offset = matched_offset = *ofs; + + while (!match(r, pc + 2, s, len, ofs, NULL, 0)) { + saved_offset = *ofs; + if (!match(r, pc + r->code[pc + 1], s, len, ofs, NULL, 0)) { + matched_offset = saved_offset; + } + *ofs = saved_offset; + } + + *ofs = matched_offset; +} + +static void loop_non_greedy(const struct slre *r, int pc, const char *s, + int len, int *ofs) { + int saved_offset = *ofs; + + while (!match(r, pc + 2, s, len, ofs, NULL, 0)) { + saved_offset = *ofs; + if (!match(r, pc + r->code[pc + 1], s, len, ofs, NULL, 0)) + break; + } + + *ofs = saved_offset; +} + +static int is_any_of(const unsigned char *p, int len, const char *s, int *ofs) { + int i, ch; + + ch = s[*ofs]; + + for (i = 0; i < len; i++) + if (p[i] == ch) { + (*ofs)++; + return 1; + } + + return 0; +} + +static int is_any_but(const unsigned char *p, int len, const char *s, + int *ofs) { + int i, ch; + + ch = s[*ofs]; + + for (i = 0; i < len; i++) + if (p[i] == ch) { + return 0; + } + + (*ofs)++; + return 1; +} + +static int lowercase(const char *s) { + return tolower(* (const unsigned char *) s); +} + +static int casecmp(const void *p1, const void *p2, size_t len) { + const char *s1 = p1, *s2 = p2; + int diff = 0; + + if (len > 0) + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + + return diff; +} + +static const char *match(const struct slre *r, int pc, const char *s, int len, + int *ofs, struct cap *caps, int caps_size) { + int n, saved_offset; + const char *error_string = NULL; + int (*cmp)(const void *string1, const void *string2, size_t len); + + while (error_string == NULL && r->code[pc] != END) { + + assert(pc < r->code_size); + assert(pc < (int) (sizeof(r->code) / sizeof(r->code[0]))); + + switch (r->code[pc]) { + case BRANCH: + saved_offset = *ofs; + error_string = match(r, pc + 3, s, len, ofs, caps, caps_size); + if (error_string != NULL) { + *ofs = saved_offset; + error_string = match(r, pc + r->code[pc + 1], s, len, ofs, caps, + caps_size); + } + pc += r->code[pc + 2]; + break; + + case EXACT: + error_string = error_no_match; + n = r->code[pc + 2]; // String length + cmp = r->options & SLRE_CASE_INSENSITIVE ? casecmp : memcmp; + if (n <= len - *ofs && !cmp(s + *ofs, r->data + r->code[pc + 1], n)) { + (*ofs) += n; + error_string = NULL; + } + pc += 3; + break; + + case QUEST: + error_string = NULL; + saved_offset = *ofs; + if (match(r, pc + 2, s, len, ofs, caps, caps_size) != NULL) { + *ofs = saved_offset; + } + pc += r->code[pc + 1]; + break; + + case STAR: + error_string = NULL; + loop_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + + case STARQ: + error_string = NULL; + loop_non_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + + case PLUS: + if ((error_string = match(r, pc + 2, s, len, ofs, + caps, caps_size)) != NULL) { + break; + } + loop_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + + case PLUSQ: + if ((error_string = match(r, pc + 2, s, len, ofs, + caps, caps_size)) != NULL) { + break; + } + loop_non_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + + case SPACE: + error_string = error_no_match; + if (*ofs < len && isspace(((unsigned char *)s)[*ofs])) { + (*ofs)++; + error_string = NULL; + } + pc++; + break; + + case NONSPACE: + error_string = error_no_match; + if (*ofs data + r->code[pc + 1], r->code[pc + 2], + s, ofs) ? NULL : error_no_match; + pc += 3; + break; + + case ANYBUT: + error_string = error_no_match; + if (*ofs < len) + error_string = is_any_but(r->data + r->code[pc + 1], r->code[pc + 2], + s, ofs) ? NULL : error_no_match; + pc += 3; + break; + + case BOL: + error_string = *ofs == 0 ? NULL : error_no_match; + pc++; + break; + + case EOL: + error_string = *ofs == len ? NULL : error_no_match; + pc++; + break; + + case OPEN: + if (caps != NULL) { + if (caps_size - 2 < r->code[pc + 1]) { + error_string = "Too many brackets"; + } else { + caps[r->code[pc + 1]].ptr = s + *ofs; + } + } + pc += 2; + break; + + case CLOSE: + if (caps != NULL) { + assert(r->code[pc + 1] >= 0); + assert(r->code[pc + 1] < caps_size); + caps[r->code[pc + 1]].len = (s + *ofs) - + caps[r->code[pc + 1]].ptr; + } + pc += 2; + break; + + case END: + pc++; + break; + + default: + printf("unknown cmd (%d) at %d\n", r->code[pc], pc); + assert(0); + break; + } + } + + return error_string; +} + +// Return 1 if match, 0 if no match. +// If `captured_substrings' array is not NULL, then it is filled with the +// values of captured substrings. captured_substrings[0] element is always +// a full matched substring. The round bracket captures start from +// captured_substrings[1]. +// It is assumed that the size of captured_substrings array is enough to +// hold all captures. The caller function must make sure it is! So, the +// array_size = number_of_round_bracket_pairs + 1 +static const char *match2(const struct slre *r, const char *buf, int len, + struct cap *caps, int caps_size) { + int i, ofs = 0; + const char *error_string = error_no_match; + + if (caps != NULL) { + memset(caps, 0, caps_size * sizeof(caps[0])); + } + + if (r->anchored) { + error_string = match(r, 0, buf, len, &ofs, caps, caps_size); + } else { + for (i = 0; i < len && error_string != NULL; i++) { + ofs = i; + error_string = match(r, 0, buf, len, &ofs, caps, caps_size); + } + } + + return error_string; +} + +static const char *capture_float(const struct cap *cap, void *p, size_t len) { + const char *fmt; + char buf[20]; + + switch (len) { + case sizeof(float): fmt = "f"; break; + case sizeof(double): fmt = "lf"; break; + default: return "SLRE_FLOAT: unsupported size"; + } + + snprintf(buf, sizeof(buf), "%%%d%s", cap->len, fmt); + return sscanf(cap->ptr, buf, p) == 1 ? NULL : "SLRE_FLOAT: capture failed"; +} + +static const char *capture_string(const struct cap *cap, void *p, size_t len) { + if ((int) len <= cap->len) { + return "SLRE_STRING: buffer size too small"; + } + memcpy(p, cap->ptr, cap->len); + ((char *) p)[cap->len] = '\0'; + return NULL; +} + +static const char *capture_int(const struct cap *cap, void *p, size_t len) { + const char *fmt; + char buf[20]; + + switch (len) { + case sizeof(char): fmt = "hh"; break; + case sizeof(short): fmt = "h"; break; + case sizeof(int): fmt = "d"; break; + case sizeof(long long int): fmt = "lld"; break; + default: return "SLRE_INT: unsupported size"; + } + + snprintf(buf, sizeof(buf), "%%%d%s", cap->len, fmt); + return sscanf(cap->ptr, buf, p) == 1 ? NULL : "SLRE_INT: capture failed"; +} + +static const char *capture(const struct cap *caps, int num_caps, va_list ap) { + int i, type; + size_t size; + void *p; + const char *err = NULL; + + for (i = 0; i < num_caps; i++) { + type = va_arg(ap, int); + size = va_arg(ap, size_t); + p = va_arg(ap, void *); + switch (type) { + case SLRE_INT: err = capture_int(&caps[i], p, size); break; + case SLRE_FLOAT: err = capture_float(&caps[i], p, size); break; + case SLRE_STRING: err = capture_string(&caps[i], p, size); break; + default: err = "Unknown type, expected SLRE_(INT|FLOAT|STRING)"; break; + } + } + return err; +} + +const char *slre_match(enum slre_option options, const char *re, + const char *buf, int buf_len, ...) { + struct slre slre = {}; + struct cap caps[20]; + va_list ap; + const char *error_string = NULL; + + slre.options = options; + if ((error_string = compile2(&slre, re)) == NULL && + (error_string = match2(&slre, buf, buf_len, caps, + sizeof(caps) / sizeof(caps[0]))) == NULL) { + va_start(ap, buf_len); + error_string = capture(caps + 1, slre.num_caps, ap); + va_end(ap); + } + + return error_string; +} + +#if defined(SLRE_UNIT_TEST) +static struct { + const char *name; + int narg; + const char *flags; +} opcodes[] = { + {"END", 0, "" }, // End of code block or program + {"BRANCH", 2, "oo"}, // Alternative operator, "|" + {"ANY", 0, "" }, // Match any character, "." + {"EXACT", 2, "d" }, // Match exact string + {"ANYOF", 2, "D" }, // Match any from set, "[]" + {"ANYBUT", 2, "D" }, // Match any but from set, "[^]" + {"OPEN ", 1, "i" }, // Capture start, "(" + {"CLOSE", 1, "i" }, // Capture end, ")" + {"BOL", 0, "" }, // Beginning of string, "^" + {"EOL", 0, "" }, // End of string, "$" + {"STAR", 1, "o" }, // Match zero or more times "*" + {"PLUS", 1, "o" }, // Match one or more times, "+" + {"STARQ", 1, "o" }, // Non-greedy STAR, "*?" + {"PLUSQ", 1, "o" }, // Non-greedy PLUS, "+?" + {"QUEST", 1, "o" }, // Match zero or one time, "?" + {"SPACE", 0, "" }, // Match whitespace, "\s" + {"NONSPACE", 0, "" }, // Match non-space, "\S" + {"DIGIT", 0, "" } // Match digit, "\d" +}; + +static void print_character_set(FILE *fp, const unsigned char *p, int len) { + int i; + + for (i = 0; i < len; i++) { + if (i > 0) + (void) fputc(',', fp); + if (p[i] == 0) { + i++; + if (p[i] == 0) + (void) fprintf(fp, "\\x%02x", p[i]); + else + (void) fprintf(fp, "%s", opcodes[p[i]].name); + } else if (isprint(p[i])) { + (void) fputc(p[i], fp); + } else { + (void) fprintf(fp,"\\x%02x", p[i]); + } + } +} + +static void dump(const struct slre *r, FILE *fp) { + int i, j, ch, op, pc; + + for (pc = 0; pc < r->code_size; pc++) { + + op = r->code[pc]; + (void) fprintf(fp, "%3d %s ", pc, opcodes[op].name); + + for (i = 0; opcodes[op].flags[i] != '\0'; i++) + switch (opcodes[op].flags[i]) { + case 'i': + fprintf(fp, "%d ", r->code[pc + 1]); + pc++; + break; + case 'o': + fprintf(fp, "%d ", pc + r->code[pc + 1] - i); + pc++; + break; + case 'D': + print_character_set(fp, r->data + + r->code[pc + 1], r->code[pc + 2]); + pc += 2; + break; + case 'd': + (void) fputc('"', fp); + for (j = 0; j < r->code[pc + 2]; j++) { + ch = r->data[r->code[pc + 1] + j]; + if (isprint(ch)) + fputc(ch, fp); + else + fprintf(fp,"\\x%02x",ch); + } + (void) fputc('"', fp); + pc += 2; + break; + } + + fputc('\n', fp); + } +} + + +int main(void) { + static const struct { const char *str, *regex, *msg; } tests[] = { + {"aa", ".+", NULL}, + {"aa", ".", NULL}, + {"", ".", "No match"}, + {" cc 1234", "c.\\s\\d+", NULL}, + }; + char buf[20]; + int int_value; + const char *msg, *str, *re; + size_t i; + + char method[10], uri[100]; + int http_version_minor, http_version_major; + const char *error; + const char *request = " \tGET /index.html HTTP/1.0\r\n\r\n"; + + error = slre_match(0, "^\\s*(GET|POST)\\s+(\\S+)\\s+HTTP/(\\d)\\.(\\d)", + request, strlen(request), + SLRE_STRING, sizeof(method), method, + SLRE_STRING, sizeof(uri), uri, + SLRE_INT, sizeof(http_version_major), &http_version_major, + SLRE_INT, sizeof(http_version_minor), &http_version_minor); + + if (error != NULL) { + printf("Error parsing HTTP request: %s\n", error); + } else { + printf("Requested URI: %s\n", uri); + } + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + msg = slre_match(0, tests[i].regex, tests[i].str, + strlen(tests[i].str), NULL); + if ((msg != NULL && tests[i].msg == NULL) || + (msg != NULL && strcmp(msg, tests[i].msg) != 0)) { + printf("Test %d failed: [%s] [%s] -> [%s]\n", (int) i, tests[i].str, + tests[i].regex, msg ? msg : "(null)"); + return EXIT_FAILURE; + } + } + + assert(slre_match(0, "a (\\d+)4\\s*(\\S+)", "aa 1234 xy\nz", 12, + SLRE_INT, sizeof(int_value), &int_value, + SLRE_STRING, sizeof(buf), buf) == NULL); + assert(int_value == 123); + assert(!strcmp(buf, "xy")); + + str = "Hello превед!"; + re = "^hello (\\S+)"; + assert(strcmp(error_no_match, slre_match(0, re, str, strlen(str), SLRE_STRING, + sizeof(buf), buf)) == 0); + assert(slre_match(SLRE_CASE_INSENSITIVE, re, str, strlen(str), + SLRE_STRING, sizeof(buf), buf) == NULL); + assert(!strcmp(buf, "превед!")); + + assert(strcmp(error_no_match, slre_match(0, "bC", "aBc", 3)) == 0); + assert(slre_match(SLRE_CASE_INSENSITIVE, "bC", "aBc", 3) == NULL); + assert(slre_match(0, "3?9", "9", 1) == NULL); + + // TODO: fix this! + //assert(slre_match(0, "9?9", "9", 1) == NULL); + + { + struct slre slre; + struct cap caps[10]; + char a[10], b[10]; + + memset(caps, 'x', sizeof(caps)); + slre.options = 0; + assert(compile2(&slre, "(\\d(\\d)?)") == NULL); + assert(!strcmp(match2(&slre, "1", 1, caps, 2), "Too many brackets")); + assert(match2(&slre, "1", 1, caps, 3) == NULL); + assert(slre.num_caps == 2); + assert(caps[1].len == 1); + assert(caps[2].len == 0); + assert(caps[1].ptr[0] == '1'); + + a[0] = b[0] = 'x'; + assert(slre_match(0, "(\\d(\\d)?)", "1", 1, + SLRE_STRING, sizeof(a), a, + SLRE_STRING, sizeof(b), b) == NULL); + assert(!strcmp(a, "1")); + assert(b[0] == '\0'); + } + + printf("%s\n", "All tests passed"); + return EXIT_SUCCESS; +} +#endif // SLRE_UNIT_TEST diff --git a/apps/nesemu2/src/misc/slre/slre.h b/apps/nesemu2/src/misc/slre/slre.h new file mode 100644 index 00000000..69f68ab2 --- /dev/null +++ b/apps/nesemu2/src/misc/slre/slre.h @@ -0,0 +1,94 @@ +// Copyright (c) 2004-2012 Sergey Lyubka +// All rights reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef SLRE_HEADER_DEFINED +#define SLRE_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +// This is a regular expression library that implements a subset of Perl RE. +// Please refer to http://slre.googlecode.com for detailed description. +// +// Supported syntax: +// ^ Match beginning of a buffer +// $ Match end of a buffer +// () Grouping and substring capturing +// [...] Match any character from set +// [^...] Match any character but ones from set +// \s Match whitespace +// \S Match non-whitespace +// \d Match decimal digit +// \r Match carriage return +// \n Match newline +// + Match one or more times (greedy) +// +? Match one or more times (non-greedy) +// * Match zero or more times (greedy) +// *? Match zero or more times (non-greedy) +// ? Match zero or once +// \xDD Match byte with hex value 0xDD +// \meta Match one of the meta character: ^$().[*+\? + +// Match string buffer "buf" of length "buf_len" against "regexp", which should +// conform the syntax outlined above. "options" could be either 0 or +// SLRE_CASE_INSENSITIVE for case-insensitive match. If regular expression +// "regexp" contains brackets, slre_match() will capture the respective +// substring into the passed placeholder. Thus, each opening parenthesis +// should correspond to three arguments passed: +// placeholder_type, placeholder_size, placeholder_address +// +// Usage example: parsing HTTP request line. +// +// char method[10], uri[100]; +// int http_version_minor, http_version_major; +// const char *error; +// const char *request = " \tGET /index.html HTTP/1.0\r\n\r\n"; + +// error = slre_match(0, "^\\s*(GET|POST)\\s+(\\S+)\\s+HTTP/(\\d)\\.(\\d)", +// request, strlen(request), +// SLRE_STRING, sizeof(method), method, +// SLRE_STRING, sizeof(uri), uri, +// SLRE_INT,sizeof(http_version_major),&http_version_major, +// SLRE_INT,sizeof(http_version_minor),&http_version_minor); +// +// if (error != NULL) { +// printf("Error parsing HTTP request: %s\n", error); +// } else { +// printf("Requested URI: %s\n", uri); +// } +// +// +// Return: +// NULL: string matched and all captures successfully made +// non-NULL: in this case, the return value is an error string + +enum slre_option {SLRE_CASE_INSENSITIVE = 1}; +enum slre_capture {SLRE_STRING, SLRE_INT, SLRE_FLOAT}; + +const char *slre_match(enum slre_option options, const char *regexp, + const char *buf, int buf_len, ...); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SLRE_HEADER_DEFINED diff --git a/apps/nesemu2/src/misc/strutil.c b/apps/nesemu2/src/misc/strutil.c new file mode 100644 index 00000000..0f019f2c --- /dev/null +++ b/apps/nesemu2/src/misc/strutil.c @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "misc/strutil.h" + +//check if a char is whitespace +static int iswhitespace(char ch) +{ + if(ch == ' ' || ch == '\t' || ch == '\n') + return(1); + return(0); +} + +//eat whitespace from beginning and end of the string +char *str_eatwhitespace(char *str) +{ + char *p,*ret = str; + + while(iswhitespace(*ret)) + ret++; + p = ret + strlen(ret) - 1; + while(iswhitespace(*p)) + *p-- = 0; + return(ret); +} + +//append a single char to the end of a string +void str_appendchar(char *str,char ch) +{ + int n = strlen(str); + + str[n] = ch; + str[n+1] = 0; +} diff --git a/apps/nesemu2/src/misc/strutil.h b/apps/nesemu2/src/misc/strutil.h new file mode 100644 index 00000000..29d5a914 --- /dev/null +++ b/apps/nesemu2/src/misc/strutil.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __strutil_h__ +#define __strutil_h__ + +char *str_eatwhitespace(char *str); +void str_appendchar(char *str,char ch); + +#endif diff --git a/apps/nesemu2/src/misc/vars.c b/apps/nesemu2/src/misc/vars.c new file mode 100644 index 00000000..e77936dd --- /dev/null +++ b/apps/nesemu2/src/misc/vars.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* todo: + +vars_add_var is able to add another var with the same name + +*/ + +#include +#include +#include +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/vars.h" +#include "misc/log.h" + +vars_t *vars_create() +{ + vars_t *ret = 0; + + ret = (vars_t*)mem_alloc(sizeof(vars_t)); + memset(ret,0,sizeof(vars_t)); + return(ret); +} + +void vars_destroy(vars_t *vs) +{ + vars_clear(vs); + mem_free(vs); +} + +vars_t *vars_load(char *filename) +{ + vars_t *ret = 0; + FILE *fp; + char line[1024],*p,*oldp; + + if((fp = fopen(filename,"rt")) == 0) { + log_printf("vars_load: error opening file '%s'\n",filename); + return(0); + } + + ret = vars_create(); + while(feof(fp) == 0) { + + //read line from file + if(fgets(line,1024,fp) == NULL) + break; + + //skip past any whitespace + p = str_eatwhitespace(line); + + //comment or empty string, do nothing + if(*p == '#' || *p == 0) + continue; + + //find where to split the string + if((oldp = strchr(p,'=')) == 0) { + log_printf("vars_load: malformed line ('%s')\n",p); + continue; + } + + //parse out the name/data pair + *oldp++ = 0; + p = str_eatwhitespace(p); + oldp = str_eatwhitespace(oldp); + + //add the var to the list + vars_add_var(ret,F_CONFIG,p,oldp); + } + + log_printf("vars_load: loaded file '%s'\n",filename); + fclose(fp); + strcpy(ret->filename,filename); + return(ret); +} + +int vars_save(vars_t *vs,char *filename) +{ + FILE *fp; + var_t *v = vs->vars; + + if(filename == 0) + filename = vs->filename; + if((fp = fopen(filename,"wt")) == 0) { + log_printf("vars_save: error opening '%s'\n",filename); + return(1); + } + while(v) { + if(v->flags == F_CONFIG) + fprintf(fp,"%s = %s\n",v->name,v->data); + v = v->next; + } + fclose(fp); + vs->changed = 0; + return(0); +} + +void vars_merge(vars_t *dest,vars_t *src) +{ + var_t *v = src->vars; + + while(v) { + vars_set_string(dest,v->flags,v->name,v->data); + v = v->next; + } +} + +void vars_clear(vars_t *vs) +{ + var_t *v,*v2; + + v = vs->vars; + while(v) { + v2 = v; + v = v->next; + mem_free(v2->name); + mem_free(v2->data); + mem_free(v2); + } + vs->vars = 0; +} + +var_t *vars_add_var(vars_t *vs,int flags,char *name,char *data) +{ + var_t *v,*var; + + var = (var_t*)mem_alloc(sizeof(var_t)); + memset(var,0,sizeof(var_t)); + var->flags = flags; + var->name = mem_strdup(name); + var->data = mem_strdup(data); + if(vs->vars == 0) + vs->vars = var; + else { + v = vs->vars; + while(v->next) + v = v->next; + v->next = var; + } + vs->changed++; + return(var); +} + +void vars_delete_var(vars_t *vs,char *name) +{ + var_t *v,*prev; + + prev = 0; + v = vs->vars; + while(v) { + if(strcmp(name,v->name) == 0) { + if(prev == 0) { + vs->vars = v->next; + } + else { + prev->next = v->next; + } + mem_free(v->name); + mem_free(v->data); + mem_free(v); + return; + } + prev = v; + v = v->next; + } + vs->changed++; +} + +var_t *vars_get_var(vars_t *vs,char *name) +{ + var_t *v = vs->vars; + + while(v) { + if(strcmp(name,v->name) == 0) { + return(v); + } + v = v->next; + } + return(0); +} + +static char tmpstr[64]; + +char *vars_get_string(vars_t *vs,char *name,char *def) +{ + var_t *v = vars_get_var(vs,name); + + return(v ? v->data : def); +} + +int vars_get_int(vars_t *vs,char *name,int def) +{ + sprintf(tmpstr,"%d",def); + return(atoi(vars_get_string(vs,name,tmpstr))); +} + +int vars_get_bool(vars_t *vs,char *name,int def) +{ + return(vars_get_int(vs,name,def) ? 1 : 0); +} + +double vars_get_double(vars_t *vs,char *name,double def) +{ + sprintf(tmpstr,"%f",def); + return(atof(vars_get_string(vs,name,tmpstr))); +} + +var_t *vars_set_string(vars_t *vs,int flags,char *name,char *data) +{ + var_t *v = vs->vars; + + while(v) { + if(strcmp(name,v->name) == 0) { + mem_free(v->data); + v->data = mem_strdup(data); +//do not change flags, once added it stays with the flags it was created with +// v->flags = flags; + vs->changed++; + return(v); + } + v = v->next; + } + return((v == 0) ? vars_add_var(vs,flags,name,data) : v); +} + +var_t *vars_set_int(vars_t *vs,int flags,char *name,int data) +{ + sprintf(tmpstr,"%d",data); + return(vars_set_string(vs,flags,name,tmpstr)); +} + +var_t *vars_set_bool(vars_t *vs,int flags,char *name,int data) +{ + return(vars_set_int(vs,flags,name,data ? 1 : 0)); +} + +var_t *vars_set_double(vars_t *vs,int flags,char *name,double data) +{ + sprintf(tmpstr,"%f",data); + return(vars_set_string(vs,flags,name,tmpstr)); +} diff --git a/apps/nesemu2/src/misc/vars.h b/apps/nesemu2/src/misc/vars.h new file mode 100644 index 00000000..a97e9ebb --- /dev/null +++ b/apps/nesemu2/src/misc/vars.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __vars_h__ +#define __vars_h__ + +#define F_VAR 0 //variable that is normal +#define F_CONFIG 1 //variable that is part of the configuration (gets saved) + +typedef struct var_s { + struct var_s *next; + int flags; + char *name; + char *data; +} var_t; + +typedef struct vars_s { + var_t *vars; + int changed; + char filename[1024]; +} vars_t; + +//create empty var list +vars_t *vars_create(); + +//destroy var list +void vars_destroy(vars_t *vs); + +//load vars from file +vars_t *vars_load(char *filename); + +//save vars to a file +int vars_save(vars_t *vars,char *filename); + +//merge all vars from src into dest +void vars_merge(vars_t *dest,vars_t *src); + +//delete all vars +void vars_clear(vars_t *vs); + +//add var +var_t *vars_add_var(vars_t *vs,int flags,char *name,char *data); + +//delete var +void vars_delete_var(vars_t *vs,char *name); + +//add var +var_t *vars_get_var(vars_t *vs,char *name); + +//get var +char *vars_get_string(vars_t *vars,char *name,char *def); +int vars_get_int(vars_t *vars,char *name,int def); +int vars_get_bool(vars_t *vars,char *name,int def); +double vars_get_double(vars_t *vars,char *name,double def); + +//set var +var_t *vars_set_string(vars_t *vars,int flags,char *name,char *data); +var_t *vars_set_int(vars_t *vars,int flags,char *name,int data); +var_t *vars_set_bool(vars_t *vars,int flags,char *name,int data); +var_t *vars_set_double(vars_t *vars,int flags,char *name,double data); + +#endif diff --git a/apps/nesemu2/src/nes/apu/apu.c b/apps/nesemu2/src/nes/apu/apu.c new file mode 100644 index 00000000..9125d987 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/apu.c @@ -0,0 +1,249 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//this code is ported from nintendulator's c++ apu + +#include "nes/nes.h" +#include "nes/state/state.h" +#include "misc/log.h" +#include "misc/memutil.h" +#include "system/sound.h" + +#define SOUND_HZ 44100 +#define NES_HZ 1789773 + +u8 LengthCounts[32] = { + 0x0A,0xFE, + 0x14,0x02, + 0x28,0x04, + 0x50,0x06, + 0xA0,0x08, + 0x3C,0x0A, + 0x0E,0x0C, + 0x1A,0x0E, + + 0x0C,0x10, + 0x18,0x12, + 0x30,0x14, + 0x60,0x16, + 0xC0,0x18, + 0x48,0x1A, + 0x10,0x1C, + 0x20,0x1E +}; + +#include "nes/apu/race.hh" +#include "nes/apu/units/square.hh" +#include "nes/apu/units/triangle.hh" +#include "nes/apu/units/noise.hh" +#include "nes/apu/units/dpcm.hh" +#include "nes/apu/units/frame.hh" + +static u8 regs[0x20]; +static int cycles = 0; +static const int soundbufsize = 1024 * 3; +static u16 soundbuflen = 0; +static s16 *soundbuf = 0; + +int apu_init() +{ + int i; + + state_register(B_APU,apu_state); + soundbuf = (s16*)mem_alloc(sizeof(s16) * soundbufsize); + for(i=0;iapu.external) + nes->apu.external->kill(); + if(soundbuf) { + mem_free(soundbuf); + soundbuf = 0; + } +} + +void apu_reset(int hard) +{ + if(hard) { + cpu_clear_irq(IRQ_FRAME | IRQ_DPCM); + } + apu_frame_reset(hard); + apu_race_reset(hard); + apu_square0_reset(hard); + apu_square1_reset(hard); + apu_triangle_reset(hard); + apu_noise_reset(hard); + apu_dpcm_reset(hard); + if(nes->apu.external) + nes->apu.external->reset(); + cycles = 0; + soundbuflen = 0; +} + +u8 apu_read(u32 addr) +{ + u8 ret = 0; + + switch(addr) { + case 0x4015: + ret = 0; + if(nes->apu.square[0].LengthCtr) ret |= 0x01; + if(nes->apu.square[1].LengthCtr) ret |= 0x02; + if(nes->apu.triangle.LengthCtr) ret |= 0x04; + if(nes->apu.noise.LengthCtr) ret |= 0x08; + if(nes->apu.dpcm.LengthCtr) ret |= 0x10; + if(nes->cpu.irqstate & IRQ_FRAME)ret |= 0x40; + if(nes->cpu.irqstate & IRQ_DPCM) ret |= 0x80; + cpu_clear_irq(IRQ_FRAME); +// log_printf("apu_read: $4015: ret = %02X (cycle %d, line %d, frame %d)\n",ret,LINECYCLES,SCANLINE,FRAMES); + return(ret); + } +// log_printf("apu_read: $%04X\n",addr); + return(0); +} + +void apu_write(u32 addr,u8 data) +{ +// log_printf("apu_write: $%04X = $%02X\n",addr,data); + regs[addr & 0x1F] = data; + switch(addr) { + case 0x4000: + case 0x4001: + case 0x4002: + case 0x4003: + apu_square0_write(addr & 3,data); + break; + case 0x4004: + case 0x4005: + case 0x4006: + case 0x4007: + apu_square1_write(addr & 3,data); + break; + case 0x4008: + case 0x4009: + case 0x400A: + case 0x400B: + apu_triangle_write(addr & 3,data); + break; + case 0x400C: + case 0x400D: + case 0x400E: + case 0x400F: + apu_noise_write(addr & 3,data); + break; + case 0x4010: + case 0x4011: + case 0x4012: + case 0x4013: + apu_dpcm_write(addr & 3,data); + break; + case 0x4015: + apu_square0_write(4,data & 1); + apu_square1_write(4,data & 2); + apu_triangle_write(4,data & 4); + apu_noise_write(4,data & 8); + apu_dpcm_write(4,data & 0x10); + break; + case 0x4017: + apu_frame_write(addr,data); + break; + default: + log_printf("apu_write: unhandled write $%04X = $%02X\n",addr,data); + break; + } +} + +static int oldpos = -1; + +static INLINE void updatebuffer() +{ + int sample; + + sample = nes->apu.square[0].Pos + nes->apu.square[1].Pos + nes->apu.triangle.Pos + nes->apu.noise.Pos + nes->apu.dpcm.Pos; + sample *= 64; + if(nes->apu.external) + sample += nes->apu.external->process(40); + if(sample < -0x8000) + sample = -0x8000; + if(sample > 0x7FFF) + sample = 0x7FFF; + soundbuf[soundbuflen++] = (s16)sample; + + //see if we are done with a frame, and if so send it away to the sound system + if(soundbuflen == 735) { + sound_update((void*)soundbuf,735); + soundbuflen = 0; + } +} + +//this is called every cycle +void apu_step() +{ + int pos; + + apu_frame_step(); + apu_race_step(); + apu_square0_step(); + apu_square1_step(); + apu_triangle_step(); + apu_noise_step(); + apu_dpcm_step(); + pos = SOUND_HZ * cycles++ / NES_HZ; + if(pos != oldpos) { + oldpos = pos; + updatebuffer(); + } +} + +void apu_setexternal(external_t *ext) +{ + if(nes->apu.external) + nes->apu.external->kill(); + nes->apu.external = ext; + ext->init(); +} + +void apu_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(regs,0x20); +} + +void apu_set_region(int r) +{ + //if this is a pal-based region + if(r & 1) { + NoiseFreqTable = NoiseFreqPAL; + DpcmFreqTable = DPCMFreqPAL; + FrameCycles = FrameCyclesPAL; + sound_setfps(50); + } + + //ntsc based region + else { + NoiseFreqTable = NoiseFreqNTSC; + DpcmFreqTable = DPCMFreqNTSC; + FrameCycles = FrameCyclesNTSC; + sound_setfps(60); + } +} diff --git a/apps/nesemu2/src/nes/apu/apu.h b/apps/nesemu2/src/nes/apu/apu.h new file mode 100644 index 00000000..08ec475a --- /dev/null +++ b/apps/nesemu2/src/nes/apu/apu.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu_h__ +#define __nes__apu_h__ + +#include "nes/apu/race.h" +#include "nes/apu/units/frame.h" +#include "nes/apu/units/square.h" +#include "nes/apu/units/triangle.h" +#include "nes/apu/units/noise.h" +#include "nes/apu/units/dpcm.h" +#include "nes/apu/units/external.h" + +//apu informations +typedef struct apu_s { + //sound channels + square_t square[2]; + triangle_t triangle; + noise_t noise; + dpcm_t dpcm; + external_t *external; + + //frame counter + frame_t frame; +} apu_t; + +typedef external_t apu_external_t; + +extern u8 LengthCounts[]; + +int apu_init(); +void apu_kill(); +void apu_reset(int hard); +void apu_step(); +u8 apu_read(u32 addr); +void apu_write(u32 addr,u8 data); +void apu_setexternal(external_t *ext); +void apu_state(int mode,u8 *data); +void apu_set_region(int r); +void apu_dpcm_fetch(); + +#endif diff --git a/apps/nesemu2/src/nes/apu/race.h b/apps/nesemu2/src/nes/apu/race.h new file mode 100644 index 00000000..8f30e8e1 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/race.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__race_h__ +#define __nes__apu__race_h__ + +typedef struct race_s { + u8 wavehold, LengthCtr1, LengthCtr2; +} race_t; + +#endif diff --git a/apps/nesemu2/src/nes/apu/race.hh b/apps/nesemu2/src/nes/apu/race.hh new file mode 100644 index 00000000..8fac8bfa --- /dev/null +++ b/apps/nesemu2/src/nes/apu/race.hh @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static void race_reset(race_t *r) +{ + r->wavehold = 0; + r->LengthCtr1 = r->LengthCtr2 = 0; +} + +static INLINE void apu_race_reset(int hard) +{ + race_reset(&nes->apu.square[0].race); + race_reset(&nes->apu.square[1].race); +// race_reset(&nes->apu.triangle.race); +// race_reset(&nes->apu.noise.race); +} + +static INLINE void apu_race_step() +{ + nes->apu.square[0].wavehold = nes->apu.square[0].race.wavehold; + if (nes->apu.square[0].race.LengthCtr1) + { + if (nes->apu.square[0].LengthCtr == nes->apu.square[0].race.LengthCtr2) + nes->apu.square[0].LengthCtr = nes->apu.square[0].race.LengthCtr1; + nes->apu.square[0].race.LengthCtr1 = 0; + } + + nes->apu.square[1].wavehold = nes->apu.square[1].race.wavehold; + if (nes->apu.square[1].race.LengthCtr1) + { + if (nes->apu.square[1].LengthCtr == nes->apu.square[1].race.LengthCtr2) + nes->apu.square[1].LengthCtr = nes->apu.square[1].race.LengthCtr1; + nes->apu.square[1].race.LengthCtr1 = 0; + } + + nes->apu.triangle.wavehold = nes->apu.triangle.race.wavehold; + if (nes->apu.triangle.race.LengthCtr1) + { + if (nes->apu.triangle.LengthCtr == nes->apu.triangle.race.LengthCtr2) + nes->apu.triangle.LengthCtr = nes->apu.triangle.race.LengthCtr1; + nes->apu.triangle.race.LengthCtr1 = 0; + } + + nes->apu.noise.wavehold = nes->apu.noise.race.wavehold; + if (nes->apu.noise.race.LengthCtr1) + { + if (nes->apu.noise.LengthCtr == nes->apu.noise.race.LengthCtr2) + nes->apu.noise.LengthCtr = nes->apu.noise.race.LengthCtr1; + nes->apu.noise.race.LengthCtr1 = 0; + } +} diff --git a/apps/nesemu2/src/nes/apu/units/dpcm.h b/apps/nesemu2/src/nes/apu/units/dpcm.h new file mode 100644 index 00000000..65183c22 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/dpcm.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__dpcm_h__ +#define __nes__apu__dpcm_h__ + +//apu dpcm +typedef struct dpcm_s { + u8 freq, wavehold, doirq, pcmdata, addr, len; + u32 CurAddr, SampleLen; // short + u8 silenced, bufempty, fetching; + u8 shiftreg, outbits, buffer; + u32 LengthCtr; + u32 Cycles; + s32 Pos; +} dpcm_t; + +#endif diff --git a/apps/nesemu2/src/nes/apu/units/dpcm.hh b/apps/nesemu2/src/nes/apu/units/dpcm.hh new file mode 100644 index 00000000..523c5640 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/dpcm.hh @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define dpcm nes->apu.dpcm + +static u32 DPCMFreqNTSC[16] = { + 0x1AC,0x17C,0x154,0x140,0x11E,0x0FE,0x0E2,0x0D6, + 0x0BE,0x0A0,0x08E,0x080,0x06A,0x054,0x048,0x036, +}; + +static u32 DPCMFreqPAL[16] = { + 0x18E,0x162,0x13C,0x12A,0x114,0x0EC,0x0D2,0x0C6, + 0x0B0,0x094,0x084,0x076,0x062,0x04E,0x042,0x032, +}; + +static u32 *DpcmFreqTable; + +static INLINE void apu_dpcm_reset(int hard) +{ + dpcm.freq = dpcm.wavehold = dpcm.doirq = dpcm.pcmdata = dpcm.addr = dpcm.len = 0; + dpcm.CurAddr = dpcm.SampleLen = 0; + dpcm.silenced = 1; + dpcm.shiftreg = dpcm.buffer = 0; + dpcm.LengthCtr = 0; + dpcm.Pos = 0; + + dpcm.Cycles = 1; + dpcm.bufempty = 1; + dpcm.fetching = 0; + dpcm.outbits = 8; +} + +static INLINE void apu_dpcm_write(u32 addr,u8 data) +{ + switch (addr) + { + case 0: dpcm.freq = data & 0xF; + dpcm.wavehold = (data >> 6) & 0x1; + dpcm.doirq = data >> 7; + if (!dpcm.doirq) + cpu_clear_irq(IRQ_DPCM); + break; + case 1: dpcm.pcmdata = data & 0x7F; + dpcm.Pos = (dpcm.pcmdata - 0x40) * 3; + break; + case 2: dpcm.addr = data; + break; + case 3: dpcm.len = data; + break; + case 4: if (data) + { + if (!dpcm.LengthCtr) + { + dpcm.CurAddr = 0xC000 | (dpcm.addr << 6); + dpcm.LengthCtr = (dpcm.len << 4) + 1; + } + } + else dpcm.LengthCtr = 0; + cpu_clear_irq(IRQ_DPCM); + break; + } +} + +static INLINE void apu_dpcm_step() +{ + // this uses pre-decrement due to the lookup table + if (!--dpcm.Cycles) + { + dpcm.Cycles = DpcmFreqTable[dpcm.freq]; + if (!dpcm.silenced) + { + if (dpcm.shiftreg & 1) + { + if (dpcm.pcmdata <= 0x7D) + dpcm.pcmdata += 2; + } + else + { + if (dpcm.pcmdata >= 0x02) + dpcm.pcmdata -= 2; + } + dpcm.shiftreg >>= 1; + dpcm.Pos = (dpcm.pcmdata - 0x40) * 3; + } + if (!--dpcm.outbits) + { + dpcm.outbits = 8; + if (!dpcm.bufempty) + { + dpcm.shiftreg = dpcm.buffer; + dpcm.bufempty = 1; + dpcm.silenced = 0; + } + else dpcm.silenced = 1; + } + } + if (dpcm.bufempty && !dpcm.fetching && dpcm.LengthCtr) + { + dpcm.fetching = 1; + nes->cpu.pcmcycles = 4; +// CPU::PCMCycles = 4; + // decrement LengthCtr now, so $4015 reads are updated in time + dpcm.LengthCtr--; + } +} + +void apu_dpcm_fetch() +{ + dpcm.buffer = cpu_read(dpcm.CurAddr); + cpu_tick(); + dpcm.bufempty = 0; + dpcm.fetching = 0; + if (++dpcm.CurAddr == 0x10000) + dpcm.CurAddr = 0x8000; + if (!dpcm.LengthCtr) + { + if (dpcm.wavehold) + { + dpcm.CurAddr = 0xC000 | (dpcm.addr << 6); + dpcm.LengthCtr = (dpcm.len << 4) + 1; + } + else if (dpcm.doirq) + cpu_set_irq(IRQ_DPCM); + } +} + +#undef dpcm \ No newline at end of file diff --git a/apps/nesemu2/src/nes/apu/units/external.h b/apps/nesemu2/src/nes/apu/units/external.h new file mode 100644 index 00000000..983f9c2b --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/external.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__external_h__ +#define __nes__apu__external_h__ + +//external sound +typedef struct external_s +{ + void (*init)(); + void (*kill)(); + void (*reset)(); + int (*process)(int); + void (*state)(int,u8*); +} external_t; + +#endif diff --git a/apps/nesemu2/src/nes/apu/units/frame.h b/apps/nesemu2/src/nes/apu/units/frame.h new file mode 100644 index 00000000..e4d53337 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/frame.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__frame_h__ +#define __nes__apu__frame_h__ + +//apu frame counter +typedef struct frame_s { + s32 cycles; + u8 reg; + u8 quarter,half; + u8 irq,zero; +} frame_t; + +#endif diff --git a/apps/nesemu2/src/nes/apu/units/frame.hh b/apps/nesemu2/src/nes/apu/units/frame.hh new file mode 100644 index 00000000..9ec8af9f --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/frame.hh @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define FRAME_REG nes->apu.frame.reg +#define FRAME_CYCLES nes->apu.frame.cycles +#define FRAME_QUARTER nes->apu.frame.quarter +#define FRAME_HALF nes->apu.frame.half +#define FRAME_IRQ nes->apu.frame.irq +#define FRAME_ZERO nes->apu.frame.zero + +static s32 FrameCyclesNTSC[5] = { 7456,14912,22370,29828,37280 }; +static s32 FrameCyclesPAL[5] = { 8312,16626,24938,33252,41560 }; + +static s32 *FrameCycles; + +static INLINE void apu_frame_reset(int hard) +{ + if(hard) { + FRAME_REG = 0; + } + FRAME_CYCLES = 0; + FRAME_QUARTER = 0; + FRAME_HALF = 0; + FRAME_IRQ = 0; + FRAME_ZERO = 0; +} + +static INLINE void apu_frame_write(u32 addr,u8 data) +{ + FRAME_REG = data & 0xC0; + if(nes->cpu.cycles & 1) + FRAME_ZERO = 3; + else + FRAME_ZERO = 2; + if(data & 0x40) { + cpu_clear_irq(IRQ_FRAME); + } +// log_printf("apu_frame_write: lengthcounter = %02X (cycle %d, line %d, frame %d)\n",data,LINECYCLES,SCANLINE,FRAMES); +} + +static INLINE void apu_frame_step() +{ + if(FRAME_CYCLES == FrameCycles[0]) { + FRAME_QUARTER = 2; + } + else if(FRAME_CYCLES == FrameCycles[1]) { + FRAME_QUARTER = 2; + FRAME_HALF = 2; + } + else if(FRAME_CYCLES == FrameCycles[2]) { + FRAME_QUARTER = 2; + } + else if(FRAME_CYCLES == FrameCycles[3]) { + if ((FRAME_REG & 0x80) == 0) { + FRAME_QUARTER = 2; + FRAME_HALF = 2; + FRAME_IRQ = 3; + FRAME_CYCLES = -2; + } + } + else if (FRAME_CYCLES == FrameCycles[4]) { + FRAME_QUARTER = 2; + FRAME_HALF = 2; + FRAME_CYCLES = -2; + } + FRAME_CYCLES++; + if(FRAME_QUARTER && --FRAME_QUARTER == 0) { + apu_square0_quarter(); + apu_square1_quarter(); + apu_triangle_quarter(); + apu_noise_quarter(); + } + if(FRAME_HALF && --FRAME_HALF == 0) { + apu_square0_half(); + apu_square1_half(); + apu_triangle_half(); + apu_noise_half(); + } + if(FRAME_IRQ) { + if(FRAME_REG == 0) { +// if(nes->cpu.flags.i == 0) +// log_printf("apu_frame_step: frame irq! cycle %d, line %d, frame %d\n",LINECYCLES,SCANLINE,FRAMES); + cpu_set_irq(IRQ_FRAME); + } + FRAME_IRQ--; + } + if(FRAME_ZERO && --FRAME_ZERO == 0) { + if(FRAME_REG & 0x80) { + FRAME_QUARTER = 2; + FRAME_HALF = 2; + } + FRAME_CYCLES = 0; + } +} diff --git a/apps/nesemu2/src/nes/apu/units/noise.h b/apps/nesemu2/src/nes/apu/units/noise.h new file mode 100644 index 00000000..cd8d351d --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/noise.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__noise_h__ +#define __nes__apu__noise_h__ + +//apu noise +typedef struct noise_s { + u8 volume, envelope, wavehold, datatype; + u32 freq; + u32 CurD; + u8 Vol; + u8 LengthCtr; + u8 EnvCtr, Envelope; + u8 Enabled; + u8 EnvClk; + u32 Cycles; + s32 Pos; + race_t race; +} noise_t; + +#endif diff --git a/apps/nesemu2/src/nes/apu/units/noise.hh b/apps/nesemu2/src/nes/apu/units/noise.hh new file mode 100644 index 00000000..0c897270 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/noise.hh @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define noi nes->apu.noise + +static u32 NoiseFreqNTSC[16] = { + 0x004,0x008,0x010,0x020,0x040,0x060,0x080,0x0A0, + 0x0CA,0x0FE,0x17C,0x1FC,0x2FA,0x3F8,0x7F2,0xFE4, +}; + +static u32 NoiseFreqPAL[16] = { + 0x004,0x007,0x00E,0x01E,0x03C,0x058,0x076,0x094, + 0x0BC,0x0EC,0x162,0x1D8,0x2C4,0x3B0,0x762,0xEC2, +}; + +static u32 *NoiseFreqTable; + +static INLINE void apu_noise_reset(int hard) +{ + noi.volume = noi.envelope = noi.wavehold = noi.datatype = 0; + noi.freq = 0; + noi.Vol = 0; + noi.LengthCtr = 0; + noi.Envelope = 0; + noi.Enabled = 0; + noi.EnvClk = 0; + noi.Pos = 0; + noi.CurD = 1; + noi.Cycles = 1; + noi.EnvCtr = 1; +} + +static INLINE void apu_noise_write(u32 addr,u8 data) +{ + switch (addr) + { + case 0: noi.volume = data & 0x0F; + noi.envelope = data & 0x10; + noi.race.wavehold = data & 0x20; + noi.Vol = noi.envelope ? noi.volume : noi.Envelope; + if (noi.LengthCtr) + noi.Pos = ((noi.CurD & 0x4000) ? -2 : 2) * noi.Vol; + break; + case 2: noi.freq = data & 0xF; + noi.datatype = data & 0x80; + break; + case 3: if (noi.Enabled) + { + noi.race.LengthCtr1 = LengthCounts[(data >> 3) & 0x1F]; + noi.race.LengthCtr2 = noi.LengthCtr; + } + noi.EnvClk = 1; + break; + case 4: noi.Enabled = data ? 1 : 0; + if (!noi.Enabled) + noi.LengthCtr = 0; + break; + } +} + +static INLINE void apu_noise_step() +{ + // this uses pre-decrement due to the lookup table + if (!--noi.Cycles) + { + noi.Cycles = NoiseFreqTable[noi.freq]; + if (noi.datatype) + noi.CurD = (noi.CurD << 1) | (((noi.CurD >> 14) ^ (noi.CurD >> 8)) & 0x1); + else noi.CurD = (noi.CurD << 1) | (((noi.CurD >> 14) ^ (noi.CurD >> 13)) & 0x1); + if (noi.LengthCtr) + noi.Pos = ((noi.CurD & 0x4000) ? -2 : 2) * noi.Vol; + } +} + +static INLINE void apu_noise_quarter() +{ + if (noi.EnvClk) + { + noi.EnvClk = 0; + noi.Envelope = 0xF; + noi.EnvCtr = noi.volume; + } + else if (!noi.EnvCtr--) + { + noi.EnvCtr = noi.volume; + if (noi.Envelope) + noi.Envelope--; + else noi.Envelope = noi.wavehold ? 0xF : 0x0; + } + noi.Vol = noi.envelope ? noi.volume : noi.Envelope; + if (noi.LengthCtr) + noi.Pos = ((noi.CurD & 0x4000) ? -2 : 2) * noi.Vol; +} + +static INLINE void apu_noise_half() +{ + if (noi.LengthCtr && !noi.wavehold) + noi.LengthCtr--; +} diff --git a/apps/nesemu2/src/nes/apu/units/square.h b/apps/nesemu2/src/nes/apu/units/square.h new file mode 100644 index 00000000..73fd4cb5 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/square.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__square_h__ +#define __nes__apu__square_h__ + +typedef struct square_s { + u8 volume,envelope,wavehold,duty; + u8 swpspeed,swpdir,swpstep,swpenab; + u32 freq; + u8 Vol; + u8 CurD; + u8 LengthCtr; + u8 EnvCtr, Envelope, BendCtr; + u8 Enabled, ValidFreq, Active; + u8 EnvClk, SwpClk; + u32 Cycles; + s32 Pos; + race_t race; +} square_t; + +#define apu_square0_reset(hard) apu_square_reset(&nes->apu.square[0],hard) +#define apu_square0_write(addr,data) apu_square_write(&nes->apu.square[0],addr,data) +#define apu_square0_step() apu_square_step(&nes->apu.square[0]) +#define apu_square0_quarter() apu_square_quarter(&nes->apu.square[0]) +#define apu_square0_half() apu_square_half(&nes->apu.square[0]) + +#define apu_square1_reset(hard) apu_square_reset(&nes->apu.square[1],hard) +#define apu_square1_write(addr,data) apu_square_write(&nes->apu.square[1],addr,data) +#define apu_square1_step() apu_square_step(&nes->apu.square[1]) +#define apu_square1_quarter() apu_square_quarter(&nes->apu.square[1]) +#define apu_square1_half() apu_square_half(&nes->apu.square[1]) + +#endif diff --git a/apps/nesemu2/src/nes/apu/units/square.hh b/apps/nesemu2/src/nes/apu/units/square.hh new file mode 100644 index 00000000..6009a109 --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/square.hh @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static const s8 SquareDuty[4][8] = { + {-4,-4,-4,-4,-4,-4,-4,+4}, + {-4,-4,-4,-4,-4,-4,+4,+4}, + {-4,-4,-4,-4,+4,+4,+4,+4}, + {+4,+4,+4,+4,+4,+4,-4,-4}, +}; + +static INLINE void squarecheckactive(square_t *sq) +{ + sq->ValidFreq = (sq->freq >= 0x8) && ((sq->swpdir) || !((sq->freq + (sq->freq >> sq->swpstep)) & 0x800)); + sq->Active = sq->LengthCtr && sq->ValidFreq; + sq->Pos = sq->Active ? (SquareDuty[sq->duty][sq->CurD] * sq->Vol) : 0; +} + +static INLINE void apu_square_reset(square_t *sq,int hard) +{ + sq->volume = sq->envelope = sq->wavehold = sq->duty = 0; + sq->swpspeed = sq->swpdir = sq->swpstep = sq->swpenab = 0; + sq->freq = 0; + sq->Vol = 0; + sq->CurD = 0; + sq->LengthCtr = 0; + sq->Envelope = 0; + sq->Enabled = sq->ValidFreq = sq->Active = 0; + sq->EnvClk = sq->SwpClk = 0; + sq->Pos = 0; + sq->Cycles = 1; + sq->EnvCtr = 1; + sq->BendCtr = 1; +} + +static INLINE void apu_square_write(square_t *sq,u32 addr,u8 data) +{ + switch (addr) + { + case 0: sq->volume = data & 0xF; + sq->envelope = data & 0x10; + sq->race.wavehold = data & 0x20; + sq->duty = (data >> 6) & 0x3; + sq->Vol = sq->envelope ? sq->volume : sq->Envelope; + break; + case 1: sq->swpstep = data & 0x07; + sq->swpdir = data & 0x08; + sq->swpspeed = (data >> 4) & 0x7; + sq->swpenab = data & 0x80; + sq->SwpClk = 1; + break; + case 2: sq->freq &= 0x700; + sq->freq |= data; + break; + case 3: sq->freq &= 0xFF; + sq->freq |= (data & 0x7) << 8; + if (sq->Enabled) + { + sq->race.LengthCtr1 = LengthCounts[(data >> 3) & 0x1F]; + sq->race.LengthCtr2 = sq->LengthCtr; + } + sq->CurD = 0; + sq->EnvClk = 1; + break; + case 4: sq->Enabled = data ? 1 : 0; + if (!sq->Enabled) + sq->LengthCtr = 0; + break; + } + squarecheckactive(sq); +} + +static INLINE void apu_square_step(square_t *sq) +{ + if (!sq->Cycles--) + { + sq->Cycles = sq->freq << 1; + sq->CurD = (sq->CurD - 1) & 0x7; + if (sq->Active) + sq->Pos = SquareDuty[sq->duty][sq->CurD] * sq->Vol; + } +} + +static INLINE void apu_square_quarter(square_t *sq) +{ + if (sq->EnvClk) + { + sq->EnvClk = 0; + sq->Envelope = 0xF; + sq->EnvCtr = sq->volume; + } + else if (!sq->EnvCtr--) + { + sq->EnvCtr = sq->volume; + if (sq->Envelope) + sq->Envelope--; + else sq->Envelope = sq->wavehold ? 0xF : 0x0; + } + sq->Vol = sq->envelope ? sq->volume : sq->Envelope; + squarecheckactive(sq); +} + +static INLINE void apu_square_half(square_t *sq) +{ + if (!sq->BendCtr--) + { + sq->BendCtr = sq->swpspeed; + if (sq->swpenab && sq->swpstep && sq->ValidFreq) + { + int sweep = sq->freq >> sq->swpstep; + sq->freq += sq->swpdir ? ~sweep : sweep; + } + } + if (sq->SwpClk) + { + sq->SwpClk = 0; + sq->BendCtr = sq->swpspeed; + } + if (sq->LengthCtr && !sq->wavehold) + sq->LengthCtr--; + squarecheckactive(sq); +} diff --git a/apps/nesemu2/src/nes/apu/units/triangle.h b/apps/nesemu2/src/nes/apu/units/triangle.h new file mode 100644 index 00000000..591686ce --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/triangle.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__apu__triangle_h__ +#define __nes__apu__triangle_h__ + +//apu triangle +typedef struct triangle_s { + u8 linear, wavehold; + u32 freq; + u8 CurD; + u8 LengthCtr, LinCtr; + u8 Enabled, Active; + u8 LinClk; + u32 Cycles; + s32 Pos; + race_t race; +} triangle_t; + +#endif diff --git a/apps/nesemu2/src/nes/apu/units/triangle.hh b/apps/nesemu2/src/nes/apu/units/triangle.hh new file mode 100644 index 00000000..c57ba3ec --- /dev/null +++ b/apps/nesemu2/src/nes/apu/units/triangle.hh @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define tri nes->apu.triangle + +static s8 TriangleDuty[32] = { + +7,+6,+5,+4,+3,+2,+1,+0, + -1,-2,-3,-4,-5,-6,-7,-8, + -8,-7,-6,-5,-4,-3,-2,-1, + +0,+1,+2,+3,+4,+5,+6,+7, +}; + +static INLINE void trianglecheckactive() +{ + tri.Active = tri.LengthCtr && tri.LinCtr; + if (tri.freq < 4) + tri.Pos = 0; // beyond hearing range + else tri.Pos = TriangleDuty[tri.CurD] * 8; +} + +static INLINE void apu_triangle_reset(int hard) +{ + tri.linear = tri.wavehold = 0; + tri.freq = 0; + tri.CurD = 0; + tri.LengthCtr = tri.LinCtr = 0; + tri.Enabled = tri.Active = 0; + tri.LinClk = 0; + tri.Pos = 0; + tri.Cycles = 1; +} + +static INLINE void apu_triangle_write(u32 addr,u8 data) +{ + switch (addr) + { + case 0: tri.linear = data & 0x7F; + tri.race.wavehold = (data >> 7) & 0x1; + break; + case 2: tri.freq &= 0x700; + tri.freq |= data; + break; + case 3: tri.freq &= 0xFF; + tri.freq |= (data & 0x7) << 8; + if (tri.Enabled) + { + tri.race.LengthCtr1 = LengthCounts[(data >> 3) & 0x1F]; + tri.race.LengthCtr2 = tri.LengthCtr; + } + tri.LinClk = 1; + break; + case 4: tri.Enabled = data ? 1 : 0; + if (!tri.Enabled) + tri.LengthCtr = 0; + break; + } + trianglecheckactive(); +} + +static INLINE void apu_triangle_step() +{ + if (!tri.Cycles--) + { + tri.Cycles = tri.freq; + if (tri.Active) + { + tri.CurD++; + tri.CurD &= 0x1F; + if (tri.freq < 4) + tri.Pos = 0; // beyond hearing range + else tri.Pos = TriangleDuty[tri.CurD] * 8; + } + } +} + +static INLINE void apu_triangle_quarter() +{ + if (tri.LinClk) + tri.LinCtr = tri.linear; + else if (tri.LinCtr) + tri.LinCtr--; + if (!tri.wavehold) + tri.LinClk = 0; + trianglecheckactive(); +} + +static INLINE void apu_triangle_half() +{ + if (tri.LengthCtr && !tri.wavehold) + tri.LengthCtr--; + trianglecheckactive(); +} diff --git a/apps/nesemu2/src/nes/cart/cart.c b/apps/nesemu2/src/nes/cart/cart.c new file mode 100644 index 00000000..abd3da89 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/cart.c @@ -0,0 +1,324 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/memutil.h" +#include "misc/log.h" +#include "misc/paths.h" +#include "misc/crc32.h" +#include "misc/memfile.h" +#include "nes/cart/cart.h" +#include "nes/cart/ines.h" +#include "nes/cart/ines20.h" +#include "nes/cart/unif.h" +#include "nes/cart/fds.h" +#include "nes/cart/doctor.h" +#include "nes/cart/nsf.h" +#include "nes/cart/patch/patch.h" + +#define FREE(p) { \ + if(p) { \ + mem_free(p); \ + p = 0; \ + } \ +} +#define FREE_DATA(d) { \ + FREE(d.data); \ + d.size = 0; \ + d.mask = 0; \ +} + +#define generate_mask_and_crc32(d) \ + d.mask = createmask(d.size); \ + d.crc32 = crc32(d.data,d.size); + +#define FORMAT_ERROR -1 +#define FORMAT_UNKNOWN 0 +#define FORMAT_INES 1 +#define FORMAT_INES20 2 +#define FORMAT_UNIF 3 +#define FORMAT_FDS 4 +#define FORMAT_RAWFDS 5 +#define FORMAT_NSF 6 +#define FORMAT_SPLIT 7 +#define FORMAT_DOCTOR 8 + +//thanks tnse for this, long ago +static u32 createmask(u32 size) +{ + u32 x,y,result; + + result = size - 1; + for(x=0;x<32;x++) { + y = 1 << x; + if(y == size) + return(result); + if(y > size) { + result = y - 1; + return(result); + } + } + return(0xFFFFFFFF); +} + +static int determineformat(memfile_t *file) +{ + u8 ident_ines[] = "NES\x1a"; + u8 ident_unif[] = "UNIF"; + u8 ident_fds[] = "FDS\x1a"; + u8 ident_rawfds[] = "\x01*NINTENDO-HVC*"; + u8 ident_nsf[] = "NESM\x1a"; + u8 header[66]; + + //read first 66 bytes and reset curpos + memfile_read(header,1,66,file); + memfile_rewind(file); + + //check if ines format + if(memcmp(header,ident_ines,4) == 0) { + if((header[15] == 0) && ((header[7] & 0xC) == 0x8)) + return(FORMAT_INES20); + return(FORMAT_INES); + } + + //check if unif + if(memcmp(header,ident_unif,4) == 0) + return(FORMAT_UNIF); + + //check if fds disk + if(memcmp(header,ident_fds,4) == 0) + return(FORMAT_FDS); + + //check if raw fds disk + if(memcmp(header, ident_rawfds, 15) == 0) + return(FORMAT_RAWFDS); + + //check if raw fds disk + if(memcmp(header,ident_nsf,5) == 0) + return(FORMAT_NSF); + + //determine gamedoctor disk + if (memcmp(header + 3, ident_rawfds, 15) == 0 && header[0] == 0 && header[1] == 0) { + if (header[61] == 2 && header[63] == 0 && header[64] == 0 && header[65] == 3) + return(FORMAT_DOCTOR); + } + + return(FORMAT_UNKNOWN); +} + +cart_t *cart_load(const char *filename) +{ + return(cart_load_patched(filename,0)); +} + +cart_t *cart_load_patched(const char *filename,const char *patchfilename) +{ + cart_t *ret = 0; + int format,n; + memfile_t *file; + + //try to open file + if((file = memfile_open((char*)filename,"rb")) == 0) { + log_printf("cart_load: error opening '%s'\n",filename); + return(0); + } + + //find out what format the file is + format = determineformat(file); + + //print errors for unknown format or error loading + if(format == FORMAT_UNKNOWN) { + log_printf("cart_load: unknown file format in '%s'\n",filename); + memfile_close(file); + return(0); + } + if(format == FORMAT_ERROR) { + log_printf("cart_load: error reading '%s'\n",filename); + memfile_close(file); + return(0); + } + + //allocate memory for struct + ret = (cart_t*)mem_alloc(sizeof(cart_t)); + memset(ret,0,sizeof(cart_t)); + + //if patch filename was passed, load the patch and patch the file in memory + if(patchfilename) { + if((ret->patch = patch_load(patchfilename))) { + patch_apply(ret->patch,file); + memfile_seek(file,0,SEEK_SET); + } + } + + //load the file + switch(format) { + case FORMAT_INES: n = cart_load_ines(ret,file); break; + case FORMAT_INES20: n = cart_load_ines20(ret,file); break; + case FORMAT_UNIF: n = cart_load_unif(ret,file); break; + case FORMAT_FDS: + case FORMAT_RAWFDS: n = cart_load_fds(ret, file); break; + case FORMAT_DOCTOR: n = cart_load_doctor(ret, file); break; + case FORMAT_NSF: n = cart_load_nsf(ret,file); break; +// case FORMAT_SPLIT: n = cart_load_split(ret,file); break; + default: + log_printf("cart_load: unhandled format (bug).\n"); + mem_free(ret); + memfile_close(file); + return(0); + } + + //if error, free data and print error + if(n != 0) { + mem_free(ret); + memfile_close(file); + log_printf("cart_load: error loading '%s'\n", filename); + return(0); + } + + //tile cache stuff + if(ret->chr.size) { + //allocate memory for the tile cache + ret->cache = (cache_t*)mem_alloc(ret->chr.size); + ret->cache_hflip = (cache_t*)mem_alloc(ret->chr.size); + + //convert all chr tiles to cache tiles + cache_tiles(ret->chr.data,ret->cache,ret->chr.size / 16,0); + cache_tiles(ret->chr.data,ret->cache_hflip,ret->chr.size / 16,1); + } + + //see if title exists and clean it up + if(strlen(ret->title)) { + char *p = ret->title; + int i; + + for(i=0;i<512;i++,p++) { + if(*p == 0x0D || *p == 0x0A) { + *p = ' '; + continue; + } + if(*p == 0x1A) { + *p = 0; + break; + } + } + } + + //rom loaded ok, create necessary masks and generate crc32's + generate_mask_and_crc32(ret->prg); + generate_mask_and_crc32(ret->chr); + generate_mask_and_crc32(ret->trainer); + generate_mask_and_crc32(ret->pc10rom); + + //store the filename + if(ret->patch) { + char *p; + + paths_normalize(ret->patch->filename); + p = strrchr(ret->patch->filename,PATH_SEPERATOR); + if(p == 0) + p = ret->patch->filename; + else + p++; + ret->filename = (char*)mem_alloc(strlen(filename) + strlen(p) + 2); + sprintf(ret->filename,"%s+%s",filename,p); + } + else + ret->filename = mem_strdup((char*)filename); + + log_printf("cart_load: rom filename is '%s'\n",ret->filename); + + //close file + memfile_close(file); + + //finished + return(ret); +} + +void cart_unload(cart_t *r) +{ + if(r) { + FREE_DATA(r->prg); + FREE_DATA(r->chr); + FREE_DATA(r->vram); + FREE_DATA(r->wram); + FREE_DATA(r->trainer); + FREE_DATA(r->pc10rom); + FREE_DATA(r->disk); + FREE_DATA(r->diskoriginal); + FREE(r->cache); + FREE(r->cache_hflip); + FREE(r->vcache); + FREE(r->vcache_hflip); + FREE(r->filename); + patch_unload(r->patch); + FREE(r); + } +} + +#define ram_alloc(size,ptr) (u8*)(ptr ? mem_realloc(ptr,size) : mem_alloc(size)) + +static void allocdata(data_t *data,u32 len) +{ + //if size hasnt changed, return + if(data->data) { + if(data->size == len) { + return; + } + if(data->size > len) { + log_printf("allocdata: cannot reduce size of memory region (old = %d, new = %d)\n",data->size,len); + return; + } + } + + //set size and create mask + data->size = len; + data->mask = createmask(len); + + //allocate (or reallocate) memory + if(data->data == 0) + data->data = mem_alloc(len); + else + data->data = mem_realloc(data->data,len); + + //zero out the newly allocated memory + memset(data->data,0,len); +} + +void cart_setwramsize(cart_t *r,int banks) +{ + //UGLY UGLY HACK! + if(banks < 128) + banks *= 1024; + //END UGLY UGLY HACK + allocdata(&r->wram,banks); + log_printf("cart_setwramsize: wram size set to %dkb\n",banks / 1024); +} + +void cart_setvramsize(cart_t *r,int banks) +{ + allocdata(&r->vram,banks * 1024); + log_printf("cart_setvramsize: vram size set to %dkb\n",banks); + + //tile cache data + r->vcache = (cache_t*)ram_alloc(r->vram.size,r->vcache); + r->vcache_hflip = (cache_t*)ram_alloc(r->vram.size,r->vcache_hflip); +} diff --git a/apps/nesemu2/src/nes/cart/cart.h b/apps/nesemu2/src/nes/cart/cart.h new file mode 100644 index 00000000..c2366758 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/cart.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__cart_h__ +#define __nes__cart_h__ + +#include "types.h" +#include "misc/memfile.h" +#include "nes/ppu/tilecache.h" +#include "nes/cart/patch/patch.h" + +//max length for cart titles +#define CART_TITLE_LEN 512 + +//mirroring types +#define MIRROR_H 0 +#define MIRROR_V 1 +#define MIRROR_1L 2 +#define MIRROR_1H 3 +#define MIRROR_4 4 +#define MIRROR_MAPPER 5 + +//battery flags +#define BATTERY_SRAM 1 +#define BATTERY_VRAM 2 + +//ines 2.0 ppu +#define RP2C03B 0 +#define RP2C03G 1 +#define RP2C04_0001 2 +#define RP2C04_0002 3 +#define RP2C04_0003 4 +#define RP2C04_0004 5 +#define RC2C03B 6 +#define RC2C03C 7 +#define RC2C05_01 8 +#define RC2C05_02 9 +#define RC2C05_03 10 +#define RC2C05_04 11 +#define RC2C05_05 12 + +//ines 2.0 vs +#define VS_NORMAL 0 +#define VS_RBI_BASEBALL 1 +#define VS_TKO_BOXING 2 +#define VS_SUPER_XEVIOUS 3 + +typedef struct data_s { + u32 size,mask; + u32 crc32; + u8 *data; +} data_t; + +typedef struct cart_s { + + //rom information + data_t prg,chr; //prg/chr data + data_t vram,wram; //vram/wram data + data_t trainer; //trainer data + data_t pc10rom; //pc10 rom data + data_t disk; //fds disk data + data_t diskoriginal; //original fds disk data + + //internal mapper id + int mapperid; + + //mirroring, battery flag + u8 mirroring,battery; + + //tv type (ntsc,pal) + u8 tvmode; + + //name of game + char title[CART_TITLE_LEN]; + + //data to be used for anything (or for nsf header) + u8 data[0x80]; + + //cached tile data + cache_t *cache,*cache_hflip; //chr cache + cache_t *vcache,*vcache_hflip; //vram cache + + //loaded file's name + char *filename; + + //loaded patch file + patch_t *patch; + +} cart_t; + +cart_t *cart_load(const char *filename); +cart_t *cart_load_patched(const char *filename,const char *patchfilename); +void cart_unload(cart_t *r); +void cart_setwramsize(cart_t *r,int banks); +void cart_setvramsize(cart_t *r,int banks); + +#endif diff --git a/apps/nesemu2/src/nes/cart/doctor.c b/apps/nesemu2/src/nes/cart/doctor.c new file mode 100644 index 00000000..994743f1 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/doctor.c @@ -0,0 +1,265 @@ +/*************************************************************************** + * Copyright (C) 2013-2016 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/log.h" +#include "misc/config.h" +#include "misc/paths.h" +#include "nes/cart/cart.h" +#include "mappers/mapperid.h" + +static u8 fdsident2[] = "\x01*NINTENDO-HVC*"; + +static int loadbios_new(char *filename, u8 **data, int *size) +{ + memfile_t *file; + int n = 0; + + *size = 0; + *data = 0; + + //open bios file + if ((file = memfile_open(filename, "rb")) == 0) { + log_printf("loadbios: error opening '%s'\n", filename); + return(1); + } + + //setup prg for bios + *size = 0x2000; + *data = (u8*)mem_alloc(*size); + + //read bios + if (memfile_read(*data, 1, 0x2000, file) != 0x2000) { + log_printf("loadbios: error reading bios file '%s'\n", filename); + n = 1; + } + else + log_printf("loadbios: loaded bios file '%s'\n", filename); + + //close bios file handle + memfile_close(file); + + //return + return(n); +} + +#if 0 +static int loadbios(cart_t *ret, char *filename) +{ + memfile_t *file; + int n = 0; + + //open bios file + if ((file = memfile_open(filename, "rb")) == 0) { + log_printf("loadbios: error opening fds bios '%s'\n", filename); + return(1); + } + + //setup prg for bios + ret->prg.size = 0x4000; + ret->prg.mask = 0x3FFF; + ret->prg.data = (u8*)mem_alloc(ret->prg.size); + + //read bios + if (memfile_read(ret->prg.data, 1, 0x2000, file) != 0x2000) { + log_printf("loadbios: error reading bios file '%s'\n", filename); + n = 1; + } + else + log_printf("loadbios: loaded bios file '%s'\n", filename); + + //close bios file handle + memfile_close(file); + + //return + return(n); +} +#endif + +void copy_block(u8 *dst, u8 *src, int size) { + memcpy(dst, src, size); +} + +#define SIDE_SIZE 80000 + +void load_diskside(memfile_t *mf, u8 *data, int size) +{ + u8 *tmpbuf, *ptr; + int i, o; + + //convert the format to fds format + tmpbuf = (u8*)mem_alloc(SIDE_SIZE); + + memfile_read(tmpbuf, 1, size, mf); + + ptr = data; + i = 3; + o = 0; + + memcpy(ptr + o, tmpbuf + i, 0x38); + i += 0x38 + 2; + o += 0x38; + + memcpy(ptr + o, tmpbuf + i, 2); + i += 2 + 2; + o += 2; + + while (tmpbuf[i] == 3) { + + int size = (tmpbuf[i + 13] | (tmpbuf[i + 14] << 8)) + 1; + + memcpy(ptr + o, tmpbuf + i, 16); + i += 16 + 2; + o += 16; + + memcpy(ptr + o, tmpbuf + i, size); + i += size + 2; + o += size; + } + mem_free(tmpbuf); +} + +int count_doctors(char *filename) +{ + char diskfile[1024]; + memfile_t *mf; + int ret = 0; + + strncpy(diskfile, filename, 1024); + for (;;) { + if ((mf = memfile_open(diskfile, "rb")) == 0) { + break; + } + memfile_close(mf); + diskfile[strlen(diskfile) - 1]++; + ret++; + } + return(ret); +} + +void load_doctors(char *filename, u32 *size, u8 **data) +{ + char diskfile[1024]; + memfile_t *mf; + int i, num; + u8 *ptr; + + strcpy(diskfile, filename); + + num = count_doctors(filename); + log_printf("doctors: %d\n", num); + + *size = num * SIDE_SIZE; + *data = mem_alloc(*size); + ptr = *data; + + for (i = 0; i < num; i++) { + + //try to open disk image + if ((mf = memfile_open(diskfile, "rb")) == 0) { + break; + } + + //load disk side + load_diskside(mf, ptr, mf->size); + + //increment + log_printf("loaded '%s' (%d bytes).\n", diskfile, mf->size); + memfile_close(mf); + diskfile[strlen(diskfile) - 1]++; + ptr += SIDE_SIZE; + } + log_printf("load_doctors: finished. %d bytes.\n", *size); +} + +int cart_load_doctor(cart_t *ret, memfile_t *file) +{ + u8 gdheader[3]; + u8 header[16]; + u32 size; + char biosfile[1024]; + char drbiosfile[1024]; + + //clear the string + memset(biosfile, 0, 1024); + + //parse the bios path + config_get_eval_string(biosfile, "path.bios"); + config_get_eval_string(drbiosfile, "path.bios"); + + //append the path seperator + str_appendchar(biosfile, PATH_SEPERATOR); + str_appendchar(drbiosfile, PATH_SEPERATOR); + + //append the bios filename + strcat(biosfile, config_get_string("nes.fds.bios")); + strcat(drbiosfile, "dr6+_rom.bin"); + + //try to load bios from the bios directory +/* if (loadbios(ret, biosfile) != 0) { + + //see if bios is in the current directory + if (loadbios(ret, config_get_string("nes.fds.bios")) != 0) { + return(1); + } + }*/ + + u8 *bios, *drbios; + int bioslen, drbioslen; + + loadbios_new(biosfile, &bios, &bioslen); + loadbios_new(drbiosfile, &drbios, &drbioslen); + + ret->prg.size = 0x4000; + ret->prg.mask = 0x3FFF; + ret->prg.data = (u8*)mem_alloc(0x4000); + memcpy(ret->prg.data + 0x0000, bios, 0x2000); + memcpy(ret->prg.data + 0x2000, drbios, 0x2000); + + mem_free(bios); + mem_free(drbios); + + //get length of file + size = memfile_size(file); + + //read the game doctor header and following fds header + memfile_read(gdheader, 1, 3, file); + memfile_read(header, 1, 16, file); + + //check if disk is valid + if (memcmp(header, fdsident2, 15) != 0) { + log_printf("cart_load_doctor: bad gamedoctor image.\n"); + return(1); + } + + //set mapper id to game doctor mapper + ret->mapperid = B_DOCTOR; + + //setup the disk data pointers + load_doctors(file->filename, &ret->disk.size, &ret->disk.data); + ret->diskoriginal.size = 0; + ret->diskoriginal.data = 0; + + log_printf("cart_load_doctor: loaded disk, %d sides (%d bytes)\n", ret->disk.size / SIDE_SIZE, size); + return(0); +} diff --git a/apps/nesemu2/src/nes/cart/doctor.h b/apps/nesemu2/src/nes/cart/doctor.h new file mode 100644 index 00000000..9188c311 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/doctor.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cart_doctor_h__ +#define __cart_doctor_h__ + +#include "nes/cart/cart.h" + +int cart_load_doctor(cart_t *ret, memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/fds.c b/apps/nesemu2/src/nes/cart/fds.c new file mode 100644 index 00000000..3cc3d54d --- /dev/null +++ b/apps/nesemu2/src/nes/cart/fds.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/log.h" +#include "misc/config.h" +#include "misc/paths.h" +#include "nes/cart/cart.h" +#include "mappers/mapperid.h" + +static u8 fdsident[] = "FDS\x1a"; +static u8 fdsident2[] = "\x01*NINTENDO-HVC*"; + +static int loadbios(cart_t *ret,char *filename) +{ + memfile_t *file; + int n = 0; + + //open bios file + if((file = memfile_open(filename,"rb")) == 0) { + log_printf("loadbios: error opening fds bios '%s'\n",filename); + return(1); + } + + //setup prg for bios + ret->prg.size = 0x2000; + ret->prg.mask = 0x1FFF; + ret->prg.data = (u8*)mem_alloc(0x2000); + + //read bios + if(memfile_read(ret->prg.data,1,0x2000,file) != 0x2000) { + log_printf("loadbios: error reading bios file '%s'\n",filename); + n = 1; + } + else + log_printf("loadbios: loaded bios file '%s'\n",filename); + + //close bios file handle + memfile_close(file); + + //return + return(n); +} + +int cart_load_fds(cart_t *ret,memfile_t *file) +{ + u8 header[16]; + u32 size; + char biosfile[1024]; + + //clear the string + memset(biosfile,0,1024); + + //parse the bios path + config_get_eval_string(biosfile,"path.bios"); + + //append the path seperator + str_appendchar(biosfile,PATH_SEPERATOR); + + //append the bios filename + strcat(biosfile,config_get_string("nes.fds.bios")); + + //try to load bios from the bios directory + if(loadbios(ret,biosfile) != 0) { + + //see if bios is in the current directory + if(loadbios(ret,config_get_string("nes.fds.bios")) != 0) { + return(1); + } + } + + //get length of file + size = memfile_size(file); + + //read the header + memfile_read(header,1,16,file); + + //check if this is raw fds disk + if(memcmp(header,fdsident2,15) == 0) { + + //check if the file is a valid size + if((size % 65500) != 0) { + log_printf("cart_load_fds: fds disk image size not multiple of 65500, aborting\n"); + return(1); + } + + //set number of disk sides +// ret->disksides = size / 65500; + + //skip back to the beginning + memfile_rewind(file); + } + + //check if this is 16-byte header fds disk + else if(memcmp(header,fdsident,4) == 0) { + + //set number of disk sides +// ret->disksides = header[4]; + size -= 16; + + } + + //set mapper id to fds mapper + ret->mapperid = B_FDS; + + //setup the disk data pointers + ret->disk.size = size; + ret->disk.data = (u8*)mem_alloc(size); + ret->diskoriginal.size = size; + ret->diskoriginal.data = (u8*)mem_alloc(size); + + //read disk data into pointer + memfile_read(ret->disk.data,1,size,file); + + //copy to original disk data pointer + memcpy(ret->diskoriginal.data,ret->disk.data,size); + + log_printf("cart_load_fds: loaded disk, %d sides (%d bytes)\n",ret->disk.size / 65500,size); + return(0); +} diff --git a/apps/nesemu2/src/nes/cart/fds.h b/apps/nesemu2/src/nes/cart/fds.h new file mode 100644 index 00000000..2ff6115b --- /dev/null +++ b/apps/nesemu2/src/nes/cart/fds.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cart_fds_h__ +#define __cart_fds_h__ + +#include "nes/cart/cart.h" + +int cart_load_fds(cart_t *ret,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/ines.c b/apps/nesemu2/src/nes/cart/ines.c new file mode 100644 index 00000000..c4ccac7b --- /dev/null +++ b/apps/nesemu2/src/nes/cart/ines.c @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/memutil.h" +#include "misc/log.h" +#include "nes/cart/cart.h" +#include "mappers/mappers.h" + +static int parse_ines_header(cart_t *ret,u8 *header) +{ + int mapper; + + //prg/chr sizes + ret->prg.size = header[4] * 0x4000; + ret->chr.size = header[5] * 0x2000; + + //kludge necessary for 4mb of prgrom + if(ret->prg.size == 0) + ret->prg.size = 256 * 0x4000; + + //mirroring info + ret->mirroring = header[6] & 1; + if(header[6] & 8) + ret->mirroring = MIRROR_4; + + //battery backed ram + if(header[6] & 2) { + ret->battery = 1; + } + + //mapper (lower bits) + mapper = (header[6] & 0xF0) >> 4; + + //check if the header is clean + if(memcmp(&header[8],"\0\0\0\0\0\0\0\0",8) != 0) + log_printf("parse_ines_header: dirty header! (%c%c%c%c%c%c%c%c%c)\n",header[7],header[8],header[9],header[10],header[11],header[12],header[13],header[14],header[15]); + else { + //mapper upper bits + mapper |= header[7] & 0xF0; + + //pc10 rom + if(header[7] & 2) + ret->pc10rom.size = 8192; + } + + //print some rom infos + log_printf("parse_ines_header: %dkb prg, %dkb chr, mapper %d, %s mirroring\n", + ret->prg.size / 1024,ret->chr.size / 1024,mapper, + (ret->mirroring == MIRROR_4) ? "four screen" : + ((ret->mirroring == 0) ? "horizontal" : "vertical")); + + //get internal board id + ret->mapperid = mapper_get_mapperid_ines(mapper); + + return(0); +} + +static int load_chunk(data_t *data,memfile_t *file) +{ + int len = 0; + + //if this chunk has a size, it exists (parse_ines_header determines this) + if(data->size) { + data->data = (u8*)mem_alloc(data->size); + len = (int)memfile_read(data->data,1,data->size,file); + } + return(len); +} + +int cart_load_ines(cart_t *ret,memfile_t *file) +{ + u8 header[16]; + u32 size; + + //get length of file + size = memfile_size(file); + + //read 16 byte header and parse its data + memfile_read(header,1,16,file); + parse_ines_header(ret,header); + + //load each chunk from the file + load_chunk(&ret->trainer,file); + load_chunk(&ret->prg,file); + load_chunk(&ret->chr,file); + load_chunk(&ret->pc10rom,file); + + //check for title + if((size - memfile_tell(file)) == 128) + memfile_read(ret->title,1,128,file); + + return(0); +} diff --git a/apps/nesemu2/src/nes/cart/ines.h b/apps/nesemu2/src/nes/cart/ines.h new file mode 100644 index 00000000..33ceae11 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/ines.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cart__ines_h__ +#define __cart__ines_h__ + +#include "nes/cart/cart.h" + +int cart_load_ines(cart_t *ret,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/ines20.c b/apps/nesemu2/src/nes/cart/ines20.c new file mode 100644 index 00000000..74b0917c --- /dev/null +++ b/apps/nesemu2/src/nes/cart/ines20.c @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/memutil.h" +#include "misc/log.h" +#include "nes/cart/cart.h" +#include "mappers/mappers.h" + +static int parse_ines20_header(cart_t *ret,u8 *header) +{ + int mapper,submapper; + + //prg/chr sizes + ret->prg.size = header[4] * 0x4000; + ret->chr.size = header[5] * 0x2000; + ret->prg.size += (((header[9] >> 0) & 0xF) << 8) * 0x4000; + ret->chr.size += (((header[9] >> 4) & 0xF) << 8) * 0x2000; + + //mirroring info + ret->mirroring = header[6] & 1; + if(header[6] & 8) + ret->mirroring = MIRROR_4; + + //battery backed ram + if(header[6] & 2) { + ret->battery = 1; + } + + //mapper (lower bits, upper bits, then extended bits) + mapper = (header[6] & 0xF0) >> 4; + mapper |= header[7] & 0xF0; + mapper |= (header[8] & 0xF) << 8; + + //submapper number + submapper = (header[8] & 0xF0) >> 4; + + //wram/sram sizes + if(header[10] & 0xF) + ret->wram.size += 0x80 << ((header[10] & 0xF) - 1); + + if(header[10] & 0xF0) + ret->wram.size += 0x80 << (((header[10] >> 4) & 0xF) - 1); + + //pc10 rom + if(header[7] & 2) + ret->pc10rom.size = 8192; + + //print some rom infos + log_printf("parse_ines20_header: %dkb prg, %dkb chr, %dkb sram/wram, mapper %d.%d, %s mirroring\n", + ret->prg.size / 1024,ret->chr.size / 1024,ret->wram.size / 1024,mapper,submapper, + (ret->mirroring == MIRROR_4) ? "four screen" : + ((ret->mirroring == 0) ? "horizontal" : "vertical")); + + //get internal board id + ret->mapperid = mapper_get_mapperid_ines20(mapper,submapper); + + return(0); +} + +static int load_chunk(data_t *data,memfile_t *file) +{ + int len = 0; + + //if this chunk has a size, it exists (parse_ines_header determines this) + if(data->size) { + data->data = (u8*)mem_alloc(data->size); + len = (int)memfile_read(data->data,1,data->size,file); + } + return(len); +} + +int cart_load_ines20(cart_t *ret,memfile_t *file) +{ + u8 header[16]; + u32 size; + + //get length of file + size = memfile_size(file); + + //read 16 byte header and parse its data + memfile_read(header,1,16,file); + parse_ines20_header(ret,header); + + //load each chunk from the file + load_chunk(&ret->trainer,file); + load_chunk(&ret->prg,file); + load_chunk(&ret->chr,file); + load_chunk(&ret->pc10rom,file); + + //check for title + if((size - memfile_tell(file)) == 128) + memfile_read(ret->title,1,128,file); + + return(0); +} diff --git a/apps/nesemu2/src/nes/cart/ines20.h b/apps/nesemu2/src/nes/cart/ines20.h new file mode 100644 index 00000000..7ea3a055 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/ines20.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cart__ines20_h__ +#define __cart__ines20_h__ + +#include "nes/cart/cart.h" + +int cart_load_ines20(cart_t *ret,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/nsf.c b/apps/nesemu2/src/nes/cart/nsf.c new file mode 100644 index 00000000..bdf1a9ef --- /dev/null +++ b/apps/nesemu2/src/nes/cart/nsf.c @@ -0,0 +1,162 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "nes/cart/cart.h" +#include "nes/cart/nsf.h" +#include "misc/log.h" +#include "misc/paths.h" +#include "misc/config.h" +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "mappers/mapperid.h" + +static int loadbios(cart_t *ret,char *filename) +{ + FILE *fp; + size_t size; + u8 header[10]; + + //open bios file + if((fp = fopen(filename,"rb")) == 0) { + log_printf("loadbios: error opening nsf bios '%s'\n",filename); + return(1); + } + + //get size of the nsf bios + fseek(fp,0,SEEK_END); + size = ftell(fp); + fseek(fp,0,SEEK_SET); + + //bios has a 10 byte header (actually part of the first bank) + // addr size description + // ---- ---- ----------- + // 00 07 ident string (NSFBIOS) + // 07 01 version hi + // 08 01 version lo + // 09 01 + fread(header,1,10,fp); + + if(memcmp(header,"NSFBIOS",7) != 0) { + log_printf("loadbios: nsf bios ident is bad\n"); + fclose(fp); + return(1); + } + + //seek back to the beginning + fseek(fp,0,SEEK_SET); + + //load bios into upper wram + cart_setwramsize(ret,8 + (size / 1024)); + + //read bios + if(fread(ret->wram.data + 8192,1,size,fp) != size) { + log_printf("loadbios: error reading nsf bios file '%s'\n",filename); + mem_free(ret->wram.data); + fclose(fp); + return(1); + } + + //close bios file handle + fclose(fp); + log_printf("loadbios: nsf bios loaded, %d bytes. version %d.%d\n",size,header[7],header[8]); + + //success + return(0); +} + +//makes the size a multiple of 4096 for padding +//static u32 padsize(u32 size) +//{ +// u32 ret = 0; +// +// while(ret < size) { +// ret += 0x1000; +// } +// return(ret); +//} + +int cart_load_nsf(cart_t *ret,memfile_t *file) +{ + int n = 0; + char biosfile[1024]; + u32 size; + u32 loadaddr; + u8 nobankswitch[8 + 8] = {0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7}; + + //clear the string + memset(biosfile,0,1024); + + //parse the bios path + config_get_eval_string(biosfile,"path.bios"); + + //append the path seperator + str_appendchar(biosfile,PATH_SEPERATOR); + + //append the bios filename + strcat(biosfile,"nsfbios.bin"); + + //try to load bios from the bios directory + if(loadbios(ret,biosfile) != 0) { + + //see if bios is in the current directory + if(loadbios(ret,"nsfbios.bin") != 0) { + return(1); + } + } + + //get length of file + size = memfile_size(file); + + //discount for the header + size -= 0x80; + + if(memfile_read(ret->data,1,0x80,file) != 0x80) { + log_printf("cart_load_nsf: error reading header from '%s'\n",file->filename); + n = 1; + } + else { + loadaddr = ret->data[8] | (ret->data[9] << 8); + + //if the nsf doesnt use bankswitching + if(memcmp((u8*)ret->data + 0x70,(u8*)nobankswitch,8) == 0) { + memcpy((u8*)ret->data + 0x70,(u8*)nobankswitch + 8,8); + ret->prg.size = (u32)size + (loadaddr & 0x7FFF); + ret->prg.data = (u8*)mem_alloc(ret->prg.size); + memset(ret->prg.data,0,ret->prg.size); + memfile_read(ret->prg.data + (loadaddr & 0x7FFF),1,size,file); + } + + //else the nsf is bankswitched + else { + ret->prg.size = (u32)size + (loadaddr & 0xFFF); + ret->prg.data = (u8*)mem_alloc(ret->prg.size); + memset(ret->prg.data,0,ret->prg.size); + memfile_read(ret->prg.data + (loadaddr & 0xFFF),1,size,file); + } + + //setup mapper + ret->mapperid = B_NSF; + log_printf("cart_load_nsf: nsf v%d loaded, %d bytes (padded to %d), %d songs.\n",ret->data[5],size,ret->prg.size,ret->data[6]); + log_printf("init $%04X, play $%04X\n",ret->data[0xA] | (ret->data[0xB] << 8),ret->data[0xC] | (ret->data[0xD] << 8)); + } + + return(n); +} diff --git a/apps/nesemu2/src/nes/cart/nsf.h b/apps/nesemu2/src/nes/cart/nsf.h new file mode 100644 index 00000000..4e1f2245 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/nsf.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cart_nsf_h__ +#define __cart_nsf_h__ + +typedef struct nsf_s { + char ident[5]; + u8 version; + u8 totalsongs; + u8 startsong; + u16 loadaddr; + u16 initaddr; + u16 playaddr; + char name[32]; + char artist[32]; + char copyright[32]; + u16 ntscspeed; + u8 bankswitch[8]; + u16 palspeed; + u8 ntscpal; + u8 chips; + u8 expansion[4]; +} nsf_t; + +int cart_load_nsf(cart_t *ret,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/patch/ips.c b/apps/nesemu2/src/nes/cart/patch/ips.c new file mode 100644 index 00000000..5aeff441 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/patch/ips.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "nes/cart/patch/patch.h" +#include "misc/memutil.h" +#include "misc/log.h" + +int patch_apply_ips(patch_t *p,memfile_t *file) +{ + u8 data[4]; + u32 size,offset; + char eof[4] = "EOF"; + + //skip over the header, it has been verified (hopefully) + memfile_seek(p,5,SEEK_SET); + + while(memfile_eof(p) == 0) { + memfile_read(data,1,3,p); + + //check for eof marker + if(memcmp(data,eof,3) == 0) + break; + + //get offset and seek there + offset = (data[0] << 16) | (data[1] << 8) | data[2]; + memfile_seek(file,offset,SEEK_SET); + + //get size of chunk + memfile_read(data,1,2,p); + size = (data[0] << 8) | data[1]; + + //read chunk data + if(size) { + memfile_copy(file,p,size); + } + + //read block fill + else { + memfile_read(data,1,2,p); + size = (data[0] << 8) | data[1]; + memfile_fill(file,memfile_getc(p),size); + } + } + log_printf("patch_apply_ips: patched ok.\n"); + return(0); +} diff --git a/apps/nesemu2/src/nes/cart/patch/ips.h b/apps/nesemu2/src/nes/cart/patch/ips.h new file mode 100644 index 00000000..3cb796db --- /dev/null +++ b/apps/nesemu2/src/nes/cart/patch/ips.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__cart__patch__ips_h__ +#define __nes__cart__patch__ips_h__ + +#include "nes/cart/patch/patch.h" + +int patch_apply_ips(patch_t *p,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/patch/patch.c b/apps/nesemu2/src/nes/cart/patch/patch.c new file mode 100644 index 00000000..2f611648 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/patch/patch.c @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#ifdef WIN32 + #include +#else + #include +#endif +#include "nes/cart/patch/patch.h" +#include "nes/cart/patch/ips.h" +#include "nes/cart/patch/ups.h" +#include "misc/memutil.h" +#include "misc/log.h" + +enum patchformat_e { + FORMAT_ERROR = -1, + FORMAT_UNKNOWN = 0, + FORMAT_IPS, + FORMAT_UPS, +}; + +static int determineformat(memfile_t *file) +{ + u8 ident_ips[] = "PATCH\x0"; + u8 ident_ups[] = "UPS1"; + u8 header[6]; + + //read first 6 bytes and seek to beginning + memfile_rewind(file); + memfile_read(header,1,6,file); + memfile_rewind(file); + + //check if ips format + if(memcmp(header,ident_ips,5) == 0) { + return(FORMAT_IPS); + } + + //check if ups + if(memcmp(header,ident_ups,4) == 0) + return(FORMAT_UPS); + + return(FORMAT_UNKNOWN); +} + +//load patch and return its data +patch_t *patch_load(const char *filename) +{ + return(memfile_open((char*)filename,"rb")); +} + +patch_t *patch_load_memory(u8 *data,u32 size) +{ + return(memfile_open_memory(data,size)); +} + +void patch_unload(patch_t *p) +{ + if(p) + memfile_close(p); +} + +int patch_apply(patch_t *p,memfile_t *file) +{ + int n; + + log_printf("patch_apply: applying patch...\n"); + + //find out what format the file is + switch(determineformat(p)) { + case FORMAT_IPS: n = patch_apply_ips(p,file); break; + case FORMAT_UPS: n = patch_apply_ups(p,file); break; + default: + log_printf("patch_apply: bad patch format.\n"); + n = 1; + break; + } + + return(n); +} diff --git a/apps/nesemu2/src/nes/cart/patch/patch.h b/apps/nesemu2/src/nes/cart/patch/patch.h new file mode 100644 index 00000000..e8b8da60 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/patch/patch.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__cart__patch_h__ +#define __nes__cart__patch_h__ + +#include "misc/memfile.h" + +typedef memfile_t patch_t; + +patch_t *patch_load(const char *filename); +patch_t *patch_load_memory(u8 *data,u32 size); +void patch_unload(patch_t *p); +int patch_apply(patch_t *p,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/patch/ups.c b/apps/nesemu2/src/nes/cart/patch/ups.c new file mode 100644 index 00000000..40b8e244 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/patch/ups.c @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "nes/cart/patch/patch.h" +#include "misc/log.h" + +int patch_apply_ups(patch_t *ret,memfile_t *file) +{ + log_printf("patch_apply_ups: ups patches not supported.\n"); + return(1); +} diff --git a/apps/nesemu2/src/nes/cart/patch/ups.h b/apps/nesemu2/src/nes/cart/patch/ups.h new file mode 100644 index 00000000..030eeb69 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/patch/ups.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__cart__patch__ups_h__ +#define __nes__cart__patch__ups_h__ + +#include "nes/cart/patch/patch.h" + +int patch_apply_ups(patch_t *ret,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cart/unif.c b/apps/nesemu2/src/nes/cart/unif.c new file mode 100644 index 00000000..c2006979 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/unif.c @@ -0,0 +1,204 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "misc/memutil.h" +#include "misc/log.h" +#include "misc/crc32.h" +#include "nes/cart/cart.h" +#include "nes/cart/unif.h" +#include "nes/state/block.h" +#include "mappers/mappers.h" + +#define BLOCKFUNCSTART() static blockfunc_t blockfuncs[] = { +#define BLOCKFUNCEND() {0,0}}; +#define BLOCKFUNC(name) {name,block_##name}, +#define BLOCKFUNCDECL(name) static void block_##name(cart_t *ret,block_t *block) + +#define block_rom(n,var,name) \ + BLOCKFUNCDECL(name##n) {\ + var[0x##n].size = block->size;\ + var[0x##n].data = (u8*)mem_alloc(block->size);\ + memcpy(var[0x##n].data,block->data,block->size);\ + } +#define block_crc(n,var,name) \ + BLOCKFUNCDECL(name##n) {\ + memcpy(&var[0x##n].crc32,block->data,4);\ + var[0x##n].flags |= 1;\ + } + +#define block_prg(n) block_rom(n,prg,ID_PRG) +#define block_chr(n) block_rom(n,chr,ID_CHR) +#define block_pck(n) block_crc(n,prg,ID_PCK) +#define block_cck(n) block_crc(n,chr,ID_CCK) + +typedef struct blockfunc_s { + u32 type; + void (*func)(cart_t*,block_t*); +} blockfunc_t; + +typedef struct romdata_s { + u32 size; + u32 crc32; + u8 *data; + u8 flags; //currently used only to indicate if crc was present in rom +} romdata_t; + +//static const char ident[] = "UNIF"; +static romdata_t prg[16],chr[16]; +static char board[128]; + +BLOCKFUNCDECL(ID_MAPR) { + char *data = (char *)block->data; + ret->mapperid = mapper_get_mapperid_unif(data); strcpy(board,data); +} +BLOCKFUNCDECL(ID_NAME) { strncpy(ret->title,(char *)block->data,CART_TITLE_LEN); } +BLOCKFUNCDECL(ID_MIRR) { ret->mirroring = block->data[0]; } +BLOCKFUNCDECL(ID_BATR) { ret->battery = block->data[0]; } +BLOCKFUNCDECL(ID_TVCI) { ret->tvmode = block->data[0]; } + +block_prg(0); block_prg(1); block_prg(2); block_prg(3); block_prg(4); block_prg(5); block_prg(6); block_prg(7); +block_prg(8); block_prg(9); block_prg(A); block_prg(B); block_prg(C); block_prg(D); block_prg(E); block_prg(F); +block_chr(0); block_chr(1); block_chr(2); block_chr(3); block_chr(4); block_chr(5); block_chr(6); block_chr(7); +block_chr(8); block_chr(9); block_chr(A); block_chr(B); block_chr(C); block_chr(D); block_chr(E); block_chr(F); +block_pck(0); block_pck(1); block_pck(2); block_pck(3); block_pck(4); block_pck(5); block_pck(6); block_pck(7); +block_pck(8); block_pck(9); block_pck(A); block_pck(B); block_pck(C); block_pck(D); block_pck(E); block_pck(F); +block_cck(0); block_cck(1); block_cck(2); block_cck(3); block_cck(4); block_cck(5); block_cck(6); block_cck(7); +block_cck(8); block_cck(9); block_cck(A); block_cck(B); block_cck(C); block_cck(D); block_cck(E); block_cck(F); + +BLOCKFUNCSTART() + BLOCKFUNC(ID_MAPR) + BLOCKFUNC(ID_NAME) + BLOCKFUNC(ID_MIRR) + BLOCKFUNC(ID_BATR) + BLOCKFUNC(ID_TVCI) + BLOCKFUNC(ID_PRG0) BLOCKFUNC(ID_PRG1) BLOCKFUNC(ID_PRG2) BLOCKFUNC(ID_PRG3) + BLOCKFUNC(ID_PRG4) BLOCKFUNC(ID_PRG5) BLOCKFUNC(ID_PRG6) BLOCKFUNC(ID_PRG7) + BLOCKFUNC(ID_PRG8) BLOCKFUNC(ID_PRG9) BLOCKFUNC(ID_PRGA) BLOCKFUNC(ID_PRGB) + BLOCKFUNC(ID_PRGC) BLOCKFUNC(ID_PRGD) BLOCKFUNC(ID_PRGE) BLOCKFUNC(ID_PRGF) + BLOCKFUNC(ID_CHR0) BLOCKFUNC(ID_CHR1) BLOCKFUNC(ID_CHR2) BLOCKFUNC(ID_CHR3) + BLOCKFUNC(ID_CHR4) BLOCKFUNC(ID_CHR5) BLOCKFUNC(ID_CHR6) BLOCKFUNC(ID_CHR7) + BLOCKFUNC(ID_CHR8) BLOCKFUNC(ID_CHR9) BLOCKFUNC(ID_CHRA) BLOCKFUNC(ID_CHRB) + BLOCKFUNC(ID_CHRC) BLOCKFUNC(ID_CHRD) BLOCKFUNC(ID_CHRE) BLOCKFUNC(ID_CHRF) + BLOCKFUNC(ID_PCK0) BLOCKFUNC(ID_PCK1) BLOCKFUNC(ID_PCK2) BLOCKFUNC(ID_PCK3) + BLOCKFUNC(ID_PCK4) BLOCKFUNC(ID_PCK5) BLOCKFUNC(ID_PCK6) BLOCKFUNC(ID_PCK7) + BLOCKFUNC(ID_PCK8) BLOCKFUNC(ID_PCK9) BLOCKFUNC(ID_PCKA) BLOCKFUNC(ID_PCKB) + BLOCKFUNC(ID_PCKC) BLOCKFUNC(ID_PCKD) BLOCKFUNC(ID_PCKE) BLOCKFUNC(ID_PCKF) + BLOCKFUNC(ID_CCK0) BLOCKFUNC(ID_CCK1) BLOCKFUNC(ID_CCK2) BLOCKFUNC(ID_CCK3) + BLOCKFUNC(ID_CCK4) BLOCKFUNC(ID_CCK5) BLOCKFUNC(ID_CCK6) BLOCKFUNC(ID_CCK7) + BLOCKFUNC(ID_CCK8) BLOCKFUNC(ID_CCK9) BLOCKFUNC(ID_CCKA) BLOCKFUNC(ID_CCKB) + BLOCKFUNC(ID_CCKC) BLOCKFUNC(ID_CCKD) BLOCKFUNC(ID_CCKE) BLOCKFUNC(ID_CCKF) +BLOCKFUNCEND() + +static int load_unif_block(cart_t *ret,memfile_t *file) +{ + block_t *block = block_load(file); + int i; + + if(block == 0) { + log_printf("load_unif_block: error loading block\n"); + return(-1); + } + for(i=0;blockfuncs[i].type;i++) { + if(blockfuncs[i].type == block->type) { + blockfuncs[i].func(ret,block); + break; + } + } + i = block->size + 8; + block_destroy(block); + return(i); +} + +static void glue_data(data_t *rom,romdata_t *roms) +{ + u32 size,pos; + int i; + + for(size=0,i=0;i<16;i++) { + size += roms[i].size; + } + rom->size = size; + rom->data = (u8*)mem_alloc(size); + for(pos=0,i=0;i<16;i++) { + if(roms[i].size) { + memcpy(rom->data + pos,roms[i].data,roms[i].size); + mem_free(roms[i].data); + pos += roms[i].size; + } + } +} + +int cart_load_unif(cart_t *ret,memfile_t *file) +{ + u8 header[32]; + u32 pos,size,tmp; + + //get file size + size = memfile_size(file); + + //read 32 byte header + memfile_read(header,1,32,file); + pos = 32; + + //clear the prg/chr rom data + memset(prg,0,sizeof(romdata_t)*16); + memset(chr,0,sizeof(romdata_t)*16); + + //clear the board name + memset(board,0,128); + + //load the unif blocks + while(pos < size) { + if((tmp = load_unif_block(ret,file)) == -1) + break; + pos += tmp; + } + + //check the crc32's (if blocks are present) + for(size=0,pos=0;pos<16;pos++) { + if(prg[pos].size && (prg[pos].flags & 1)) { + if(prg[pos].crc32 != crc32(prg[pos].data,prg[pos].size)) { + size |= 1 << pos; + } + } + if(chr[pos].size && (chr[pos].flags & 1)) { + if(prg[pos].crc32 != crc32(prg[pos].data,prg[pos].size)) { + size |= 1 << (pos + 16); + } + } + } + + //glue together the prg/chr data + glue_data(&ret->prg,prg); + glue_data(&ret->chr,chr); + + //show some information + log_printf("cart_load_unif: loaded ok. %dkb prg, %dkb chr, board '%s'\n", + ret->prg.size / 1024,ret->chr.size / 1024,board); + + //report crc errors + if(size) { + log_printf("cart_load_unif: crc32 stored does not match the data\n"); + } + + return(0); +} diff --git a/apps/nesemu2/src/nes/cart/unif.h b/apps/nesemu2/src/nes/cart/unif.h new file mode 100644 index 00000000..97c162d1 --- /dev/null +++ b/apps/nesemu2/src/nes/cart/unif.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __cart_unif_h__ +#define __cart_unif_h__ + +#include "nes/cart/cart.h" +#include "nes/state/block.h" + +#define ID_MAPR MAKEID('M','A','P','R') +#define ID_NAME MAKEID('N','A','M','E') +#define ID_MIRR MAKEID('M','I','R','R') +#define ID_WRTR MAKEID('W','R','T','R') +#define ID_BATR MAKEID('B','A','T','R') +#define ID_TVCI MAKEID('T','V','C','I') +#define ID_PRG0 MAKEID('P','R','G','0') +#define ID_PRG1 MAKEID('P','R','G','1') +#define ID_PRG2 MAKEID('P','R','G','2') +#define ID_PRG3 MAKEID('P','R','G','3') +#define ID_PRG4 MAKEID('P','R','G','4') +#define ID_PRG5 MAKEID('P','R','G','5') +#define ID_PRG6 MAKEID('P','R','G','6') +#define ID_PRG7 MAKEID('P','R','G','7') +#define ID_PRG8 MAKEID('P','R','G','8') +#define ID_PRG9 MAKEID('P','R','G','9') +#define ID_PRGA MAKEID('P','R','G','A') +#define ID_PRGB MAKEID('P','R','G','B') +#define ID_PRGC MAKEID('P','R','G','C') +#define ID_PRGD MAKEID('P','R','G','D') +#define ID_PRGE MAKEID('P','R','G','E') +#define ID_PRGF MAKEID('P','R','G','F') +#define ID_CHR0 MAKEID('C','H','R','0') +#define ID_CHR1 MAKEID('C','H','R','1') +#define ID_CHR2 MAKEID('C','H','R','2') +#define ID_CHR3 MAKEID('C','H','R','3') +#define ID_CHR4 MAKEID('C','H','R','4') +#define ID_CHR5 MAKEID('C','H','R','5') +#define ID_CHR6 MAKEID('C','H','R','6') +#define ID_CHR7 MAKEID('C','H','R','7') +#define ID_CHR8 MAKEID('C','H','R','8') +#define ID_CHR9 MAKEID('C','H','R','9') +#define ID_CHRA MAKEID('C','H','R','A') +#define ID_CHRB MAKEID('C','H','R','B') +#define ID_CHRC MAKEID('C','H','R','C') +#define ID_CHRD MAKEID('C','H','R','D') +#define ID_CHRE MAKEID('C','H','R','E') +#define ID_CHRF MAKEID('C','H','R','F') +#define ID_PCK0 MAKEID('P','C','K','0') +#define ID_PCK1 MAKEID('P','C','K','1') +#define ID_PCK2 MAKEID('P','C','K','2') +#define ID_PCK3 MAKEID('P','C','K','3') +#define ID_PCK4 MAKEID('P','C','K','4') +#define ID_PCK5 MAKEID('P','C','K','5') +#define ID_PCK6 MAKEID('P','C','K','6') +#define ID_PCK7 MAKEID('P','C','K','7') +#define ID_PCK8 MAKEID('P','C','K','8') +#define ID_PCK9 MAKEID('P','C','K','9') +#define ID_PCKA MAKEID('P','C','K','A') +#define ID_PCKB MAKEID('P','C','K','B') +#define ID_PCKC MAKEID('P','C','K','C') +#define ID_PCKD MAKEID('P','C','K','D') +#define ID_PCKE MAKEID('P','C','K','E') +#define ID_PCKF MAKEID('P','C','K','F') +#define ID_CCK0 MAKEID('C','C','K','0') +#define ID_CCK1 MAKEID('C','C','K','1') +#define ID_CCK2 MAKEID('C','C','K','2') +#define ID_CCK3 MAKEID('C','C','K','3') +#define ID_CCK4 MAKEID('C','C','K','4') +#define ID_CCK5 MAKEID('C','C','K','5') +#define ID_CCK6 MAKEID('C','C','K','6') +#define ID_CCK7 MAKEID('C','C','K','7') +#define ID_CCK8 MAKEID('C','C','K','8') +#define ID_CCK9 MAKEID('C','C','K','9') +#define ID_CCKA MAKEID('C','C','K','A') +#define ID_CCKB MAKEID('C','C','K','B') +#define ID_CCKC MAKEID('C','C','K','C') +#define ID_CCKD MAKEID('C','C','K','D') +#define ID_CCKE MAKEID('C','C','K','E') +#define ID_CCKF MAKEID('C','C','K','F') + +int cart_load_unif(cart_t *ret,memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/nes/cpu/addrmodes.hh b/apps/nesemu2/src/nes/cpu/addrmodes.hh new file mode 100644 index 00000000..bd709665 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/addrmodes.hh @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//implied addressing +static INLINE void AM_IMP() +{ + TMPREG = memread(PC); +} + +//immediate addressing +static INLINE void AM_IMM() +{ + EFFADDR = PC++; +} + +//absolute addressing +static INLINE void AM_ABS() +{ + EFFADDR = memread(PC++); + EFFADDR |= memread(PC++) << 8; +} + +/* +Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, LAX, LAE, SHS, NOP) +# address R/W description +--- --------- --- ------------------------------------------ +1 PC R fetch opcode, increment PC +2 PC R fetch low byte of address, increment PC +3 PC R fetch high byte of address, add index register to low address byte, increment PC +4 address+I* R read from effective address, fix the high byte of effective address +5+ address+I R re-read from effective address +Notes: I denotes either index register (X or Y). +* The high byte of the effective address may be invalid at this time, i.e. it may be smaller by $100. ++ This cycle will be executed only if the effective address was invalid during cycle #4, i.e. page boundary was crossed. +*/ +//absolute x addressing (for reading only) +static INLINE void AM_AXR() +{ + EFFADDR = memread(PC++); + EFFADDR |= memread(PC++) << 8; + tmpi = (EFFADDR & 0xFF) + X; + if(tmpi >= 0x100) { + memread((EFFADDR & 0xFF00) | (u8)tmpi); + } + EFFADDR += X; +} + +//absolute x addressing +static INLINE void AM_ABX() +{ + EFFADDR = memread(PC++); + EFFADDR |= memread(PC++) << 8; + tmpi = (EFFADDR & 0xFF) + X; + memread((EFFADDR & 0xFF00) | (u8)tmpi); + EFFADDR += X; +} + +//absolute y addressing (for reading only) +static INLINE void AM_AYR() +{ + EFFADDR = memread(PC++); + EFFADDR |= memread(PC++) << 8; + tmpi = (EFFADDR & 0xFF) + Y; + if(tmpi >= 0x100) { + memread((EFFADDR & 0xFF00) | (u8)tmpi); + } + EFFADDR += Y; +} + +//absolute y addressing +static INLINE void AM_ABY() +{ + EFFADDR = memread(PC++); + EFFADDR |= memread(PC++) << 8; + tmpi = (EFFADDR & 0xFF) + Y; + memread((EFFADDR & 0xFF00) | (u8)tmpi); + EFFADDR += Y; +} + +//zero-page addressing +static INLINE void AM_ZPG() +{ + EFFADDR = memread(PC++); +} + +//zero-page x addressing +static INLINE void AM_ZPX() +{ + EFFADDR = memread(PC++); + memread(EFFADDR); + EFFADDR = (EFFADDR + X) & 0xFF; +} + +//zero-page y addressing +static INLINE void AM_ZPY() +{ + EFFADDR = memread(PC++); + memread(EFFADDR); + EFFADDR = (EFFADDR + Y) & 0xFF; +} + +/* +Write instructions (STA, SAX) +# address R/W description +--- ----------- --- ------------------------------------------ +1 PC R fetch opcode, increment PC +2 PC R fetch pointer address, increment PC +3 pointer R read from the address, add X to it +4 pointer+X R fetch effective address low +5 pointer+X+1 R fetch effective address high +6 address W write to effective address +Note: The effective address is always fetched from zero page, i.e. the zero page boundary crossing is not handled. +*/ +//indirect x +static INLINE void AM_INX() +{ + TMPREG = memread(PC++); + memread(TMPREG); + TMPREG += X; + EFFADDR = memread(TMPREG++); + EFFADDR |= memread(TMPREG) << 8; +} + +/* +Read instructions (LDA, EOR, AND, ORA, ADC, SBC, CMP) +# address R/W description +--- ----------- --- ------------------------------------------ +1 PC R fetch opcode, increment PC +2 PC R fetch pointer address, increment PC +3 pointer R fetch effective address low +4 pointer+1 R fetch effective address high, add Y to low byte of effective address +5 address+Y* R read from effective address, fix high byte of effective address +6+ address+Y R read from effective address +Notes: The effective address is always fetched from zero page, i.e. the zero page boundary crossing is not handled. +* The high byte of the effective address may be invalid at this time, i.e. it may be smaller by $100. ++ This cycle will be executed only if the effective address was invalid during cycle #5, i.e. page boundary was crossed. +*/ +//indirect y (for reading only) +static INLINE void AM_IYR() +{ + TMPREG = memread(PC++); + EFFADDR = memread(TMPREG++); + EFFADDR |= memread(TMPREG) << 8; + tmpi = (EFFADDR & 0xFF) + Y; + if(tmpi >= 0x100) { + memread((EFFADDR & 0xFF00) | (u8)tmpi); + } + EFFADDR += Y; +} + +/* +Write instructions (STA, SHA) +# address R/W description +--- ----------- --- ------------------------------------------ +1 PC R fetch opcode, increment PC +2 PC R fetch pointer address, increment PC +3 pointer R fetch effective address low +4 pointer+1 R fetch effective address high, add Y to low byte of effective address +5 address+Y* R read from effective address, fix high byte of effective address +6 address+Y W write to effective address +Notes: The effective address is always fetched from zero page, i.e. the zero page boundary crossing is not handled. +* The high byte of the effective address may be invalid at this time, i.e. it may be smaller by $100. +*/ +//indirect y +static INLINE void AM_INY() +{ + TMPREG = memread(PC++); + EFFADDR = memread(TMPREG++); + EFFADDR |= memread(TMPREG) << 8; + tmpi = (EFFADDR & 0xFF) + Y; + memread((EFFADDR & 0xFF00) | (u8)tmpi); + EFFADDR += Y; +} + +//indirect addressing +static INLINE void AM_IND() +{ + TMPADDR = memread(PC++); + TMPADDR |= memread(PC++) << 8; + EFFADDR = memread(TMPADDR); + TMPADDR = (TMPADDR & 0xFF00) | ((TMPADDR + 1) & 0xFF); + EFFADDR |= memread(TMPADDR) << 8; +} + +//relative addressing +static INLINE void AM_REL() +{ + TMPREG = memread(PC++); +} + +//unknown addressing (for bad opcodes, and jsr) +static INLINE void AM_UNK() +{ +} diff --git a/apps/nesemu2/src/nes/cpu/cpu.c b/apps/nesemu2/src/nes/cpu/cpu.c new file mode 100644 index 00000000..c53af8a8 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/cpu.c @@ -0,0 +1,274 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "nes/nes.h" +#include "nes/state/state.h" +#include "misc/log.h" +#include "misc/config.h" + +//show disassembly as we execute +#define SHOW_DISASM + +//defines to make easier reading +#define PC nes->cpu.pc +#define A nes->cpu.a +#define X nes->cpu.x +#define Y nes->cpu.y +#define P nes->cpu.p +#define SP nes->cpu.sp +#define TMPADDR nes->cpu.tmpaddr +#define EFFADDR nes->cpu.effaddr +#define FLAG_C nes->cpu.flags.c +#define FLAG_Z nes->cpu.flags.z +#define FLAG_I nes->cpu.flags.i +#define FLAG_D nes->cpu.flags.d +#define FLAG_V nes->cpu.flags.v +#define FLAG_N nes->cpu.flags.n +#define OPCODE nes->cpu.opcode +#define OPADDR nes->cpu.opaddr +#define TMPREG nes->cpu.tmpreg +#define CYCLES nes->cpu.cycles +#define NMISTATE nes->cpu.nmistate +#define IRQSTATE nes->cpu.irqstate +#define PREV_NMISTATE nes->cpu.prev_nmistate +#define PREV_IRQSTATE nes->cpu.prev_irqstate + +#define BADOPCODE nes->cpu.badopcode + +//cpu memory read/write functions +readfunc_t cpu_read; +writefunc_t cpu_write; + +//temp variable for opcode functions +static u8 tmp8; +static int tmpi; + +//pal hack +static int palticks; + +//for stopping execution when invalid opcodes are encountered (kludge) +extern int running; + +//include helper functions +#include "helper.hh" + +//include addressing mode functions +#include "addrmodes.hh" + +//include opcode functions +#include "opcodes/misc.hh" +#include "opcodes/branch.hh" +#include "opcodes/loadstore.hh" +#include "opcodes/transfer.hh" +#include "opcodes/compare.hh" +#include "opcodes/incdec.hh" +#include "opcodes/alu.hh" +#include "opcodes/flag.hh" +#include "opcodes/stack.hh" +#ifdef CPU_UNDOC + #include "opcodes/undocumented.hh" +#endif + +//include functions to execute opcodes +#include "execute.hh" +//#include "newcpu.i" + +int cpu_init() +{ + palticks = 0; + cpu_disassemble_init(); + state_register(B_CPU,cpu_state); + return(0); +} + +void cpu_kill() +{ +} + +void cpu_reset(int hard) +{ + int i; + + for(i=0;i<8;i+=2) { + nes->cpu.readpages[i + 0] = nes->cpu.writepages[i + 0] = (u8*)nes->cpu.ram; + nes->cpu.readpages[i + 1] = nes->cpu.writepages[i + 1] = (u8*)nes->cpu.ram + 0x400; + } + + nes->cpu.pcmcycles = 0; + nes->cpu.badopcode = 0; + if(hard) { + A = X = Y = 0; + SP = 0xFD; + P = 0x24; + expand_flags(); + memset(nes->cpu.ram,0,0x800); + } + else { + FLAG_I = 1; + SP -= 3; + } + PC = memread(0xFFFC); + PC |= memread(0xFFFD) << 8; + log_printf("vectors:\n"); + log_printf(" nmi: $%04X\n",cpu_read(0xFFFA) | (cpu_read(0xFFFB) << 8)); + log_printf(" irq: $%04X\n",cpu_read(0xFFFE) | (cpu_read(0xFFFF) << 8)); + log_printf(" reset: $%04X\n",cpu_read(0xFFFC) | (cpu_read(0xFFFD) << 8)); +} + +u64 cpu_getcycles() +{ + return(nes->cpu.cycles); +} + +void cpu_set_nmi() +{ + NMISTATE = 1; +} + +void cpu_clear_nmi() +{ + NMISTATE = 0; +} + +void cpu_set_irq(u8 state) +{ + IRQSTATE |= state; +} + +void cpu_clear_irq(u8 state) +{ + IRQSTATE &= ~state; +} + +static INLINE void ppu_tick() +{ + if(nes->region->id & REGION_PAL) { + palticks++; + if(palticks == 5) { + palticks = 0; + ppu_step(); + } + } + ppu_step(); + ppu_step(); + ppu_step(); +} + +void cpu_tick() +{ + //acknowledge interrupts + PREV_NMISTATE = NMISTATE; + PREV_IRQSTATE = (FLAG_I == 0) ? IRQSTATE : 0; + + //increment cycle counter for every memory access + CYCLES++; + + //catch up the ppu + ppu_tick(); + + //catch up the apu + apu_step(); + + //call the mapper callback + nes->mapper->cpucycle(); +} + +static u8 read_cpu_memory(u32 addr) +{ + u32 page = addr >> 10; + + //see if this page is handled by a memory pointer + if(nes->cpu.readpages[page] != 0) { + return(nes->cpu.readpages[page][addr & 0x3FF]); + } + + //see if this page is handled by a read function + if(nes->cpu.readfuncs[page] != 0) { + return(nes->cpu.readfuncs[page](addr)); + } + + //not handled + if(config_get_bool("nes.log_unhandled_io")) + log_printf("cpu_read: unhandled read at $%04X (page %d)\n",addr,page); + return(0); +} + +static void write_cpu_memory(u32 addr,u8 data) +{ + u32 page = addr >> 10; + + //see if this page is handled by a memory pointer + if(nes->cpu.writepages[page] != 0) { + nes->cpu.writepages[page][addr & 0x3FF] = data; + return; + } + + //see if this page is handled by a read function + if(nes->cpu.writefuncs[page] != 0) { + nes->cpu.writefuncs[page](addr,data); + return; + } + + //not handled + if(config_get_bool("nes.log_unhandled_io")) + log_printf("cpu_write: unhandled write at $%04X = $%02X\n",addr,data); +} + +u8 cpu_getflags() +{ + compact_flags(); + return(P); +} + +readfunc_t cpu_getreadfunc() +{ + return(cpu_read); +} + +writefunc_t cpu_getwritefunc() +{ + return(cpu_write); +} + +void cpu_setreadfunc(readfunc_t readfunc) +{ + cpu_read = (readfunc == 0) ? read_cpu_memory : readfunc; +} + +void cpu_setwritefunc(writefunc_t writefunc) +{ + cpu_write = (writefunc == 0) ? write_cpu_memory : writefunc; +} + +void cpu_state(int mode,u8 *data) +{ + STATE_U8(A); + STATE_U8(X); + STATE_U8(Y); + STATE_U8(SP); + STATE_U8(P); + STATE_U16(PC); + STATE_U64(CYCLES); + STATE_U8(PREV_NMISTATE); + STATE_U8(PREV_IRQSTATE); + STATE_ARRAY_U8(nes->cpu.ram,0x800); +} diff --git a/apps/nesemu2/src/nes/cpu/cpu.h b/apps/nesemu2/src/nes/cpu/cpu.h new file mode 100644 index 00000000..d00de95d --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/cpu.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__cpu_h__ +#define __nes__cpu_h__ + +#include "types.h" + +typedef struct cpu_s { + + //program counter + u16 pc; + + //registers + u8 a,x,y,sp,p; + + //seperated flags register + struct { + u8 c,z,i,d,b,v,n; + } flags; + + //interrupt flags + u8 nmistate,prev_nmistate; + u8 irqstate,prev_irqstate; + + //effective/temporary address + u16 effaddr; + u16 tmpaddr; + + //opcode info + u8 opcode; + u16 opaddr; + + //for byte read after opcode + u8 tmpreg; + + //cycle counter + u64 cycles; + + //internal memory + u8 ram[0x800]; + + //memory access pointers + u8 *readpages[64]; + u8 *writepages[64]; + + //memory access functions + readfunc_t readfuncs[64]; + writefunc_t writefuncs[64]; + + //kludge for apu pcm cycle stealing + u32 pcmcycles; + + //bad opcode counter + int badopcode; + +} cpu_t; + +extern readfunc_t cpu_read; +extern writefunc_t cpu_write; + +int cpu_init(); +void cpu_kill(); +void cpu_reset(int hard); +u64 cpu_getcycles(); +void cpu_set_nmi(); +void cpu_clear_nmi(); +void cpu_set_irq(u8 state); +void cpu_clear_irq(u8 state); +void cpu_tick(); +u32 cpu_execute(u32 cycles); +void cpu_execute_frame(); +u16 cpu_disassemble(char *buffer, u16 opcodepos); +void cpu_disassemble_init(); +readfunc_t cpu_getreadfunc(); +writefunc_t cpu_getwritefunc(); +void cpu_setreadfunc(readfunc_t readfunc); +void cpu_setwritefunc(writefunc_t writefunc); +void cpu_state(int mode, u8 *data); + +#endif diff --git a/apps/nesemu2/src/nes/cpu/disassemble.c b/apps/nesemu2/src/nes/cpu/disassemble.c new file mode 100644 index 00000000..278da122 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/disassemble.c @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "nes/cpu/cpu.h" + +enum addrmodes { + er=0,no,ab,ax,ay,ac,im,ix,iy,in,re,zp,zx,zy +}; + +#ifdef CPU_UNDOC +static char opcodes[256][4] = { +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ + "BRK","ORA","???","SLO","NOP","ORA","ASL","SLO","PHP","ORA","ASL","AAC","NOP","ORA","ASL","SLO", /*00-0F*/ + "BPL","ORA","???","SLO","NOP","ORA","ASL","SLO","CLC","ORA","NOP","SLO","NOP","ORA","ASL","SLO", /*10-1F*/ + "JSR","AND","???","RLA","BIT","AND","ROL","RLA","PLP","AND","ROL","AAC","BIT","AND","ROL","RLA", /*20-2F*/ + "BMI","AND","???","RLA","NOP","AND","ROL","RLA","SEC","AND","NOP","RLA","NOP","AND","ROL","RLA", /*30-3F*/ + "RTI","EOR","???","SRE","NOP","EOR","LSR","SRE","PHA","EOR","LSR","ASR","JMP","EOR","LSR","SRE", /*40-4F*/ + "BVC","EOR","???","SRE","NOP","EOR","LSR","SRE","CLI","EOR","NOP","SRE","NOP","EOR","LSR","SRE", /*50-5F*/ + "RTS","ADC","???","RRA","NOP","ADC","ROR","RRA","PLA","ADC","ROR","???","JMP","ADC","ROR","RRA", /*60-6F*/ + "BVS","ADC","???","RRA","NOP","ADC","ROR","RRA","SEI","ADC","NOP","RRA","NOP","ADC","ROR","RRA", /*70-7F*/ + "NOP","STA","NOP","SAX","STY","STA","STX","SAX","DEY","NOP","TXA","???","STY","STA","STX","SAX", /*80-8F*/ + "BCC","STA","???","???","STY","STA","STX","SAX","TYA","STA","TXS","XAS","SYA","STA","SXA","???", /*90-9F*/ + "LDY","LDA","LDX","LAX","LDY","LDA","LDX","LAX","TAY","LDA","TAX","ATX","LDY","LDA","LDX","LAX", /*A0-AF*/ + "BCS","LDA","???","LAX","LDY","LDA","LDX","LAX","CLV","LDA","TSX","???","LDY","LDA","LDX","LAX", /*B0-BF*/ + "CPY","CMP","NOP","DCP","CPY","CMP","DEC","DCP","INY","CMP","DEX","AXS","CPY","CMP","DEC","DCP", /*C0-CF*/ + "BNE","CMP","???","DCP","NOP","CMP","DEC","DCP","CLD","CMP","NOP","DCP","NOP","CMP","DEC","DCP", /*D0-DF*/ + "CPX","SBC","NOP","ISB","CPX","SBC","INC","ISB","INX","SBC","NOP","SBC","CPX","SBC","INC","ISB", /*E0-EF*/ + "BEQ","SBC","???","ISB","NOP","SBC","INC","ISB","SED","SBC","NOP","ISB","NOP","SBC","INC","ISB" /*F0-FF*/ +}; +static u8 addrtable[256] = { +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ + im,ix,er,ix,zp,zp,zp,zp,no,im,ac,im,ab,ab,ab,ab, /*00-0f*/ + re,iy,er,iy,zx,zx,zx,zx,no,ay,im,ay,ax,ax,ax,ax, /*10-1f*/ + ab,ix,er,ix,zp,zp,zp,zp,no,im,ac,im,ab,ab,ab,ab, /*20-2f*/ + re,iy,er,iy,zx,zx,zx,zx,no,ay,im,ay,ax,ax,ax,ax, /*30-3f*/ + no,ix,er,ix,zp,zp,zp,zp,no,im,ac,im,ab,ab,ab,ab, /*40-4f*/ + re,iy,er,iy,zx,zx,zx,zx,no,ay,im,ay,ax,ax,ax,ax, /*50-5f*/ + no,ix,er,ix,zp,zp,zp,zp,no,im,ac,er,in,ab,ab,ab, /*60-6f*/ + re,iy,er,iy,zx,zx,zx,zx,no,ay,im,ay,ax,ax,ax,ax, /*70-7f*/ + im,ix,im,ix,zp,zp,zp,zp,no,im,no,er,ab,ab,ab,ab, /*80-8f*/ + re,iy,er,er,zx,zx,zy,zy,no,ay,no,ay,ax,ax,ay,er, /*90-9f*/ + im,ix,im,ix,zp,zp,zp,zp,no,im,no,im,ab,ab,ab,ab, /*a0-af*/ + re,iy,er,iy,zx,zx,zy,zy,no,ay,no,er,ax,ax,ay,ay, /*b0-bf*/ + im,ix,im,ix,zp,zp,zp,zp,no,im,no,im,ab,ab,ab,ab, /*c0-cf*/ + re,iy,iy,er,zx,zx,zx,zx,no,ay,im,ay,ax,ax,ax,ax, /*d0-df*/ + im,ix,im,ix,zp,zp,zp,zp,no,im,no,im,ab,ab,ab,ab, /*e0-ef*/ + re,iy,er,iy,zx,zx,zx,zx,no,ay,im,ay,ax,ax,ax,ax /*f0-ff*/ +}; +#else +static char opcodes[256][4] = { + "BRK","ORA","???","???","???","ORA","ASL","???","PHP","ORA","ASL","???","???","ORA","ASL","???", /*00-0F*/ + "BPL","ORA","???","???","???","ORA","ASL","???","CLC","ORA","???","???","???","ORA","ASL","???", /*10-1F*/ + "JSR","AND","???","???","BIT","AND","ROL","???","PLP","AND","ROL","???","BIT","AND","ROL","???", /*20-2F*/ + "BMI","AND","???","???","???","AND","ROL","???","SEC","AND","???","???","???","AND","ROL","???", /*30-3F*/ + "RTI","EOR","???","???","???","EOR","LSR","???","PHA","EOR","LSR","???","JMP","EOR","LSR","???", /*40-4F*/ + "BVC","EOR","???","???","???","EOR","LSR","???","CLI","EOR","???","???","???","EOR","LSR","???", /*50-5F*/ + "RTS","ADC","???","???","???","ADC","ROR","???","PLA","ADC","ROR","???","JMP","ADC","ROR","???", /*60-6F*/ + "BVS","ADC","???","???","???","ADC","ROR","???","SEI","ADC","???","???","???","ADC","ROR","???", /*70-7F*/ + "???","STA","???","???","STY","STA","STX","???","DEY","???","TXA","???","STY","STA","STX","???", /*80-8F*/ + "BCC","STA","???","???","STY","STA","STX","???","TYA","STA","TXS","???","???","STA","???","???", /*90-9F*/ + "LDY","LDA","LDX","???","LDY","LDA","LDX","???","TAY","LDA","TAX","???","LDY","LDA","LDX","???", /*A0-AF*/ + "BCS","LDA","???","???","LDY","LDA","LDX","???","CLV","LDA","TSX","???","LDY","LDA","LDX","???", /*B0-BF*/ + "CPY","CMP","???","???","CPY","CMP","DEC","???","INY","CMP","DEX","???","CPY","CMP","DEC","???", /*C0-CF*/ + "BNE","CMP","???","???","???","CMP","DEC","???","CLD","CMP","???","???","???","CMP","DEC","???", /*D0-DF*/ + "CPX","SBC","???","???","CPX","SBC","INC","???","INX","SBC","NOP","???","CPX","SBC","INC","???", /*E0-EF*/ + "BEQ","SBC","???","???","???","SBC","INC","???","SED","SBC","???","???","???","SBC","INC","???" /*F0-FF*/ +}; +static u8 addrtable[256] = { +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ + im,ix,er,er,er,zp,zp,er,no,im,ac,er,er,ab,ab,er, /*00-0f*/ + re,iy,er,er,er,zx,zx,er,no,ay,er,er,er,ax,ax,er, /*10-1f*/ + ab,ix,er,er,zp,zp,zp,er,no,im,ac,er,ab,ab,ab,er, /*20-2f*/ + re,iy,er,er,er,zx,zx,er,no,ay,er,er,er,ax,ax,er, /*30-3f*/ + no,ix,er,er,er,zp,zp,er,no,im,ac,er,ab,ab,ab,er, /*40-4f*/ + re,iy,er,er,er,zx,zx,er,no,ay,er,er,er,ax,ax,er, /*50-5f*/ + no,ix,er,er,er,zp,zp,er,no,im,ac,er,in,ab,ab,er, /*60-6f*/ + re,iy,er,er,er,zx,zx,er,no,ay,er,er,er,ax,ax,er, /*70-7f*/ + er,ix,er,er,zp,zp,zp,er,no,er,no,er,ab,ab,ab,er, /*80-8f*/ + re,iy,er,er,zx,zx,zy,er,no,ay,no,er,er,ax,er,er, /*90-9f*/ + im,ix,im,er,zp,zp,zp,er,no,im,no,er,ab,ab,ab,er, /*a0-af*/ + re,iy,er,er,zx,zx,zy,er,no,ay,no,er,ax,ax,ay,er, /*b0-bf*/ + im,ix,er,er,zp,zp,zp,er,no,im,no,er,ab,ab,ab,er, /*c0-cf*/ + re,iy,er,er,er,zx,zx,er,no,ay,er,er,er,ax,ax,er, /*d0-df*/ + im,ix,er,er,zp,zp,zp,er,no,im,no,er,ab,ab,ab,er, /*e0-ef*/ + re,iy,er,er,zx,zx,zx,er,no,ay,er,er,ax,ax,ax,er /*f0-ff*/ +}; +#endif + +static u8 oplength[256]; + +u16 cpu_disassemble(char *buffer,u16 opcodepos) +{ + u8 opcode,size; + u16 addr; + + strcpy(buffer,""); + opcode = cpu_read(opcodepos); + switch(addrtable[opcode]) { + case er:size = 1;sprintf(buffer,"%02X .db $%02x",opcode,opcode);break; + case no:size = 1;sprintf(buffer,"%02X %s",opcode,opcodes[opcode]);break; + case ac:size = 1;sprintf(buffer,"%02X %s a",opcode,opcodes[opcode]);break; + case ab: + size = 3; + addr = cpu_read(opcodepos+1) | (cpu_read(opcodepos+2) << 8); + sprintf(buffer,"%02X %02X %02X %s $%04X",opcode,addr & 0xFF,(addr >> 8) & 0xFF,opcodes[opcode],addr); + break; + case ax: + size = 3; + addr = cpu_read(opcodepos+1) | (cpu_read(opcodepos+2) << 8); + sprintf(buffer,"%02X %02X %02X %s $%04X,x",opcode,addr & 0xFF,(addr >> 8) & 0xFF,opcodes[opcode],addr); + break; + case ay: + size = 3; + addr = cpu_read(opcodepos+1) | (cpu_read(opcodepos+2) << 8); + sprintf(buffer,"%02X %02X %02X %s $%04X,y",opcode,addr & 0xFF,(addr >> 8) & 0xFF,opcodes[opcode],addr); + break; + case in:size = 3;sprintf(buffer,"%02X %02X %02X %s ($%04X)",opcode,cpu_read(opcodepos+1),cpu_read(opcodepos+2),opcodes[opcode],cpu_read(opcodepos+1) | (cpu_read(opcodepos+2) << 8));break; + case im:size = 2;sprintf(buffer,"%02X %02X %s #$%02X",opcode,cpu_read(opcodepos+1),opcodes[opcode],cpu_read(opcodepos+1));break; + case ix:size = 2;sprintf(buffer,"%02X %02X %s ($%02X,x)",opcode,cpu_read(opcodepos+1),opcodes[opcode],cpu_read(opcodepos+1));break; + case iy:size = 2;sprintf(buffer,"%02X %02X %s ($%02X),y",opcode,cpu_read(opcodepos+1),opcodes[opcode],cpu_read(opcodepos+1));break; + case re:size = 2;sprintf(buffer,"%02X %02X %s $%04X",opcode,cpu_read(opcodepos+1),opcodes[opcode],opcodepos+size+((s8)cpu_read(opcodepos+1)));break; + case zp:size = 2;sprintf(buffer,"%02X %02X %s $%02X",opcode,cpu_read(opcodepos+1),opcodes[opcode],cpu_read(opcodepos+1));break; + case zx:size = 2;sprintf(buffer,"%02X %02X %s $%02X,x",opcode,cpu_read(opcodepos+1),opcodes[opcode],cpu_read(opcodepos+1));break; + case zy:size = 2;sprintf(buffer,"%02X %02X %s $%02X,y",opcode,cpu_read(opcodepos+1),opcodes[opcode],cpu_read(opcodepos+1));break; + default:size = 1;sprintf(buffer,"disassembler bug");break; + } + return(opcodepos + size); +} + +void cpu_disassemble_init() +{ + int i; + + for(i=0;i<256;i++) { + switch(addrtable[i]) { + case er: + case no: + case ac: + oplength[i] = 1; + break; + case im: + case ix: + case iy: + case re: + case zp: + case zx: + case zy: + oplength[i] = 2; + break; + case ab: + case ax: + case ay: + case in: + oplength[i] = 3; + break; + } + } +} diff --git a/apps/nesemu2/src/nes/cpu/execute.hh b/apps/nesemu2/src/nes/cpu/execute.hh new file mode 100644 index 00000000..003ae3c5 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/execute.hh @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define OP(n,o,a) \ + case 0x##n: \ + AM_##a(); \ + OP_##o(); \ + break; + +#ifdef SHOW_DISASM +int showdisasm = 0; +#endif + +static INLINE void cpu_step() +{ + OPADDR = PC; + OPCODE = memread(PC); +#ifdef SHOW_DISASM + if(showdisasm) { + static char buf[256]; + cpu_disassemble(buf,PC); + compact_flags(); + log_printf("%7d A:%02X X:%02X Y:%02X P:%02X SP:%02X [%02X %02X %02X %02X %02X] I:%02X %04X: %s\n", + (u32)CYCLES,A,X,Y,P,SP, + cpu_read((SP|0x100)+1), cpu_read((SP|0x100)+2), cpu_read((SP|0x100)+3), cpu_read((SP|0x100)+4), cpu_read((SP|0x100)+5), + PREV_IRQSTATE,PC,buf); + } +#endif + PC++; + switch(nes->cpu.opcode) { + +#ifdef CPU_UNDOC + OP(00, BRK,IMM) OP(01, ORA,INX) OP(02, UNK,UNK) OP(03, SLO,INX) OP(04,NOPR,ZPG) OP(05, ORA,ZPG) OP(06, ASL,ZPG) OP(07, SLO,ZPG) + OP(08, PHP,IMP) OP(09, ORA,IMM) OP(0A,ASLA,IMP) OP(0B, AAC,IMM) OP(0C,NOPR,ABS) OP(0D, ORA,ABS) OP(0E, ASL,ABS) OP(0F, SLO,ABS) + OP(10, BPL,REL) OP(11, ORA,IYR) OP(12, UNK,UNK) OP(13, SLO,INY) OP(14,NOPR,ZPX) OP(15, ORA,ZPX) OP(16, ASL,ZPX) OP(17, SLO,ZPX) + OP(18, CLC,IMP) OP(19, ORA,AYR) OP(1A, NOP,IMP) OP(1B, SLO,ABY) OP(1C,NOPR,AXR) OP(1D, ORA,AXR) OP(1E, ASL,ABX) OP(1F, SLO,ABX) + OP(20, JSR,IMP) OP(21, AND,INX) OP(22, UNK,UNK) OP(23, RLA,INX) OP(24, BIT,ZPG) OP(25, AND,ZPG) OP(26, ROL,ZPG) OP(27, RLA,ZPG) + OP(28, PLP,IMP) OP(29, AND,IMM) OP(2A,ROLA,IMP) OP(2B, AAC,IMM) OP(2C, BIT,ABS) OP(2D, AND,ABS) OP(2E, ROL,ABS) OP(2F, RLA,ABS) + OP(30, BMI,REL) OP(31, AND,IYR) OP(32, UNK,UNK) OP(33, RLA,INY) OP(34,NOPR,ZPX) OP(35, AND,ZPX) OP(36, ROL,ZPX) OP(37, RLA,ZPX) + OP(38, SEC,IMP) OP(39, AND,AYR) OP(3A, NOP,IMP) OP(3B, RLA,ABY) OP(3C,NOPR,AXR) OP(3D, AND,AXR) OP(3E, ROL,ABX) OP(3F, RLA,ABX) + OP(40, RTI,IMP) OP(41, EOR,INX) OP(42, UNK,UNK) OP(43, SRE,INX) OP(44,NOPR,ZPG) OP(45, EOR,ZPG) OP(46, LSR,ZPG) OP(47, SRE,ZPG) + OP(48, PHA,IMP) OP(49, EOR,IMM) OP(4A,LSRA,IMP) OP(4B, ASR,IMM) OP(4C, JMP,ABS) OP(4D, EOR,ABS) OP(4E, LSR,ABS) OP(4F, SRE,ABS) + OP(50, BVC,REL) OP(51, EOR,IYR) OP(52, UNK,UNK) OP(53, SRE,INY) OP(54,NOPR,ZPX) OP(55, EOR,ZPX) OP(56, LSR,ZPX) OP(57, SRE,ZPX) + OP(58, CLI,IMP) OP(59, EOR,AYR) OP(5A, NOP,IMP) OP(5B, SRE,ABY) OP(5C,NOPR,AXR) OP(5D, EOR,AXR) OP(5E, LSR,ABX) OP(5F, SRE,ABX) + OP(60, RTS,IMP) OP(61, ADC,INX) OP(62, UNK,UNK) OP(63, RRA,INX) OP(64,NOPR,ZPG) OP(65, ADC,ZPG) OP(66, ROR,ZPG) OP(67, RRA,ZPG) + OP(68, PLA,IMP) OP(69, ADC,IMM) OP(6A,RORA,IMP) OP(6B, ARR,IMM) OP(6C, JMP,IND) OP(6D, ADC,ABS) OP(6E, ROR,ABS) OP(6F, RRA,ABS) + OP(70, BVS,REL) OP(71, ADC,IYR) OP(72, UNK,UNK) OP(73, RRA,INY) OP(74,NOPR,ZPX) OP(75, ADC,ZPX) OP(76, ROR,ZPX) OP(77, RRA,ZPX) + OP(78, SEI,IMP) OP(79, ADC,AYR) OP(7A, NOP,IMP) OP(7B, RRA,ABY) OP(7C,NOPR,AXR) OP(7D, ADC,AXR) OP(7E, ROR,ABX) OP(7F, RRA,ABX) + OP(80,NOPR,IMM) OP(81, STA,INX) OP(82,NOPR,IMM) OP(83, SAX,INX) OP(84, STY,ZPG) OP(85, STA,ZPG) OP(86, STX,ZPG) OP(87, SAX,ZPG) + OP(88, DEY,IMP) OP(89,NOPR,IMM) OP(8A, TXA,IMP) OP(8B, XAA,IMM) OP(8C, STY,ABS) OP(8D, STA,ABS) OP(8E, STX,ABS) OP(8F, SAX,ABS) + OP(90, BCC,REL) OP(91, STA,INY) OP(92, UNK,UNK) OP(93, AXA,INY) OP(94, STY,ZPX) OP(95, STA,ZPX) OP(96, STX,ZPY) OP(97, SAX,ZPY) + OP(98, TYA,IMP) OP(99, STA,ABY) OP(9A, TXS,IMP) OP(9B, XAS,ABY) OP(9C, SYA,AXR) OP(9D, STA,ABX) OP(9E, SXA,AYR) OP(9F, AXA,ABY) + OP(A0, LDY,IMM) OP(A1, LDA,INX) OP(A2, LDX,IMM) OP(A3, LAX,INX) OP(A4, LDY,ZPG) OP(A5, LDA,ZPG) OP(A6, LDX,ZPG) OP(A7, LAX,ZPG) + OP(A8, TAY,IMP) OP(A9, LDA,IMM) OP(AA, TAX,IMP) OP(AB, ATX,IMM) OP(AC, LDY,ABS) OP(AD, LDA,ABS) OP(AE, LDX,ABS) OP(AF, LAX,ABS) + OP(B0, BCS,REL) OP(B1, LDA,IYR) OP(B2, UNK,UNK) OP(B3, LAX,IYR) OP(B4, LDY,ZPX) OP(B5, LDA,ZPX) OP(B6, LDX,ZPY) OP(B7, LAX,ZPY) + OP(B8, CLV,IMP) OP(B9, LDA,AYR) OP(BA, TSX,IMP) OP(BB, LAR,AYR) OP(BC, LDY,AXR) OP(BD, LDA,AXR) OP(BE, LDX,AYR) OP(BF, LAX,AYR) + OP(C0, CPY,IMM) OP(C1, CMP,INX) OP(C2,NOPR,IMM) OP(C3, DCP,INX) OP(C4, CPY,ZPG) OP(C5, CMP,ZPG) OP(C6, DEC,ZPG) OP(C7, DCP,ZPG) + OP(C8, INY,IMP) OP(C9, CMP,IMM) OP(CA, DEX,IMP) OP(CB, AXS,IMM) OP(CC, CPY,ABS) OP(CD, CMP,ABS) OP(CE, DEC,ABS) OP(CF, DCP,ABS) + OP(D0, BNE,REL) OP(D1, CMP,IYR) OP(D2, UNK,UNK) OP(D3, DCP,INY) OP(D4,NOPR,ZPX) OP(D5, CMP,ZPX) OP(D6, DEC,ZPX) OP(D7, DCP,ZPX) + OP(D8, CLD,IMP) OP(D9, CMP,AYR) OP(DA, NOP,IMP) OP(DB, DCP,ABY) OP(DC,NOPR,AXR) OP(DD, CMP,AXR) OP(DE, DEC,ABX) OP(DF, DCP,ABX) + OP(E0, CPX,IMM) OP(E1, SBC,INX) OP(E2,NOPR,IMM) OP(E3, ISB,INX) OP(E4, CPX,ZPG) OP(E5, SBC,ZPG) OP(E6, INC,ZPG) OP(E7, ISB,ZPG) + OP(E8, INX,IMP) OP(E9, SBC,IMM) OP(EA, NOP,IMP) OP(EB, SBC,IMM) OP(EC, CPX,ABS) OP(ED, SBC,ABS) OP(EE, INC,ABS) OP(EF, ISB,ABS) + OP(F0, BEQ,REL) OP(F1, SBC,IYR) OP(F2, UNK,UNK) OP(F3, ISB,INY) OP(F4,NOPR,ZPX) OP(F5, SBC,ZPX) OP(F6, INC,ZPX) OP(F7, ISB,ZPX) + OP(F8, SED,IMP) OP(F9, SBC,AYR) OP(FA, NOP,IMP) OP(FB, ISB,ABY) OP(FC,NOPR,AXR) OP(FD, SBC,AXR) OP(FE, INC,ABX) OP(FF, ISB,ABX) +#else + OP(00, BRK,IMP) OP(01, ORA,INX) OP(02, UNK,UNK) OP(03, UNK,UNK) OP(04, UNK,UNK) OP(05, ORA,ZPG) OP(06, ASL,ZPG) OP(07, UNK,UNK) + OP(08, PHP,IMP) OP(09, ORA,IMM) OP(0A,ASLA,IMP) OP(0B, UNK,UNK) OP(0C, UNK,UNK) OP(0D, ORA,ABS) OP(0E, ASL,ABS) OP(0F, UNK,UNK) + OP(10, BPL,REL) OP(11, ORA,IYR) OP(12, UNK,UNK) OP(13, UNK,UNK) OP(14, UNK,UNK) OP(15, ORA,ZPX) OP(16, ASL,ZPX) OP(17, UNK,UNK) + OP(18, CLC,IMP) OP(19, ORA,AYR) OP(1A, UNK,UNK) OP(1B, UNK,UNK) OP(1C, UNK,UNK) OP(1D, ORA,AXR) OP(1E, ASL,ABX) OP(1F, UNK,UNK) + OP(20, JSR,IMP) OP(21, AND,INX) OP(22, UNK,UNK) OP(23, UNK,UNK) OP(24, BIT,ZPG) OP(25, AND,ZPG) OP(26, ROL,ZPG) OP(27, UNK,UNK) + OP(28, PLP,IMP) OP(29, AND,IMM) OP(2A,ROLA,IMP) OP(2B, UNK,UNK) OP(2C, BIT,ABS) OP(2D, AND,ABS) OP(2E, ROL,ABS) OP(2F, UNK,UNK) + OP(30, BMI,REL) OP(31, AND,IYR) OP(32, UNK,UNK) OP(33, UNK,UNK) OP(34, UNK,UNK) OP(35, AND,ZPX) OP(36, ROL,ZPX) OP(37, UNK,UNK) + OP(38, SEC,IMP) OP(39, AND,AYR) OP(3A, UNK,UNK) OP(3B, UNK,UNK) OP(3C, UNK,UNK) OP(3D, AND,AXR) OP(3E, ROL,ABX) OP(3F, UNK,UNK) + OP(40, RTI,IMP) OP(41, EOR,INX) OP(42, UNK,UNK) OP(43, UNK,UNK) OP(44, UNK,UNK) OP(45, EOR,ZPG) OP(46, LSR,ZPG) OP(47, UNK,UNK) + OP(48, PHA,IMP) OP(49, EOR,IMM) OP(4A,LSRA,IMP) OP(4B, UNK,UNK) OP(4C, JMP,ABS) OP(4D, EOR,ABS) OP(4E, LSR,ABS) OP(4F, UNK,UNK) + OP(50, BVC,REL) OP(51, EOR,IYR) OP(52, UNK,UNK) OP(53, UNK,UNK) OP(54, UNK,UNK) OP(55, EOR,ZPX) OP(56, LSR,ZPX) OP(57, UNK,UNK) + OP(58, CLI,IMP) OP(59, EOR,AYR) OP(5A, UNK,UNK) OP(5B, UNK,UNK) OP(5C, UNK,UNK) OP(5D, EOR,AXR) OP(5E, LSR,ABX) OP(5F, UNK,UNK) + OP(60, RTS,IMP) OP(61, ADC,INX) OP(62, UNK,UNK) OP(63, UNK,UNK) OP(64, UNK,UNK) OP(65, ADC,ZPG) OP(66, ROR,ZPG) OP(67, UNK,UNK) + OP(68, PLA,IMP) OP(69, ADC,IMM) OP(6A,RORA,IMP) OP(6B, UNK,UNK) OP(6C, JMP,IND) OP(6D, ADC,ABS) OP(6E, ROR,ABS) OP(6F, UNK,UNK) + OP(70, BVS,REL) OP(71, ADC,IYR) OP(72, UNK,UNK) OP(73, UNK,UNK) OP(74, UNK,UNK) OP(75, ADC,ZPX) OP(76, ROR,ZPX) OP(77, UNK,UNK) + OP(78, SEI,IMP) OP(79, ADC,AYR) OP(7A, UNK,UNK) OP(7B, UNK,UNK) OP(7C, UNK,UNK) OP(7D, ADC,AXR) OP(7E, ROR,ABX) OP(7F, UNK,UNK) + OP(80, UNK,UNK) OP(81, STA,INX) OP(82, UNK,UNK) OP(83, UNK,UNK) OP(84, STY,ZPG) OP(85, STA,ZPG) OP(86, STX,ZPG) OP(87, UNK,UNK) + OP(88, DEY,IMP) OP(89, UNK,UNK) OP(8A, TXA,IMP) OP(8B, UNK,UNK) OP(8C, STY,ABS) OP(8D, STA,ABS) OP(8E, STX,ABS) OP(8F, UNK,UNK) + OP(90, BCC,REL) OP(91, STA,INY) OP(92, UNK,UNK) OP(93, UNK,UNK) OP(94, STY,ZPX) OP(95, STA,ZPX) OP(96, STX,ZPY) OP(97, UNK,UNK) + OP(98, TYA,IMP) OP(99, STA,ABY) OP(9A, TXS,IMP) OP(9B, UNK,UNK) OP(9C, UNK,UNK) OP(9D, STA,ABX) OP(9E, UNK,UNK) OP(9F, UNK,UNK) + OP(A0, LDY,IMM) OP(A1, LDA,INX) OP(A2, LDX,IMM) OP(A3, UNK,UNK) OP(A4, LDY,ZPG) OP(A5, LDA,ZPG) OP(A6, LDX,ZPG) OP(A7, UNK,UNK) + OP(A8, TAY,IMP) OP(A9, LDA,IMM) OP(AA, TAX,IMP) OP(AB, UNK,UNK) OP(AC, LDY,ABS) OP(AD, LDA,ABS) OP(AE, LDX,ABS) OP(AF, UNK,UNK) + OP(B0, BCS,REL) OP(B1, LDA,IYR) OP(B2, UNK,UNK) OP(B3, UNK,UNK) OP(B4, LDY,ZPX) OP(B5, LDA,ZPX) OP(B6, LDX,ZPY) OP(B7, UNK,UNK) + OP(B8, CLV,IMP) OP(B9, LDA,AYR) OP(BA, TSX,IMP) OP(BB, UNK,UNK) OP(BC, LDY,AXR) OP(BD, LDA,AXR) OP(BE, LDX,AYR) OP(BF, UNK,UNK) + OP(C0, CPY,IMM) OP(C1, CMP,INX) OP(C2, UNK,UNK) OP(C3, UNK,UNK) OP(C4, CPY,ZPG) OP(C5, CMP,ZPG) OP(C6, DEC,ZPG) OP(C7, UNK,UNK) + OP(C8, INY,IMP) OP(C9, CMP,IMM) OP(CA, DEX,IMP) OP(CB, UNK,UNK) OP(CC, CPY,ABS) OP(CD, CMP,ABS) OP(CE, DEC,ABS) OP(CF, UNK,UNK) + OP(D0, BNE,REL) OP(D1, CMP,IYR) OP(D2, UNK,UNK) OP(D3, UNK,UNK) OP(D4, UNK,UNK) OP(D5, CMP,ZPX) OP(D6, DEC,ZPX) OP(D7, UNK,UNK) + OP(D8, CLD,IMP) OP(D9, CMP,AYR) OP(DA, UNK,UNK) OP(DB, UNK,UNK) OP(DC, UNK,UNK) OP(DD, CMP,AXR) OP(DE, DEC,ABX) OP(DF, UNK,UNK) + OP(E0, CPX,IMM) OP(E1, SBC,INX) OP(E2, UNK,UNK) OP(E3, UNK,UNK) OP(E4, CPX,ZPG) OP(E5, SBC,ZPG) OP(E6, INC,ZPG) OP(E7, UNK,UNK) + OP(E8, INX,IMP) OP(E9, SBC,IMM) OP(EA, NOP,IMP) OP(EB, UNK,UNK) OP(EC, CPX,ABS) OP(ED, SBC,ABS) OP(EE, INC,ABS) OP(EF, UNK,UNK) + OP(F0, BEQ,REL) OP(F1, SBC,IYR) OP(F2, UNK,UNK) OP(F3, UNK,UNK) OP(F4, UNK,UNK) OP(F5, SBC,ZPX) OP(F6, INC,ZPX) OP(F7, UNK,UNK) + OP(F8, SED,IMP) OP(F9, SBC,AYR) OP(FA, UNK,UNK) OP(FB, UNK,UNK) OP(FC, UNK,UNK) OP(FD, SBC,AXR) OP(FE, INC,ABX) OP(FF, UNK,UNK) +#endif + + } + if(PREV_NMISTATE) { + NMISTATE = 0; + execute_nmi(); + } + else if(PREV_IRQSTATE) { + execute_irq(); + } +} + +u32 cpu_execute(u32 cycles) +{ + u64 start = CYCLES; + u64 stop = CYCLES + cycles; + + while (CYCLES < stop && BADOPCODE == 0) { + cpu_step(); + if (OPCODE == 0) + break; + } + return((u32)(CYCLES - start)); +} + +void cpu_execute_frame() +{ + u32 curframe = FRAMES; + + while(FRAMES == curframe) { + cpu_step(); + } +} diff --git a/apps/nesemu2/src/nes/cpu/helper.hh b/apps/nesemu2/src/nes/cpu/helper.hh new file mode 100644 index 00000000..2cda70ca --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/helper.hh @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE u8 memread(u32 addr) +{ + static int n; + + //handle dpcm cycle stealing + if(nes->cpu.pcmcycles) { + n = nes->cpu.pcmcycles - 1; + nes->cpu.pcmcycles = 0; + if(addr == 0x4016 || addr == 0x4017) { + if(n--) + memread(addr); + while(n--) + cpu_tick(); + } + else { + while(n--) + memread(addr); + } + apu_dpcm_fetch(); + } + + //increment cycle counter, check irq lines + cpu_tick(); + + //read data from address + return(cpu_read(addr)); +} + +static INLINE void memwrite(u32 addr,u8 data) +{ + //handle dpcm cycle stealing + if(nes->cpu.pcmcycles) + nes->cpu.pcmcycles--; + + //increment cycle counter, check irq lines + cpu_tick(); + + //write data to its address + cpu_write(addr,data); +} + +//push data to stack +static INLINE void push(u8 data) +{ + memwrite(SP | 0x100,data); + SP--; +} + +//pop data from stack +static INLINE u8 pop() +{ + SP++; + return(memread(SP | 0x100)); +} + +//check value for n/z and set flags +static INLINE void checknz(u8 n) +{ + FLAG_N = (n >> 7) & 1; + FLAG_Z = (n == 0) ? 1 : 0; +} + +static INLINE void expand_flags() +{ + FLAG_C = (P & 0x01) >> 0; + FLAG_Z = (P & 0x02) >> 1; + FLAG_I = (P & 0x04) >> 2; + FLAG_D = (P & 0x08) >> 3; + FLAG_V = (P & 0x40) >> 6; + FLAG_N = (P & 0x80) >> 7; +} + +static INLINE void compact_flags() +{ +#ifdef CPU_DEBUG + if(FLAG_C & 0xFE || + FLAG_Z & 0xFE || + FLAG_I & 0xFE || + FLAG_D & 0xFE || + FLAG_V & 0xFE || + FLAG_N & 0xFE) { + log_printf("compact_flags: one or more flags is dirty!\n"); + } +#endif + P = 0x20; + P |= (FLAG_C) << 0; + P |= (FLAG_Z) << 1; + P |= (FLAG_I) << 2; + P |= (FLAG_D) << 3; + P |= (FLAG_V) << 6; + P |= (FLAG_N) << 7; +} + +static INLINE void execute_nmi() +{ + memread(PC); + memread(PC); + push((u8)(PC >> 8)); + push((u8)PC); + compact_flags(); + push(P); + FLAG_I = 1; + PC = memread(0xFFFA); + PC |= memread(0xFFFB) << 8; +} + +static INLINE void execute_irq() +{ + memread(PC); + memread(PC); + push((u8)(PC >> 8)); + push((u8)PC); + compact_flags(); + push(P); + FLAG_I = 1; + if(NMISTATE) { + NMISTATE = 0; + PC = memread(0xFFFA); + PC |= memread(0xFFFB) << 8; + } + else { + PC = memread(0xFFFE); + PC |= memread(0xFFFF) << 8; + } +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/alu.hh b/apps/nesemu2/src/nes/cpu/opcodes/alu.hh new file mode 100644 index 00000000..55cc9613 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/alu.hh @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_ORA() +{ + A |= memread(EFFADDR); + checknz(A); +} + +static INLINE void OP_AND() +{ + A &= memread(EFFADDR); + checknz(A); +} + +static INLINE void OP_EOR() +{ + A ^= memread(EFFADDR); + checknz(A); +} + +static INLINE void OP_ASL() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + FLAG_C = (TMPREG >> 7) & 1; + TMPREG <<= 1; + memwrite(EFFADDR,TMPREG); + checknz(TMPREG); +} + +static INLINE void OP_ASLA() +{ + FLAG_C = (A >> 7) & 1; + A <<= 1; + checknz(A); +} + +static INLINE void OP_ROL() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + tmp8 = FLAG_C; + FLAG_C = (TMPREG >> 7) & 1; + TMPREG = (TMPREG << 1) | tmp8; + memwrite(EFFADDR,TMPREG); + checknz(TMPREG); +} + +static INLINE void OP_ROLA() +{ + TMPREG = FLAG_C; + FLAG_C = (A >> 7) & 1; + A = (A << 1) | TMPREG; + checknz(A); +} + +static INLINE void OP_ROR() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + tmp8 = FLAG_C; + FLAG_C = TMPREG & 1; + TMPREG = (TMPREG >> 1) | (tmp8 << 7); + memwrite(EFFADDR,TMPREG); + checknz(TMPREG); +} + +static INLINE void OP_RORA() +{ + TMPREG = FLAG_C; + FLAG_C = A & 1; + A = (A >> 1) | (TMPREG << 7); + checknz(A); +} + +static INLINE void OP_LSR() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + FLAG_C = TMPREG & 1; + TMPREG >>= 1; + memwrite(EFFADDR,TMPREG); + checknz(TMPREG); +} + +static INLINE void OP_LSRA() +{ + FLAG_C = A & 1; + A >>= 1; + checknz(A); +} + +static INLINE void OP_ADC() +{ + TMPREG = memread(EFFADDR); + tmpi = A + TMPREG + FLAG_C; + FLAG_C = (tmpi & 0xFF00) ? 1 : 0; + FLAG_V = (((A ^ tmpi) & (TMPREG ^ tmpi)) & 0x80) ? 1 : 0; + A = (u8)tmpi; + checknz(A); +} + +static INLINE void OP_SBC() +{ + TMPREG = memread(EFFADDR); + tmpi = A - TMPREG - (1 - FLAG_C); + FLAG_C = ((tmpi & 0xFF00) == 0) ? 1 : 0; + FLAG_V = (((A ^ TMPREG) & (A ^ tmpi)) & 0x80) ? 1 : 0; + A = (u8)tmpi; + checknz(A); +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/branch.hh b/apps/nesemu2/src/nes/cpu/opcodes/branch.hh new file mode 100644 index 00000000..2c1cd7b7 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/branch.hh @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* +Relative addressing (BCC, BCS, BNE, BEQ, BPL, BMI, BVC, BVS) +# address R/W description +--- --------- --- --------------------------------------------- +1 PC R fetch opcode, increment PC +2 PC R fetch operand, increment PC +3 PC R Fetch opcode of next instruction. If branch is taken, add operand to PCL. Otherwise increment PC. +4+ PC* R Fetch opcode of next instruction. Fix PCH. If it did not change, increment PC. +5! PC R Fetch opcode of next instruction, increment PC. +Notes: The opcode fetch of the next instruction is included to this diagram for illustration + purposes. When determining real execution times, remember to subtract the last cycle. +* The high byte of Program Counter (PCH) may be invalid at this time, i.e. it may be smaller or bigger by $100. ++ If branch is taken, this cycle will be executed. +! If branch occurs to different page, this cycle will be executed. +*/ +static INLINE void BRANCH(int n) +{ + if(n == 0) + return; + memread(PC); + TMPADDR = PC + (s8)TMPREG; + if((TMPADDR ^ PC) & 0xFF00) { + memread((PC & 0xFF00) | (TMPADDR & 0xFF)); + } + PC = TMPADDR; +} + +static INLINE void OP_BMI() +{ + BRANCH(FLAG_N); +} + +static INLINE void OP_BPL() +{ + BRANCH((~FLAG_N) & 1); +} + +static INLINE void OP_BVS() +{ + BRANCH(FLAG_V); +} + +static INLINE void OP_BVC() +{ + BRANCH((~FLAG_V) & 1); +} + +static INLINE void OP_BCS() +{ + BRANCH(FLAG_C); +} + +static INLINE void OP_BCC() +{ + BRANCH((~FLAG_C) & 1); +} + +static INLINE void OP_BEQ() +{ + BRANCH(FLAG_Z); +} + +static INLINE void OP_BNE() +{ + BRANCH((~FLAG_Z) & 1); +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/compare.hh b/apps/nesemu2/src/nes/cpu/opcodes/compare.hh new file mode 100644 index 00000000..f962ac76 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/compare.hh @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_BIT() +{ + TMPREG = memread(EFFADDR); + FLAG_V = (TMPREG >> 6) & 1; + FLAG_N = (TMPREG >> 7) & 1; + FLAG_Z = (A & TMPREG) ? 0 : 1; +} + +static INLINE void OP_CMP() +{ + tmpi = A - memread(EFFADDR); + FLAG_C = (tmpi >= 0) ? 1 : 0; + checknz((u8)tmpi); +} + +static INLINE void OP_CPX() +{ + tmpi = X - memread(EFFADDR); + FLAG_C = (tmpi >= 0) ? 1 : 0; + checknz((u8)tmpi); +} + +static INLINE void OP_CPY() +{ + tmpi = Y - memread(EFFADDR); + FLAG_C = (tmpi >= 0) ? 1 : 0; + checknz((u8)tmpi); +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/flag.hh b/apps/nesemu2/src/nes/cpu/opcodes/flag.hh new file mode 100644 index 00000000..44fbb7f1 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/flag.hh @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_CLC() +{ + FLAG_C = 0; +} + +static INLINE void OP_CLI() +{ + FLAG_I = 0; +} + +static INLINE void OP_CLD() +{ + FLAG_D = 0; +} + +static INLINE void OP_CLV() +{ + FLAG_V = 0; +} + +static INLINE void OP_SEC() +{ + FLAG_C = 1; +} + +static INLINE void OP_SEI() +{ + FLAG_I = 1; +} + +static INLINE void OP_SED() +{ + FLAG_D = 1; +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/incdec.hh b/apps/nesemu2/src/nes/cpu/opcodes/incdec.hh new file mode 100644 index 00000000..9daf8de2 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/incdec.hh @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_DEC() +{ + tmp8 = memread(EFFADDR); + memwrite(EFFADDR,tmp8--); + memwrite(EFFADDR,tmp8); + checknz(tmp8); +} + +static INLINE void OP_DEX() +{ + X--; + checknz(X); +} + +static INLINE void OP_DEY() +{ + Y--; + checknz(Y); +} + +static INLINE void OP_INC() +{ + tmp8 = memread(EFFADDR); + memwrite(EFFADDR,tmp8++); + memwrite(EFFADDR,tmp8); + checknz(tmp8); +} + +static INLINE void OP_INX() +{ + X++; + checknz(X); +} + +static INLINE void OP_INY() +{ + Y++; + checknz(Y); +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/loadstore.hh b/apps/nesemu2/src/nes/cpu/opcodes/loadstore.hh new file mode 100644 index 00000000..f2eb02d9 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/loadstore.hh @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_LDA() +{ + A = memread(EFFADDR); + checknz(A); +} + +static INLINE void OP_LDX() +{ + X = memread(EFFADDR); + checknz(X); +} + +static INLINE void OP_LDY() +{ + Y = memread(EFFADDR); + checknz(Y); +} + +static INLINE void OP_STA() +{ + memwrite(EFFADDR,A); +} + +static INLINE void OP_STX() +{ + memwrite(EFFADDR,X); +} + +static INLINE void OP_STY() +{ + memwrite(EFFADDR,Y); +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/misc.hh b/apps/nesemu2/src/nes/cpu/opcodes/misc.hh new file mode 100644 index 00000000..3bde9173 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/misc.hh @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_NOP() +{ +} + +static INLINE void OP_NOPR() +{ + memread(EFFADDR); +} + +static INLINE void OP_UNK() +{ + BADOPCODE++; + log_printf("OP_UNK: $%04X = $%02X (cpu cycle %d)\n",OPADDR,OPCODE,CYCLES); + running = 0; +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/stack.hh b/apps/nesemu2/src/nes/cpu/opcodes/stack.hh new file mode 100644 index 00000000..ee554d49 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/stack.hh @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//push/pop +static INLINE void OP_PHA() +{ + push(A); +} + +static INLINE void OP_PHP() +{ + compact_flags(); + push(P | 0x10); +} + +static INLINE void OP_PLA() +{ + memread(SP | 0x100); + A = pop(); + checknz(A); +} + +static INLINE void OP_PLP() +{ + memread(SP | 0x100); + P = pop(); + expand_flags(); +} + +//rts/rti +static INLINE void OP_RTS() +{ + memread(SP | 0x100); + PC = pop(); + PC |= pop() << 8; + memread(PC++); +} + +static INLINE void OP_RTI() +{ + memread(SP | 0x100); + P = pop(); + expand_flags(); + PC = pop(); + PC |= pop() << 8; +} + +//jmp/jsr +static INLINE void OP_JMP() +{ + PC = EFFADDR; +} + +static INLINE void OP_JSR() +{ + PC++; + memread(SP | 0x100); + push((u8)(PC >> 8)); + push((u8)PC); + PC = (memread(PC) << 8) | TMPREG; +} + +/* +BRK +# address R/W description +--- ------- --- ----------------------------------------------- +1 PC R fetch opcode, increment PC +2 PC R read next instruction byte (and throw it away), increment PC +3 $0100,S W push PCH on stack (with B flag set), decrement S +4 $0100,S W push PCL on stack, decrement S +5 $0100,S W push P on stack, decrement S +6 $FFFE R fetch PCL +7 $FFFF R fetch PCH +*/ +static INLINE void OP_BRK() +{ + memread(EFFADDR); + push((u8)(PC >> 8)); + push((u8)PC); + compact_flags(); + push(P | 0x10); + FLAG_I = 1; + if(NMISTATE) { + NMISTATE = 0; + PC = memread(0xFFFA); + PC |= memread(0xFFFB) << 8; + } + else { + PC = memread(0xFFFE); + PC |= memread(0xFFFF) << 8; + } +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/transfer.hh b/apps/nesemu2/src/nes/cpu/opcodes/transfer.hh new file mode 100644 index 00000000..ea771319 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/transfer.hh @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void OP_TAX() +{ + X = A; + checknz(X); +} + +static INLINE void OP_TAY() +{ + Y = A; + checknz(Y); +} + +static INLINE void OP_TXA() +{ + A = X; + checknz(A); +} + +static INLINE void OP_TYA() +{ + A = Y; + checknz(A); +} + +static INLINE void OP_TSX() +{ + X = SP; + checknz(X); +} + +static INLINE void OP_TXS() +{ + SP = X; +} diff --git a/apps/nesemu2/src/nes/cpu/opcodes/undocumented.hh b/apps/nesemu2/src/nes/cpu/opcodes/undocumented.hh new file mode 100644 index 00000000..c4625ed0 --- /dev/null +++ b/apps/nesemu2/src/nes/cpu/opcodes/undocumented.hh @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//undocumented opcodes +static INLINE void OP_LAX() +{ + A = X = memread(EFFADDR); + checknz(A); +} + +static INLINE void OP_SAX() +{ + memwrite(EFFADDR,A & X); +} + +static INLINE void OP_DCP() +{ + tmp8 = memread(EFFADDR); + memwrite(EFFADDR,tmp8--); + memwrite(EFFADDR,tmp8); + tmpi = A - tmp8; + FLAG_C = (tmpi >= 0) ? 1 : 0; + checknz((u8)tmpi); +} + +static INLINE void OP_ISB() +{ + tmp8 = memread(EFFADDR); + memwrite(EFFADDR,tmp8++); + memwrite(EFFADDR,tmp8); + tmpi = A - tmp8 - (1 - FLAG_C); + FLAG_C = ((tmpi & 0xFF00) == 0) ? 1 : 0; + FLAG_V = (((A ^ tmp8) & (A ^ tmpi)) & 0x80) ? 1 : 0; + A = (u8)tmpi; + checknz(A); +} + +static INLINE void OP_SLO() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + FLAG_C = (TMPREG >> 7) & 1; + TMPREG <<= 1; + A |= TMPREG; + memwrite(EFFADDR,TMPREG); + checknz(A); +} + +static INLINE void OP_RLA() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + tmp8 = FLAG_C; + FLAG_C = (TMPREG >> 7) & 1; + TMPREG = (TMPREG << 1) | tmp8; + memwrite(EFFADDR,TMPREG); + A &= TMPREG; + checknz(A); +} + +static INLINE void OP_SRE() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + FLAG_C = TMPREG & 1; + TMPREG >>= 1; + memwrite(EFFADDR,TMPREG); + A ^= TMPREG; + checknz(A); +} + +static INLINE void OP_RRA() +{ + TMPREG = memread(EFFADDR); + memwrite(EFFADDR,TMPREG); + tmp8 = FLAG_C; + FLAG_C = TMPREG & 1; + TMPREG = (TMPREG >> 1) | (tmp8 << 7); + memwrite(EFFADDR,TMPREG); + tmpi = A + TMPREG + FLAG_C; + FLAG_C = (tmpi & 0xFF00) ? 1 : 0; + FLAG_V = (((A ^ tmpi) & (TMPREG ^ tmpi)) & 0x80) ? 1 : 0; + A = (u8)tmpi; + checknz(A); +} + +//"strange behavior" opcodes +static INLINE void OP_AAC() +{ + A &= memread(EFFADDR); + checknz(A); + FLAG_C = FLAG_N; +} + +static INLINE void OP_ASR() +{ + A &= memread(EFFADDR); + FLAG_C = A & 1; + A >>= 1; + checknz(A); +} + +static INLINE void OP_ARR() +{ + A &= memread(EFFADDR); + A = (A >> 1) | (FLAG_C << 7); + checknz(A); + FLAG_C = (A >> 6) & 1; + FLAG_V = ((A >> 5) & 1) ^ FLAG_C; +} + +static INLINE void OP_ATX() +{ +// A &= memread(EFFADDR); + A = memread(EFFADDR); + X = A; + checknz(A); +} + +static INLINE void OP_AXS() +{ + tmpi = (X & A) - memread(EFFADDR); + X = (u8)tmpi; + checknz(X); + if(tmpi >= 0) + FLAG_C = 1; + else + FLAG_C = 0; +} + +static INLINE void OP_SYA() +{ + tmp8 = Y & ((EFFADDR >> 8) + 1); + if((X + memread(OPADDR + 1)) <= 0xFF) + memwrite(EFFADDR,tmp8); +} + +static INLINE void OP_SXA() +{ + tmp8 = X & ((EFFADDR >> 8) + 1); + if((Y + memread(OPADDR + 1)) <= 0xFF) + memwrite(EFFADDR,tmp8); +} + +static INLINE void OP_LAR() +{ + tmp8 = memread(EFFADDR) & SP; + A = X = SP = tmp8; +} + +static INLINE void OP_AXA() +{ + tmp8 = (A & X) & 7; + memwrite(EFFADDR,tmp8); +} + +static INLINE void OP_XAS() +{ + SP = A & X; + tmp8 = SP & (EFFADDR >> 8); + memwrite(EFFADDR,tmp8); +} + +static INLINE void OP_XAA() +{ + A = (A | 0xEE) & X & memread(EFFADDR); + checknz(A); +} diff --git a/apps/nesemu2/src/nes/genie.c b/apps/nesemu2/src/nes/genie.c new file mode 100644 index 00000000..79857151 --- /dev/null +++ b/apps/nesemu2/src/nes/genie.c @@ -0,0 +1,283 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "nes/nes.h" +#include "nes/genie.h" +#include "nes/state/state.h" +#include "mappers/mapperinc.h" +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/config.h" +#include "misc/log.h" +#include "misc/paths.h" + +//holds code information +typedef struct codedata_s { + u16 addr; + u8 compare,data; +} codedata_t; + +//ines ident for determining if the genie rom has ines header +static u8 inesident[4] = {'N','E','S',0x1A}; + +//genie rom image (prg + chr) +static u8 *genierom = 0; + +//genie chr cache +static cache_t *geniecache = 0; + +//old read function pointers +static readfunc_t readcpu; + +//codes/register +static codedata_t code[3]; +static u8 reg; + +//kludge? +static void nullcycle() {} + +//declare the mapper here +MAPPER(B_GENIE,genie_reset,nullcycle,nullcycle,genie_state); + +//read handler for the cheats +static u8 genie_read_cheat(u32 addr) +{ + u8 ret = readcpu(addr); + + //reading from rom, lets cheat! + if(addr >= 0x8000) { + if((reg & 0x10) == 0 && addr == code[0].addr && ((reg & 2) == 0 || ret == code[0].compare)) { + log_printf("applying code 0, addr = %04X\n",addr); + return(code[0].data); + } + if((reg & 0x20) == 0 && addr == code[1].addr && ((reg & 4) == 0 || ret == code[1].compare)) { + log_printf("applying code 1, addr = %04X\n",addr); + return(code[1].data); + } + if((reg & 0x40) == 0 && addr == code[2].addr && ((reg & 8) == 0 || ret == code[2].compare)) { + log_printf("applying code 2, addr = %04X\n",addr); + return(code[2].data); + } + } + + return(ret); +} + +static void genie_write(u32 addr,u8 data) +{ + if(addr >= 0x8000) { + switch(addr) { + case 0x8000: + if((data & 1) == 0) { + + //unmap the write function + mem_setwritefunc(8,0); + + //we not using genie anymore + reg &= 0x7F; + + //save old read func + readcpu = cpu_getreadfunc(); + + //setup new read function + cpu_setreadfunc(genie_read_cheat); + + //restore the old mapper + nes->mapper = mapper_init(nes->cart->mapperid); + + //reset the mapper + nes->mapper->reset(1); + } + else { + reg = 0x80 | (data & 0x7E); + log_printf("genie_write: reg = $%02X (data = $%02X)\n",reg,data); + log_printf(" code 0: %s, %s\n",(reg & 0x10) ? "disabled" : "enabled",(reg & 2) ? "compare" : "no compare"); + log_printf(" code 1: %s, %s\n",(reg & 0x20) ? "disabled" : "enabled",(reg & 4) ? "compare" : "no compare"); + log_printf(" code 2: %s, %s\n",(reg & 0x40) ? "disabled" : "enabled",(reg & 8) ? "compare" : "no compare"); + } + break; + case 0x8001: code[0].addr = (code[0].addr & 0x00FF) | (data << 8); break; + case 0x8002: code[0].addr = (code[0].addr & 0xFF00) | (data << 0); break; + case 0x8003: code[0].compare = data; break; + case 0x8004: code[0].data = data; break; + case 0x8005: code[1].addr = (code[1].addr & 0x00FF) | (data << 8); break; + case 0x8006: code[1].addr = (code[1].addr & 0xFF00) | (data << 0); break; + case 0x8007: code[1].compare = data; break; + case 0x8008: code[1].data = data; break; + case 0x8009: code[2].addr = (code[2].addr & 0x00FF) | (data << 8); break; + case 0x800A: code[2].addr = (code[2].addr & 0xFF00) | (data << 0); break; + case 0x800B: code[2].compare = data; break; + case 0x800C: code[2].data = data; break; + } + log_printf("genie write: $%04X = $%02X\n",addr,data); + } +} + +int genie_loadrom(char *filename) +{ + FILE *fp; + size_t len; + u8 buf[16]; + + //try to open the file + if((fp = fopen(filename,"rb")) == 0) { + log_printf("genie_load: error opening game genie rom '%s'\n",filename); + return(1); + } + + //read 16 bytes to see if it has a ines header + fread(buf,1,16,fp); + + //get the file size of the file + fseek(fp,0,SEEK_END); + len = ftell(fp); + fseek(fp,0,SEEK_SET); + + //allocate memory for genierom/geniecache + genierom = (u8*)mem_alloc(0x1000 + 0x400); + geniecache = (cache_t*)mem_alloc(0x400); + + //see if there is an ines header, we know we have 16kb prg, 8kb chr + if(memcmp(buf,inesident,4) == 0) { + fseek(fp,16,SEEK_SET); + fread(genierom,1,0x1000,fp); + fseek(fp,16 + 0x4000,SEEK_SET); + fread(genierom + 0x1000,1,0x400,fp); + } + + //we have no header and 16kb prg, 8kb chr + else if(len == 0x6000) { + fread(genierom,1,0x1000,fp); + fseek(fp,0x4000,SEEK_SET); + fread(genierom + 0x1000,1,0x400,fp); + } + + //if we have raw prg/chr rom dump 4kb prg, 1kb chr + else if(len == 0x1400) { + fread(genierom,1,0x1400,fp); + } + + //now what? error! + else { + fclose(fp); + genie_unload(); + log_printf("genie_load: unable to load genie rom '%s'\n",filename); + return(1); + } + + //cache the genie chr + cache_tiles(genierom + 0x1000,geniecache,64,0); + + //close the file, output message, return + fclose(fp); + log_printf("genie_load: loaded game genie rom '%s' (%d bytes)\n",filename,len); + return(0); +} + +int genie_load() +{ + char biosfile[1024]; + + //if the rom isnt loaded, we need to do that now + if(genierom == 0) { + + //clear the string + memset(biosfile,0,1024); + + //parse the bios path + config_get_eval_string(biosfile,"path.bios"); + + //append the path seperator + str_appendchar(biosfile,PATH_SEPERATOR); + + //append the bios filename + strcat(biosfile,config_get_string("nes.gamegenie.bios")); + + //try to load bios from the bios directory + if(genie_loadrom(biosfile) != 0) { + + //see if bios is in the current directory + if(genie_loadrom(config_get_string("nes.gamegenie.bios")) != 0) { + return(1); + } + } + + //register the save state stuff + state_register(B_GG,genie_state); + } + + //load in the genie mapper + nes->mapper = &mapperB_GENIE; + + return(0); +} + +void genie_unload() +{ + if(genierom) { + state_unregister(B_GG); + mem_free(genierom); + mem_free(geniecache); + genierom = 0; + geniecache = 0; + log_printf("genie_unload: unloaded game genie.\n"); + } +} + +void genie_reset(int hard) +{ + int i; + + //manually map in the prg and set write handlers + for(i=8;i<16;i++) { + mem_setreadptr(i,genierom); + mem_setwritefunc(8,genie_write); + } + + //manually map the game genie chr/cache + for(i=0;i<8;i++) { + nes->ppu.readpages[i] = genierom + 0x1000; + nes->ppu.writepages[i] = 0; + nes->ppu.cachepages[i] = geniecache; + nes->ppu.cachepages_hflip[i] = geniecache; + } + + //setup genie registers + memset(code,0,sizeof(codedata_t) * 3); + reg = 0x80; + + log_printf("genie_reset: ok\n"); +} + +void genie_state(int mode,u8 *data) +{ + STATE_U8(reg); + STATE_U16(code[0].addr); + STATE_U8(code[0].compare); + STATE_U8(code[0].data); + STATE_U16(code[1].addr); + STATE_U8(code[1].compare); + STATE_U8(code[1].data); + STATE_U16(code[2].addr); + STATE_U8(code[2].compare); + STATE_U8(code[2].data); +} diff --git a/apps/nesemu2/src/nes/genie.h b/apps/nesemu2/src/nes/genie.h new file mode 100644 index 00000000..d84b6546 --- /dev/null +++ b/apps/nesemu2/src/nes/genie.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__genie_h__ +#define __nes__genie_h__ + +int genie_load(); +void genie_unload(); +void genie_reset(int hard); +void genie_state(int mode,u8 *data); + +#endif diff --git a/apps/nesemu2/src/nes/io.c b/apps/nesemu2/src/nes/io.c new file mode 100644 index 00000000..c6c4ab01 --- /dev/null +++ b/apps/nesemu2/src/nes/io.c @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "misc/log.h" +#include "misc/config.h" +#include "nes/io.h" +#include "nes/nes.h" +#include "system/input.h" + +//read from apu/joypads +u8 nes_read_4000(u32 addr) +{ + switch(addr) { + //apu registers + case 0x4000: case 0x4001: case 0x4002: case 0x4003: + case 0x4004: case 0x4005: case 0x4006: case 0x4007: + case 0x4008: case 0x4009: case 0x400A: case 0x400B: + case 0x400C: case 0x400D: case 0x400E: case 0x400F: + case 0x4010: case 0x4011: case 0x4012: case 0x4013: + case 0x4015: + return(apu_read(addr)); + + //sprite memory read + case 0x4014: + return(nes->ppu.oam[nes->ppu.oamaddr]); + + //input port 0 read + case 0x4016: + return(nes->inputdev[0]->read()); + + //input port 1 read + case 0x4017: + return(nes->inputdev[1]->read()); + + default: + if(config_get_bool("nes.log_unhandled_io")) + log_printf("nes_read_4000: unhandled read at $%04X\n",addr); + break; + } + return(0); +} + +//write to sprite dma, nes joypad strobe, and apu registers +void nes_write_4000(u32 addr,u8 data) +{ + u32 temp,temp2; + + switch(addr) { + //apu registers + case 0x4000: case 0x4001: case 0x4002: case 0x4003: + case 0x4004: case 0x4005: case 0x4006: case 0x4007: + case 0x4008: case 0x4009: case 0x400A: case 0x400B: + case 0x400C: case 0x400D: case 0x400E: case 0x400F: + case 0x4010: case 0x4011: case 0x4012: case 0x4013: + case 0x4015: case 0x4017: + apu_write(addr,data); + break; + + //sprite dma write + case 0x4014: + temp2 = data << 8; + for(temp=0;temp<256;temp++,temp2++) + cpu_write(0x2004,cpu_read(temp2)); + temp2 = 513 + ((u32)nes->cpu.cycles & 1); + for(temp=0;tempinputdev[0]->write(data); + nes->inputdev[1]->write(data); + nes->expdev->write(data); + break; + + default: + if(config_get_bool("nes.log_unhandled_io")) + log_printf("nes_write_4000: unhandled write at $%04X = $%02X\n",addr,data); + break; + } +} + +u8 nes_read_mem(u32 addr) +{ + if(nes->cpu.readpages[addr >> 10]) + return(nes->cpu.readpages[addr >> 10][addr & 0x3FF]); + return(0); +} + +void nes_write_mem(u32 addr,u8 data) +{ + if(nes->cpu.writepages[addr >> 10]) + nes->cpu.writepages[addr >> 10][addr & 0x3FF] = data; +} + +//read nes rom memory area ($8000-FFFF) +u8 nes_read_rom(u32 addr) +{ + return(nes->cpu.readpages[addr >> 10][addr & 0x3FF]); +} diff --git a/apps/nesemu2/src/nes/io.h b/apps/nesemu2/src/nes/io.h new file mode 100644 index 00000000..0c87a6e7 --- /dev/null +++ b/apps/nesemu2/src/nes/io.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Dead_Body * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__io_h__ +#define __nes__io_h__ + +#include "types.h" + +//nes io handlers +u8 nes_read_4000(u32 addr); +void nes_write_4000(u32 addr,u8 data); + +//generic read/write function handlers that use the memory ptrs +u8 nes_read_mem(u32 addr); +void nes_write_mem(u32 addr,u8 data); + +//nes rom read handler +u8 nes_read_rom(u32 addr); + +#endif diff --git a/apps/nesemu2/src/nes/memory.c b/apps/nesemu2/src/nes/memory.c new file mode 100644 index 00000000..a0e045cb --- /dev/null +++ b/apps/nesemu2/src/nes/memory.c @@ -0,0 +1,205 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "nes/memory.h" +#include "nes/nes.h" +#include "misc/log.h" + +void mem_setreadfunc(int page,readfunc_t func) +{ + page <<= 2; + nes->cpu.readfuncs[page+0] = func; + nes->cpu.readfuncs[page+1] = func; + nes->cpu.readfuncs[page+2] = func; + nes->cpu.readfuncs[page+3] = func; +} + +void mem_setwritefunc(int page,writefunc_t func) +{ + page <<= 2; + nes->cpu.writefuncs[page+0] = func; + nes->cpu.writefuncs[page+1] = func; + nes->cpu.writefuncs[page+2] = func; + nes->cpu.writefuncs[page+3] = func; +} + +void mem_setreadptr(int page,u8 *ptr) +{ + page <<= 2; + nes->cpu.readpages[page+0] = ptr; + nes->cpu.readpages[page+1] = ptr + 0x400; + nes->cpu.readpages[page+2] = ptr + 0x800; + nes->cpu.readpages[page+3] = ptr + 0xC00; +} + +void mem_setwriteptr(int page,u8 *ptr) +{ + page <<= 2; + nes->cpu.writepages[page+0] = ptr; + nes->cpu.writepages[page+1] = ptr + 0x400; + nes->cpu.writepages[page+2] = ptr + 0x800; + nes->cpu.writepages[page+3] = ptr + 0xC00; +} + +readfunc_t mem_getreadfunc(int page) +{ + page <<= 2; + return(nes->cpu.readfuncs[page]); +} + +writefunc_t mem_getwritefunc(int page) +{ + page <<= 2; + return(nes->cpu.writefuncs[page]); +} + +u8 *mem_getreadptr(int page) +{ + page <<= 2; + return(nes->cpu.readpages[page]); +} + +u8 *mem_getwriteptr(int page) +{ + page <<= 2; + return(nes->cpu.writepages[page]); +} + +void mem_setppureadfunc(int page,readfunc_t func) { nes->ppu.readfuncs[page] = func; } +void mem_setppuwritefunc(int page,writefunc_t func) { nes->ppu.writefuncs[page] = func; } +void mem_setppureadptr(int page,u8 *ptr) { nes->ppu.readpages[page] = ptr; } +void mem_setppuwriteptr(int page,u8 *ptr) { nes->ppu.writepages[page] = ptr; } +readfunc_t mem_getppureadfunc(int page) { return(nes->ppu.readfuncs[page]); } +writefunc_t mem_getppuwritefunc(int page) { return(nes->ppu.writefuncs[page]); } +u8 *mem_getppureadptr(int page) { return(nes->ppu.readpages[page]); } +u8 *mem_getppuwriteptr(int page) { return(nes->ppu.writepages[page]); } + +void mem_unsetcpu(int banksize,int page) +{ + int i; + + page <<= 2; + for(i=0;i<(banksize);i++) { + nes->cpu.readpages[page + i] = + nes->cpu.writepages[page + i] = 0; + } +} + +void mem_unsetppu(int banksize,int page) +{ + int i; + + for(i=0;ippu.readpages[page + i] = + nes->ppu.writepages[page + i] = 0; + nes->ppu.cachepages[page] = + nes->ppu.cachepages_hflip[page] = 0; + } +} + +void mem_setprg(int banksize,int page,int bank) +{ + int i; + u8 *ptr = nes->cart->prg.data + ((bank * banksize * 1024) & nes->cart->prg.mask); + + page <<= 2; + for(i=0;i<(banksize);i++) { + nes->cpu.readpages[page + i] = ptr + ((i * 0x400) & nes->cart->prg.mask); + nes->cpu.writepages[page + i] = 0; + } +} + +void mem_setwram(int banksize,int page,int bank) +{ + int i; + u8 *ptr = nes->cart->wram.data + ((bank * banksize * 1024) & nes->cart->wram.mask); + + page <<= 2; + for(i=0;i<(banksize);i++) { + nes->cpu.readpages[page + i] = + nes->cpu.writepages[page + i] = ptr + i * 0x400; + } +} + +void mem_setnt(int banksize,int page,int bank) +{ + int i,p,offset = (bank * banksize * 1024) & 0xFFF; + + for(i=0;ippu.readpages[p] = nes->ppu.nametables + offset + (i * 1024); + nes->ppu.writepages[p] = nes->ppu.nametables + offset + (i * 1024); + } +} + +void mem_setchr(int banksize,int page,int bank) +{ + int i,p,offset = (bank * banksize * 1024) & nes->cart->chr.mask; + + for(i=0;ippu.readpages[p] = nes->cart->chr.data + offset + (i * 1024); + nes->ppu.writepages[p] = 0; + nes->ppu.cachepages[p] = (cache_t*)((u8*)nes->cart->cache + offset + (i * 0x400)); + nes->ppu.cachepages_hflip[p] = (cache_t*)((u8*)nes->cart->cache_hflip + offset + (i * 0x400)); + } +} + +void mem_setvram(int banksize,int page,int bank) +{ + int i,p,offset = (bank * banksize * 1024) & nes->cart->vram.mask; + + for(i=0;ippu.readpages[p] = + nes->ppu.writepages[p] = nes->cart->vram.data + offset + (i * 1024); + nes->ppu.cachepages[p] = (cache_t*)((u8*)nes->cart->vcache + offset + (i * 0x400)); + nes->ppu.cachepages_hflip[p] = (cache_t*)((u8*)nes->cart->vcache_hflip + offset + (i * 0x400)); + } +} + +void mem_setwramsize(int banks) { cart_setwramsize(nes->cart,banks); } +void mem_setvramsize(int banks) { cart_setvramsize(nes->cart,banks); } + +void mem_setmirroring(int m) +{ + switch(m) { + default: + log_printf("mem_setmirroring: bad mirroring type %d\n",m); + case MIRROR_H: mem_setmirroring2(0,0,1,1); break; + case MIRROR_V: mem_setmirroring2(0,1,0,1); break; + case MIRROR_1L:mem_setmirroring2(0,0,0,0); break; + case MIRROR_1H:mem_setmirroring2(1,1,1,1); break; + case MIRROR_4: mem_setmirroring2(0,1,2,3); break; + case MIRROR_MAPPER: break; + } +} + +void mem_setmirroring2(int n0,int n1,int n2,int n3) +{ + mem_setnt1(8 + 0,n0); + mem_setnt1(8 + 1,n1); + mem_setnt1(8 + 2,n2); + mem_setnt1(8 + 3,n3); + mem_setnt1(8 + 4,n0); + mem_setnt1(8 + 5,n1); + mem_setnt1(8 + 6,n2); + mem_setnt1(8 + 7,n3); +} diff --git a/apps/nesemu2/src/nes/memory.h b/apps/nesemu2/src/nes/memory.h new file mode 100644 index 00000000..3df75fab --- /dev/null +++ b/apps/nesemu2/src/nes/memory.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __memory_h__ +#define __memory_h__ + +#include "types.h" + +int mem_getprgsize(); +int mem_getchrsize(); + +//set/get cpu page read/write pointers/functions +void mem_setreadfunc(int page,readfunc_t func); +void mem_setwritefunc(int page,writefunc_t func); +void mem_setreadptr(int page,u8 *ptr); +void mem_setwriteptr(int page,u8 *ptr); +readfunc_t mem_getreadfunc(int page); +writefunc_t mem_getwritefunc(int page); +u8 *mem_getreadptr(int page); +u8 *mem_getwriteptr(int page); + +//set/get ppu page read/write pointers/functions +void mem_setppureadfunc(int page,readfunc_t func); +void mem_setppuwritefunc(int page,writefunc_t func); +void mem_setppureadptr(int page,u8 *ptr); +void mem_setppuwriteptr(int page,u8 *ptr); +readfunc_t mem_getppureadfunc(int page); +writefunc_t mem_getppuwritefunc(int page); +u8 *mem_getppureadptr(int page); +u8 *mem_getppuwriteptr(int page); + +//unset cpu/ppu memory page +void mem_unsetcpu(int banksize,int page); +void mem_unsetppu(int banksize,int page); + +//map to cpu memory +void mem_setprg(int banksize,int page,int rombank); +void mem_setwram(int banksize,int page,int rombank); + +//map to ppu memory +void mem_setnt(int banksize,int page,int rombank); +void mem_setchr(int banksize,int page,int rombank); +void mem_setvram(int banksize,int page,int rombank); + +//setup wram/sram/vram/svram +void mem_setwramsize(int banks); +void mem_setvramsize(int banks); + +//mirroring +void mem_setmirroring(int t); +void mem_setmirroring2(int n0,int n1,int n2,int n3); + +//macros for cpu memory +#define mem_unsetcpu1(p) mem_unsetcpu(1,p) +#define mem_unsetcpu2(p) mem_unsetcpu(2,p) +#define mem_unsetcpu4(p) mem_unsetcpu(4,p) +#define mem_unsetcpu8(p) mem_unsetcpu(8,p) +#define mem_unsetcpu16(p) mem_unsetcpu(16,p) +#define mem_unsetcpu32(p) mem_unsetcpu(32,p) +#define mem_setprg1(p,b) mem_setprg(1,p,b) +#define mem_setprg2(p,b) mem_setprg(2,p,b) +#define mem_setprg4(p,b) mem_setprg(4,p,b) +#define mem_setprg8(p,b) mem_setprg(8,p,b) +#define mem_setprg16(p,b) mem_setprg(16,p,b) +#define mem_setprg32(p,b) mem_setprg(32,p,b) +#define mem_setwram1(p,b) mem_setwram(1,p,b) +#define mem_setwram2(p,b) mem_setwram(2,p,b) +#define mem_setwram4(p,b) mem_setwram(4,p,b) +#define mem_setwram8(p,b) mem_setwram(8,p,b) +#define mem_setwram16(p,b) mem_setwram(16,p,b) +#define mem_setwram32(p,b) mem_setwram(32,p,b) + +//macros for ppu memory +#define mem_unsetppu1(p) mem_unsetppu(1,p) +#define mem_unsetppu2(p) mem_unsetppu(2,p) +#define mem_unsetppu4(p) mem_unsetppu(4,p) +#define mem_unsetppu8(p) mem_unsetppu(8,p) +#define mem_setnt1(p,b) mem_setnt(1,p,b) +#define mem_setnt2(p,b) mem_setnt(2,p,b) +#define mem_setchr1(p,b) mem_setchr(1,p,b) +#define mem_setchr2(p,b) mem_setchr(2,p,b) +#define mem_setchr4(p,b) mem_setchr(4,p,b) +#define mem_setchr8(p,b) mem_setchr(8,p,b) +#define mem_setvram1(p,b) mem_setvram(1,p,b) +#define mem_setvram2(p,b) mem_setvram(2,p,b) +#define mem_setvram4(p,b) mem_setvram(4,p,b) +#define mem_setvram8(p,b) mem_setvram(8,p,b) + +#endif diff --git a/apps/nesemu2/src/nes/movie.c b/apps/nesemu2/src/nes/movie.c new file mode 100644 index 00000000..bd74d750 --- /dev/null +++ b/apps/nesemu2/src/nes/movie.c @@ -0,0 +1,292 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "nes/nes.h" +#include "misc/memutil.h" +#include "misc/memfile.h" +#include "misc/crc32.h" +#include "misc/log.h" +#include "system/video.h" + +#define MOVIE_PREALLOC_SIZE 1024 + +static char movie_ident[] = "NMV\0"; + +int movie_init() +{ + memset(&nes->movie,0,sizeof(movie_t)); + return(0); +} + +void movie_kill() +{ + movie_unload(); +} + +int movie_load(char *filename) +{ + memfile_t *file; + u32 size; + u8 flags; + int devid[3]; + char ident[5]; + + movie_unload(); + if((file = memfile_open(filename,"rb")) == 0) { + log_printf("movie_load: error opening movie '%s'\n",filename); + return(1); + } + //need header and stuff here + memfile_read(ident,1,4,file); + if(memcmp(ident,movie_ident,4) != 0) { + log_printf("movie_load: bad movie ident\n"); + memfile_close(file); + return(2); + } + + //read movie flags + memfile_read(&flags,1,sizeof(u8),file); + if(flags & 1) + nes->movie.mode |= MOVIE_TEST; + + //set required input devices + memfile_read(&devid[0],1,sizeof(int),file); + memfile_read(&devid[1],1,sizeof(int),file); + memfile_read(&devid[2],1,sizeof(int),file); + nes_set_inputdev(0,devid[0]); + nes_set_inputdev(1,devid[1]); + nes_set_inputdev(2,devid[2]); + + //read movie data + memfile_read(&nes->movie.startframe,1,sizeof(u32),file); + memfile_read(&nes->movie.endframe,1,sizeof(u32),file); + memfile_read(&nes->movie.crc32,1,sizeof(u32),file); + memfile_read(&nes->movie.len,1,sizeof(u32),file); + log_printf("movie_load: start, end = %d, %d :: len = %d bytes\n",nes->movie.startframe,nes->movie.endframe,nes->movie.len); + nes->movie.data = (u8*)mem_alloc(nes->movie.len); + memfile_read(nes->movie.data,1,nes->movie.len,file); + size = memfile_size(file) - memfile_tell(file); + nes->movie.state = memfile_create(); + memfile_copy(nes->movie.state,file,size); + memfile_close(file); + return(0); +} + +int movie_save(char *filename) +{ + memfile_t *file; + u8 flags; + + if(filename) { + if(nes->movie.filename) + mem_free(nes->movie.filename); + nes->movie.filename = mem_strdup(filename); + } + else if(nes->movie.filename == 0) { + log_printf("movie_save: error! no filename given and movie doesnt have filename set\n"); + return(0); + } + + if(nes->movie.len == 0) { + log_printf("movie_save: no movie data to save, just setting filename\n"); + return(0); + } + if(nes->movie.mode & MOVIE_RECORD) { + log_printf("movie_save: still recording, stopping...\n"); + movie_stop(); + } + if((file = memfile_open(nes->movie.filename,"wb")) == 0) { + log_printf("movie_save: error opening movie '%s'\n",filename); + return(1); + } + memfile_seek(nes->movie.state,0,SEEK_SET); + + flags = (nes->movie.mode & MOVIE_TEST) ? 1 : 0; + + //header (just ident for now) + memfile_write(movie_ident,1,4,file); + memfile_write(&flags,1,sizeof(u8),file); + + //save input device configuration + memfile_write(&nes->inputdev[0]->id,1,sizeof(int),file); + memfile_write(&nes->inputdev[1]->id,1,sizeof(int),file); + memfile_write(&nes->expdev->id,1,sizeof(int),file); + + //save movie data + memfile_write(&nes->movie.startframe,1,sizeof(u32),file); + memfile_write(&nes->movie.endframe,1,sizeof(u32),file); + memfile_write(&nes->movie.crc32,1,sizeof(u32),file); + memfile_write(&nes->movie.len,1,sizeof(u32),file); + log_printf("movie_save: start, end = %d, %d :: len = %d bytes\n",nes->movie.startframe,nes->movie.endframe,nes->movie.len); + memfile_write(nes->movie.data,1,nes->movie.len,file); + + //append savestate to end of movie + memfile_copy(file,nes->movie.state,memfile_size(nes->movie.state)); + memfile_close(file); + return(0); +} + +void movie_unload() +{ + if(nes->movie.filename) { + movie_save(0); + mem_free(nes->movie.filename); + } + if(nes->movie.data) + mem_free(nes->movie.data); + if(nes->movie.state) + memfile_close(nes->movie.state); + memset(&nes->movie,0,sizeof(movie_t)); +} + +//process frame for movie, MUST be called after input updates +void movie_frame() +{ + if((nes->movie.mode & MOVIE_PLAY) && nes->movie.endframe == nes->ppu.frames) { + log_printf("movie_frame: reached end of movie, stopping.\n"); + movie_stop(); + } + else { + nes->inputdev[0]->movie(nes->movie.mode); + nes->inputdev[1]->movie(nes->movie.mode); + nes->expdev->movie(nes->movie.mode); + } +} + +//initialize movie data for recording +int movie_record() +{ + //get rid of previous data + if(nes->movie.data) + movie_unload(); + + //initialize the movie struct + nes->movie.mode &= ~7; + nes->movie.mode |= MOVIE_RECORD; + nes->movie.pos = 0; + nes->movie.len = MOVIE_PREALLOC_SIZE; + nes->movie.data = (u8*)mem_alloc(nes->movie.len); + nes->movie.startframe = nes->ppu.frames; + + //save state here for loading + nes->movie.state = memfile_create(); + state_save(nes->movie.state); + + return(0); +} + +//setup for playing data +int movie_play() +{ + if(nes->movie.state == 0) { + log_printf("movie_play: cannot play movie, no data loaded\n"); + return(1); + } + + memfile_seek(nes->movie.state,0,SEEK_SET); + state_load(nes->movie.state); + nes->movie.mode &= ~7; + nes->movie.mode |= MOVIE_PLAY; + nes->movie.pos = 0; + return(0); +} + +//stop recording/playing +int movie_stop() +{ + u32 crc; + + switch(nes->movie.mode & 7) { + + //movie stopped (or not loaded, or something...) + case 0: + break; + + //if recording, stop recording + case MOVIE_RECORD: + nes->movie.len = nes->movie.pos; + nes->movie.endframe = nes->ppu.frames; + nes->movie.crc32 = crc32(video_getscreen(),256 * 240); + log_printf("movie_stop: stopped recording, screen crc32 = %08X (%d frames)\n",nes->movie.crc32,nes->movie.endframe - nes->movie.startframe); + break; + + //if playing, check crcs + case MOVIE_PLAY: + crc = crc32(video_getscreen(),256 * 240); + if(crc != nes->movie.crc32) { + nes->movie.mode |= MOVIE_CRCFAIL; + log_printf("movie_stop: error: screen crc32 doesnt match! (wanted %08X, got %08X)\n",nes->movie.crc32,crc); + } + else { + nes->movie.mode |= MOVIE_CRCPASS; + log_printf("movie_stop: screen crc32 matched!\n"); + } + break; + + default: + log_printf("movie_stop: invalid movie mode (%d)\n",nes->movie.mode & 7); + } + + nes->movie.mode &= ~7; + return(0); +} + +//helpers for reading/writing movie data +u8 movie_read_u8() +{ + u8 ret; + + //make sure we are playing + if((nes->movie.mode & MOVIE_PLAY) == 0) { + log_printf("movie_read_u8: not playing, cannot read data\n"); + return(0xFF); + } + + //make sure we dont read past end of the data + if(nes->movie.pos < nes->movie.len) + ret = nes->movie.data[nes->movie.pos++]; + else { + log_printf("movie_read_u8: playing past end of movie, stopping\n"); + movie_stop(); + ret = 0xFF; + } + + return(ret); +} + +void movie_write_u8(u8 data) +{ + //make sure we are recording + if((nes->movie.mode & MOVIE_RECORD) == 0) { + log_printf("movie_write_u8: not recording, cannot write data\n"); + return; + } + + //see if we are at the end of the movie data + if(nes->movie.pos == nes->movie.len) { + nes->movie.len += MOVIE_PREALLOC_SIZE; + nes->movie.data = (u8*)mem_realloc(nes->movie.data,nes->movie.len); + log_printf("movie_write_u8: increasing size of movie buffer (%d bytes)\n",nes->movie.len); + } + + //write the byte + nes->movie.data[nes->movie.pos++] = data; +} diff --git a/apps/nesemu2/src/nes/movie.h b/apps/nesemu2/src/nes/movie.h new file mode 100644 index 00000000..c58ab511 --- /dev/null +++ b/apps/nesemu2/src/nes/movie.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__movie_h__ +#define __nes__movie_h__ + +#include "types.h" +#include "misc/memfile.h" + +//movie modes +#define MOVIE_PLAY 0x01 +#define MOVIE_RECORD 0x02 + +//flags +#define MOVIE_TEST 0x10 +#define MOVIE_CRCPASS 0x40 +#define MOVIE_CRCFAIL 0x80 + +typedef struct movie_s { + memfile_t *state; + u8 *data; + u32 len,pos; + int mode; + u32 startframe,endframe; + int port0,port1,exp; + u32 crc32; + char *filename; +} movie_t; + +int movie_init(); +void movie_kill(); +int movie_load(char *filename); +int movie_save(char *filename); +void movie_unload(); +void movie_frame(); +int movie_record(); +int movie_play(); +int movie_stop(); +u8 movie_read_u8(); +void movie_write_u8(u8 data); + +#endif diff --git a/apps/nesemu2/src/nes/nes.c b/apps/nesemu2/src/nes/nes.c new file mode 100644 index 00000000..143af2ed --- /dev/null +++ b/apps/nesemu2/src/nes/nes.c @@ -0,0 +1,317 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/log.h" +#include "misc/config.h" +#include "misc/memutil.h" +#include "cartdb/cartdb.h" +#include "nes/nes.h" +#include "nes/io.h" +#include "nes/memory.h" +#include "nes/genie.h" +#include "nes/state/state.h" +#include "nes/cart/patch/patch.h" + +nes_t *nes = 0; + +//kludge (or possibly not) +static void mapper_state(int mode,u8 *data) { nes->mapper->state(mode,data); } + +//non-kludges +static void wram_state(int mode,u8 *data) { STATE_ARRAY_U8(nes->cart->wram.data,nes->cart->wram.size); } +static void vram_state(int mode,u8 *data) +{ + STATE_ARRAY_U8(nes->cart->vram.data,nes->cart->vram.size); + if(mode == STATE_LOAD) { + cache_tiles(nes->cart->vram.data,nes->cart->vcache,nes->cart->vram.size / 16,0); + cache_tiles(nes->cart->vram.data,nes->cart->vcache_hflip,nes->cart->vram.size / 16,1); + } +} + +static int get_device_id(char *str) +{ + int ret = I_NULL; + + if(stricmp(str,"joypad0") == 0) ret = I_JOYPAD0; + if(stricmp(str,"joypad1") == 0) ret = I_JOYPAD1; + if(stricmp(str,"zapper") == 0) ret = I_ZAPPER; + if(stricmp(str,"powerpad") == 0) ret = I_POWERPAD; + return(ret); +} + +static int get_region_id(char *str) +{ + int ret = REGION_NTSC; + + if(stricmp(str,"ntsc") == 0) ret = REGION_NTSC; + if(stricmp(str,"pal") == 0) ret = REGION_PAL; + if(stricmp(str,"dendy") == 0) ret = REGION_DENDY; + return(ret); +} + +int nes_init() +{ + int ret = 0; + + nes = (nes_t*)mem_alloc(sizeof(nes_t)); + memset(nes,0,sizeof(nes_t)); + nes_set_region(REGION_NTSC); + nes_set_inputdev(0,I_NULL); + nes_set_inputdev(1,I_NULL); + nes_set_inputdev(2,I_NULL); + if((ret = state_init()) == 0) { + state_register(B_NES,nes_state); + state_register(B_MAPR,mapper_state); + state_register(B_WRAM,wram_state); + state_register(B_VRAM,vram_state); + } + + //this could be moved elsewhere + nes_set_inputdev(0,get_device_id(config_get_string("input.port0"))); + nes_set_inputdev(1,get_device_id(config_get_string("input.port1"))); + + nes_set_region(get_region_id(config_get_string("nes.region"))); + + //init all dependesnt systems + ret += cpu_init(); + ret += ppu_init(); + ret += apu_init(); + ret += movie_init(); + return(ret); +} + +void nes_kill() +{ + if(nes) { + movie_kill(); + nes_unload(); + genie_unload(); + state_kill(); + cpu_kill(); + ppu_kill(); + apu_kill(); + mem_free(nes); + nes = 0; + } +} + +int nes_load_cart(cart_t *c) +{ + mapper_t *m; + + //check mapper id + if(c->mapperid < 0) { + log_printf("nes_load_cart: cart with unsupported mapperid loaded (id = %d)\n",c->mapperid); + return(1); + } + + //try to load the mapper functions + if((m = mapper_init(c->mapperid)) == 0) { + log_printf("nes_load_cart: error calling mapper_init\n"); + return(1); + } + + //it is ready for execution...set pointers and return + nes->cart = c; + nes->mapper = m; + log_printf("nes_load_cart: success\n"); + return(0); +} + +int nes_load(char *filename) +{ + return(nes_load_patched(filename,0)); +} + +int nes_load_patched(char *filename,char *patchfilename) +{ + cart_t *c; + + //make sure cart is unload + nes_unload(); + + //try to load the file into a cart_t struct + if((c = cart_load_patched(filename,patchfilename)) == 0) { + log_printf("nes_load: error loading '%s'\n",filename); + return(1); + } + + //output some information + if(strlen(c->title)) + log_printf("nes_load: loaded file '%s' (title = '%s')\n",filename,c->title); + else + log_printf("nes_load: loaded file '%s'\n",filename); + + //check cartdb for rom (will update the cart_t struct) + if(config_get_bool("cartdb.enabled")) + cartdb_find(c); + + //see if we should pre-init some wram for the cart + if((c->battery & 1) && c->wram.size == 0) { + c->wram.size = 8; + log_printf("nes_load: cart has battery, pre-allocating wram\n"); + } + + //allocate wram + if(c->wram.size) + cart_setwramsize(c,c->wram.size); + + //see if the nes accepts it (mapper is supported) + if(nes_load_cart(c) != 0) { + cart_unload(c); + return(2); + } + + return(0); +} + +void nes_unload() +{ + movie_stop(); + //need to save sram/diskdata/whatever here + if(nes->cart) + cart_unload(nes->cart); + nes->cart = 0; + nes->mapper = 0; +} + +void nes_set_inputdev(int n,int id) +{ + if(n < 2) + nes->inputdev[n] = inputdev_get(id); + else + nes->expdev = inputdev_get(id); +} + +void nes_reset(int hard) +{ + int i; + + if(nes->cart == 0) { + log_printf("nes_reset: no cart loaded, cannot reset\n"); + return; + } + + //zero out all read/write pages/functions + for(i=0;i<64;i++) { + nes->cpu.readfuncs[i] = 0; + nes->cpu.readpages[i] = 0; + nes->cpu.writefuncs[i] = 0; + nes->cpu.writepages[i] = 0; + nes->ppu.readfuncs[i / 4] = 0; + nes->ppu.readpages[i / 4] = 0; + nes->ppu.writefuncs[i / 4] = 0; + nes->ppu.writepages[i / 4] = 0; + } + + //setup read/write funcs + mem_setreadfunc(2,ppu_read); + mem_setreadfunc(3,ppu_read); + mem_setreadfunc(4,nes_read_4000); + mem_setwritefunc(2,ppu_write); + mem_setwritefunc(3,ppu_write); + mem_setwritefunc(4,nes_write_4000); + + //set cart mirroring + mem_setmirroring(nes->cart->mirroring); + + //setup default cpu/ppu read/write funcs + cpu_setreadfunc(0); + cpu_setwritefunc(0); + ppu_setreadfunc(0); + ppu_setwritefunc(0); + + //load in the game genie if it is enabled and we doing hard reset + if(config_get_bool("nes.gamegenie.enabled") != 0) { + if(hard) + genie_load(); + } + else { + //make sure we dont have a game genie block in the state if it is disabled + genie_unload(); + } + + //reset the mapper + nes->mapper->reset(hard); + + //reset the cpu, ppu, and apu + ppu_reset(hard); + cpu_reset(hard); + apu_reset(hard); + + //clear some memory for hard reset + if(hard) { + memset(nes->cpu.ram,0,0x800); + memset(nes->ppu.nametables,0,0x800); + memset(nes->ppu.oam,0,256); + memset(nes->ppu.palette,0,32); + } +} + +void nes_frame() +{ + nes->inputdev[0]->update(); + nes->inputdev[1]->update(); + nes->expdev->update(); + if(nes->movie.mode) + movie_frame(); + cpu_execute_frame(); +} + +void nes_state(int mode,u8 *data) +{ +} + +void nes_savestate(char *filename) +{ + memfile_t *file; + + if(nes->cart == 0) { + log_printf("nes_savestate: no cart loaded, cannot save state\n"); + return; + } + if((file = memfile_open(filename,"wb")) == 0) { + log_printf("nes_savestate: error opening file '%s'\n",filename); + return; + } + log_printf("nes_savestate: saving state to '%s'\n",filename); + state_save(file); + memfile_close(file); +} + +void nes_loadstate(char *filename) +{ + memfile_t *file; + + if(nes->cart == 0) { + log_printf("nes_loadstate: no cart loaded, cannot load state\n"); + return; + } + if((file = memfile_open(filename,"rb")) == 0) { + log_printf("nes_loadstate: error opening file '%s'\n",filename); + return; + } + log_printf("nes_loadstate: loading state from '%s'\n",filename); + state_load(file); + memfile_close(file); +} diff --git a/apps/nesemu2/src/nes/nes.h b/apps/nesemu2/src/nes/nes.h new file mode 100644 index 00000000..2f2ec4ff --- /dev/null +++ b/apps/nesemu2/src/nes/nes.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes_h__ +#define __nes_h__ + +#include "types.h" +#include "nes/movie.h" +#include "nes/region.h" +#include "nes/cpu/cpu.h" +#include "nes/ppu/ppu.h" +#include "nes/apu/apu.h" +#include "nes/cart/cart.h" +#include "mappers/mappers.h" +#include "inputdev/inputdev.h" + +//irq masks +#define IRQ_TIMER 0x01 //fds +#define IRQ_DISK 0x02 //fds +#define IRQ_MAPPER 0x10 //mappers +#define IRQ_MAPPER2 0x20 //mappers +#define IRQ_FRAME 0x40 //apu +#define IRQ_DPCM 0x80 //apu + +//timing +#define LINECYCLES nes->ppu.linecycles +#define SCANLINE nes->ppu.scanline +#define FRAMES nes->ppu.frames + +//scroll registers +#define SCROLLX nes->ppu.scrollx +#define SCROLL nes->ppu.scroll +#define TMPSCROLL nes->ppu.tmpscroll +#define TOGGLE nes->ppu.toggle + +//ppu io +#define IOADDR nes->ppu.ioaddr +#define IODATA nes->ppu.iodata +#define IOMODE nes->ppu.iomode + +//registers +#define PPUCONTROL nes->ppu.control0 //'correct' names +#define PPUMASK nes->ppu.control1 +#define CONTROL0 nes->ppu.control0 //old names +#define CONTROL1 nes->ppu.control1 +#define STATUS nes->ppu.status + +typedef struct nes_s { + + //2a03/2c02 data + cpu_t cpu; + apu_t apu; + ppu_t ppu; + + //cartridge inserted + cart_t *cart; + + //mapper functions + mapper_t *mapper; + + //connected input devices + inputdev_t *inputdev[2],*expdev; + + //movie support + movie_t movie; + + //region information + region_t *region; + +} nes_t; + +extern nes_t *nes; + +int nes_init(); +void nes_kill(); +int nes_load_cart(cart_t *c); +int nes_load(char *filename); +int nes_load_patched(char *filename,char *patchfilename); +void nes_unload(); +void nes_set_inputdev(int n,int id); +void nes_reset(int hard); +void nes_frame(); +void nes_state(int mode,u8 *data); +void nes_savestate(char *filename); +void nes_loadstate(char *filename); + +#endif diff --git a/apps/nesemu2/src/nes/ppu/io.c b/apps/nesemu2/src/nes/ppu/io.c new file mode 100644 index 00000000..760dd46b --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/io.c @@ -0,0 +1,258 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "misc/log.h" +#include "nes/nes.h" +#include "system/video.h" +#include "misc/config.h" + +readfunc_t ppu_memread; +writefunc_t ppu_memwrite; + +static u8 read_ppu_memory(u32 addr) +{ + //see if a memory page is mapped + if(nes->ppu.readpages[addr >> 10]) + return(nes->ppu.readpages[addr >> 10][addr & 0x3FF]); + + //check for read function mapped + else if(nes->ppu.readfuncs[addr >> 10]) + return(nes->ppu.readfuncs[addr >> 10](addr)); + + //spit out debug message + else if(config_get_bool("nes.log_unhandled_io")) + log_printf("ppu_memread: read from unmapped memory at $%04X\n",addr); + + //return open bus + return((u8)(addr >> 8)); +} + +static void write_ppu_memory(u32 addr,u8 data) +{ + u8 page = (addr >> 10) & 0xF; + cache_t *cache,*cache_hflip; + + //check if mapped to memory pointer + if(nes->ppu.writepages[page]) { + nes->ppu.writepages[page][addr & 0x3FF] = data; + + //we have tile cache for this page, update it + cache = nes->ppu.cachepages[page]; + if(cache) { + u8 *chr = nes->ppu.readpages[page]; + u32 a = addr & 0x3F0; + + cache_hflip = nes->ppu.cachepages_hflip[page]; + cache_tile(chr + a,cache + (a / 8)); + cache_tile_hflip(chr + a,cache_hflip + (a / 8)); + } + } + + //see if write function is mapped + else if(nes->ppu.writefuncs[page]) + nes->ppu.writefuncs[page](addr,data); + + //not mapped, report error + else if(config_get_bool("nes.log_unhandled_io")) + log_printf("ppu_memwrite: write to unmapped memory at $%04X = $%02X\n",addr,data); +} + +readfunc_t ppu_getreadfunc() +{ + return(ppu_memread); +} + +writefunc_t ppu_getwritefunc() +{ + return(ppu_memwrite); +} + +void ppu_setreadfunc(readfunc_t readfunc) +{ + ppu_memread = (readfunc == 0) ? read_ppu_memory : readfunc; +} + +void ppu_setwritefunc(writefunc_t writefunc) +{ + ppu_memwrite = (writefunc == 0) ? write_ppu_memory : writefunc; +} + +u8 ppu_pal_read(u32 addr) +{ + return(nes->ppu.palette[addr]); +} + +void ppu_pal_write(u32 addr,u8 data) +{ + nes->ppu.palette[addr] = data; + video_updatepalette(addr,data); +} + +static INLINE void r2007increment() +{ + int i; + + //see if we are rendering + if((SCANLINE < 240 || SCANLINE == nes->region->end_line) && (CONTROL1 & 0x18)) { + if((SCROLL >> 12) == 7) { + SCROLL &= 0xFFF; + i = SCROLL & 0x3E0; + if(i == 0x3A0) + SCROLL ^= 0xBA0; + else if(i == 0x3E0) + SCROLL ^= 0x3E0; + else + SCROLL += 0x20; + } + else + SCROLL += 0x1000; + } + + //not rendering, increment normally + else { + if(CONTROL0 & 4) + SCROLL += 32; + else + SCROLL += 1; + SCROLL &= 0x7FFF; + } +} + +u8 ppu_read(u32 addr) +{ + u8 ret = 0; + + switch(addr & 7) { + case 2: + //bottom 5 bits come from the $2007 buffer + ret = (nes->ppu.status & 0xE0) | (nes->ppu.buf & 0x1F); + + //clear vblank flag + if(ret & 0x80) { + nes->ppu.status &= 0x60; + } + + //nmi suppression + if(SCANLINE == nes->region->vblank_start) { + if(LINECYCLES == 1) { + ret &= 0x7F; + cpu_clear_nmi(); + } + if(LINECYCLES < 4 && LINECYCLES > 1) { + cpu_clear_nmi(); + } + } + TOGGLE = 0; + nes->ppu.buf = ret; + break; + case 4: + nes->ppu.buf = nes->ppu.oam[nes->ppu.oamaddr]; + if((nes->ppu.oamaddr & 3) == 2) + nes->ppu.buf &= 0xE3; + break; + case 7: + //setup io for ppu + IOADDR = SCROLL & 0x3FFF; + IOMODE = 5; + + //increment registers + r2007increment(); + + //handle palette reads + if(IOADDR >= 0x3F00) { + nes->ppu.buf &= 0xC0; + nes->ppu.buf |= ppu_pal_read(IOADDR & 0x1F); + } + + //regular read + else + nes->ppu.buf = nes->ppu.latch; + break; + } + return(nes->ppu.buf); +} + +void ppu_write(u32 addr,u8 data) +{ + nes->ppu.buf = data; + switch(addr & 7) { + case 0: + if((STATUS & 0x80) && (data & 0x80) && ((CONTROL0 & 0x80) == 0)) + cpu_set_nmi(); + if(((data & 0x80) == 0) && (SCANLINE == nes->region->vblank_start) && (LINECYCLES < 4)) + cpu_clear_nmi(); + CONTROL0 = data; + TMPSCROLL = (TMPSCROLL & 0x73FF) | ((data & 3) << 10); + return; + case 1: + if((data & 0x18) && (SCANLINE < 240 || SCANLINE == nes->region->end_line)) + nes->ppu.rendering = 1; + else + nes->ppu.rendering = 0; + CONTROL1 = data; + return; + case 3: + nes->ppu.oamaddr = data; + return; + case 4: + //check if we are rendering + if(nes->ppu.rendering) + data = 0xFF; + nes->ppu.oam[nes->ppu.oamaddr++] = data; + return; + case 5: //scroll + if(TOGGLE == 0) { //first write + TMPSCROLL = (TMPSCROLL & 0x7FE0) | (data >> 3); + SCROLLX = data & 7; + TOGGLE = 1; + } + else { //second write + TMPSCROLL &= 0x0C1F; + TMPSCROLL |= ((data & 0xF8) << 2) | ((data & 7) << 12); + TOGGLE = 0; + } + return; + case 6: //vram addr + if(TOGGLE == 0) { //first write + TMPSCROLL = (TMPSCROLL & 0x00FF) | ((data & 0x7F) << 8); + TOGGLE = 1; + } + else { //second write + SCROLL = TMPSCROLL = (TMPSCROLL & 0x7F00) | data; + TOGGLE = 0; + } + return; + case 7: //vram data + if((SCROLL & 0x3F00) != 0x3F00) { + IOADDR = SCROLL & 0x3FFF; + IODATA = data; + IOMODE = 6; + } + else { + addr = SCROLL & 0x1F; + data &= 0x3F; + ppu_pal_write(addr,data); + if((addr & 3) == 0) + ppu_pal_write(addr ^ 0x10,data); + } + r2007increment(); + return; + } +} diff --git a/apps/nesemu2/src/nes/ppu/ppu.c b/apps/nesemu2/src/nes/ppu/ppu.c new file mode 100644 index 00000000..b0d74243 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/ppu.c @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "nes/nes.h" +#include "nes/memory.h" +#include "nes/state/state.h" + +int ppu_init() +{ + state_register(B_PPU,ppu_state); + return(0); +} + +void ppu_kill() +{ +} + +void ppu_reset(int hard) +{ + nes->ppu.scanline = 0; + nes->ppu.linecycles = 0; + nes->ppu.frames = 0; + nes->ppu.cursprite = 0; + nes->ppu.ioaddr = 0; + nes->ppu.iodata = 0; + nes->ppu.iomode = 0; + nes->ppu.rendering = 0; +} + +void ppu_sync() +{ + int i; + + //cache palette data + for(i=0;i<0x20;i++) + ppu_pal_write(i,ppu_pal_read(i)); + +} + +void ppu_state(int mode,u8 *data) +{ + STATE_U8(CONTROL0); + STATE_U8(CONTROL1); + STATE_U8(STATUS); + STATE_U16(TMPSCROLL); + STATE_U16(SCROLL); + STATE_U8(SCROLLX); + STATE_U8(TOGGLE); + STATE_U8(nes->ppu.buf); + STATE_U8(nes->ppu.latch); + STATE_U8(nes->ppu.oamaddr); + STATE_ARRAY_U8(nes->ppu.oam,256); + STATE_U32(LINECYCLES); + STATE_U32(SCANLINE); + STATE_U32(FRAMES); + STATE_U8(nes->ppu.ioaddr); + STATE_U8(nes->ppu.iodata); + STATE_U8(nes->ppu.iomode); + STATE_ARRAY_U8(nes->ppu.nametables,0x1000); + STATE_ARRAY_U8(nes->ppu.palette,32); + ppu_sync(); +} diff --git a/apps/nesemu2/src/nes/ppu/ppu.h b/apps/nesemu2/src/nes/ppu/ppu.h new file mode 100644 index 00000000..37f834f5 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/ppu.h @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__ppu_h__ +#define __nes__ppu_h__ + +#include "types.h" +#include "tilecache.h" + +typedef struct ppu_s { + + //registers + u8 control0,control1; + u8 status; + + //scroll/temp scroll/fine x scroll + u32 scroll,tmpscroll; + u8 scrollx; + + //$2005/6 flipflop + u8 toggle; + + //sprite memory address + u8 oamaddr; + + //$2007 buffer and latch + u8 buf,latch; + + //internal memory + u8 nametables[0x800 * 2]; + u8 oam[0x100]; + u8 palette[32]; + + //sprite rendering + u8 oam2[32]; + u8 oam2pos; + u8 oam2read; + u8 oam2mode; + + //ppu external io + u32 ioaddr; + u8 iodata,iomode; + + //rendering data, current address the ppu is accessing + u32 busaddr; + + //current nametable byte + u8 ntbyte; + + //is the ppu outputting pixels + u8 rendering; + + //current sprite we are fetching tile data for + u8 cursprite; + + //tile buffer (34 tiles) hold tiles/attributes read + u8 tilebuffer[256 + 16]; + + //sprite buffer holds pre-drawn sprite pixels + u8 spritebuffer[256 + 16]; + + //read/write pointers + u8 *readpages[16]; + u8 *writepages[16]; + + //read/write functions + readfunc_t readfuncs[16]; + writefunc_t writefuncs[16]; + + //cached tile pointers + cache_t *cachepages[16]; + cache_t *cachepages_hflip[16]; + + //line cycle counter, scanline counter and frame counter + u32 linecycles; + u32 scanline; + u32 frames; + +} ppu_t; + +extern readfunc_t ppu_memread; +extern writefunc_t ppu_memwrite; + +int ppu_init(); +void ppu_kill(); +void ppu_reset(int hard); +u8 ppu_read(u32 addr); +void ppu_write(u32 addr,u8 data); +u8 ppu_pal_read(u32 addr); +void ppu_pal_write(u32 addr,u8 data); +void ppu_step(); +void ppu_sync(); +void ppu_state(int mode,u8 *data); +readfunc_t ppu_getreadfunc(); +writefunc_t ppu_getwritefunc(); +void ppu_setreadfunc(readfunc_t readfunc); +void ppu_setwritefunc(writefunc_t writefunc); + +#endif diff --git a/apps/nesemu2/src/nes/ppu/step.c b/apps/nesemu2/src/nes/ppu/step.c new file mode 100644 index 00000000..0b35a0e5 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step.c @@ -0,0 +1,674 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "nes/nes.h" +#include "system/video.h" +#include "misc/log.h" + +typedef struct { + u64 line; //cache line data + u8 attr; //attrib bits + u8 x; //x coord + u8 flags; //flags + u8 tile; //sprite tile index + u8 sprline; //line of sprite bitmap to draw +} sprtemp_t; //sprite temp entry + +//todo: this needs to be moved to the ppu struct +static sprtemp_t sprtemp[8]; +static sprtemp_t *spr0 = 0; + +#include "step/calc.hh" +#include "step/fetch.hh" +#include "step/scroll.hh" +#include "step/misc.hh" +#include "step/sprite.hh" +#include "step/draw.hh" + +static INLINE void scanline_prerender() +{ + /* There are 2 conditions that update all 5 PPU scroll counters with the + contents of the latches adjacent to them. The first is after a write to + 2006/2. The second, is at the beginning of scanline 20, when the PPU starts + rendering data for the first time in a frame (this update won't happen if + all rendering is disabled via 2001.3 and 2001.4). */ + switch(LINECYCLES) { + + //the idle cycle + case 0: + nes->ppu.rendering = 1; + clear_sp0hit_flag(); + break; + + //nametable byte read + case 1: + //clear ppu status register + clear_nmi_flag(); + case 9: case 17: case 25: case 33: case 41: case 49: case 57: + case 65: case 73: case 81: case 89: case 97: case 105: case 113: case 121: + case 129: case 137: case 145: case 153: case 161: case 169: case 177: case 185: + case 193: case 201: case 209: case 217: case 225: case 233: case 241: case 249: + calc_ntaddr(); + break; + case 2: case 10: case 18: case 26: case 34: case 42: case 50: case 58: + case 66: case 74: case 82: case 90: case 98: case 106: case 114: case 122: + case 130: case 138: case 146: case 154: case 162: case 170: case 178: case 186: + case 194: case 202: case 210: case 218: case 226: case 234: case 242: case 250: + fetch_ntbyte(); + break; + + //attribute table byte calc + case 3: + clear_nmi_line(); + case 11: case 19: case 27: case 35: case 43: case 51: case 59: + case 67: case 75: case 83: case 91: case 99: case 107: case 115: case 123: + case 131: case 139: case 147: case 155: case 163: case 171: case 179: case 187: + case 195: case 203: case 211: case 219: case 227: case 235: case 243: case 251: + calc_ataddr(); + break; + + //attribute table byte fetch + case 4: fetch_atbyte(2); break; + case 12: fetch_atbyte(3); break; + case 20: fetch_atbyte(4); break; + case 28: fetch_atbyte(5); break; + case 36: fetch_atbyte(6); break; + case 44: fetch_atbyte(7); break; + case 52: fetch_atbyte(8); break; + case 60: fetch_atbyte(9); break; + case 68: fetch_atbyte(10); break; + case 76: fetch_atbyte(11); break; + case 84: fetch_atbyte(12); break; + case 92: fetch_atbyte(13); break; + case 100: fetch_atbyte(14); break; + case 108: fetch_atbyte(15); break; + case 116: fetch_atbyte(16); break; + case 124: fetch_atbyte(17); break; + case 132: fetch_atbyte(18); break; + case 140: fetch_atbyte(19); break; + case 148: fetch_atbyte(20); break; + case 156: fetch_atbyte(21); break; + case 164: fetch_atbyte(22); break; + case 172: fetch_atbyte(23); break; + case 180: fetch_atbyte(24); break; + case 188: fetch_atbyte(25); break; + case 196: fetch_atbyte(26); break; + case 204: fetch_atbyte(27); break; + case 212: fetch_atbyte(28); break; + case 220: fetch_atbyte(29); break; + case 228: fetch_atbyte(30); break; + case 236: fetch_atbyte(31); break; + case 244: fetch_atbyte(32); break; + case 252: fetch_atbyte(33); break; + + //pattern table byte 0 calc + case 5: case 13: case 21: case 29: case 37: case 45: case 53: case 61: + case 69: case 77: case 85: case 93: case 101: case 109: case 117: case 125: + case 133: case 141: case 149: case 157: case 165: case 173: case 181: case 189: + case 197: case 205: case 213: case 221: case 229: case 237: case 245: case 253: + calc_pt0addr(); + break; + + //pattern table byte 0 fetch + case 6: fetch_pt0byte(2); break; + case 14: fetch_pt0byte(3); break; + case 22: fetch_pt0byte(4); break; + case 30: fetch_pt0byte(5); break; + case 38: fetch_pt0byte(6); break; + case 46: fetch_pt0byte(7); break; + case 54: fetch_pt0byte(8); break; + case 62: fetch_pt0byte(9); break; + case 70: fetch_pt0byte(10); break; + case 78: fetch_pt0byte(11); break; + case 86: fetch_pt0byte(12); break; + case 94: fetch_pt0byte(13); break; + case 102: fetch_pt0byte(14); break; + case 110: fetch_pt0byte(15); break; + case 118: fetch_pt0byte(16); break; + case 126: fetch_pt0byte(17); break; + case 134: fetch_pt0byte(18); break; + case 142: fetch_pt0byte(19); break; + case 150: fetch_pt0byte(20); break; + case 158: fetch_pt0byte(21); break; + case 166: fetch_pt0byte(22); break; + case 174: fetch_pt0byte(23); break; + case 182: fetch_pt0byte(24); break; + case 190: fetch_pt0byte(25); break; + case 198: fetch_pt0byte(26); break; + case 206: fetch_pt0byte(27); break; + case 214: fetch_pt0byte(28); break; + case 222: fetch_pt0byte(29); break; + case 230: fetch_pt0byte(30); break; + case 238: fetch_pt0byte(31); break; + case 246: fetch_pt0byte(32); break; + case 254: fetch_pt0byte(33); break; + + //pattern table byte 1 read + case 7: case 15: case 23: case 31: case 39: case 47: case 55: case 63: + case 71: case 79: case 87: case 95: case 103: case 111: case 119: case 127: + case 135: case 143: case 151: case 159: case 167: case 175: case 183: case 191: + case 199: case 207: case 215: case 223: case 231: case 239: case 247: case 255: + calc_pt1addr(); + break; + case 8: case 16: case 24: case 32: case 40: case 48: case 56: case 64: + case 72: case 80: case 88: case 96: case 104: case 112: case 120: case 128: + case 136: case 144: case 152: case 160: case 168: case 176: case 184: case 192: + case 200: case 208: case 216: case 224: case 232: case 240: case 248:// case 256: + fetch_pt1byte(); + inc_hscroll(); + break; + case 256: + fetch_pt1byte(); + inc_hscroll(); + inc_vscroll(); + break; + + //garbage nametable address calc + case 257: +#ifdef QUICK_SPRITES + quick_process_sprites(); +#endif + update_hscroll(); + case 265: case 273: case 281: case 289: case 297: case 305: case 313: + calc_ntaddr(); + break; + + //garbage nametable fetch + case 258: case 266: case 274: case 282: case 290: case 298: case 306: case 314: + break; + + //garbage attribute address calc + case 259: case 267: case 275: case 283: case 291: case 299: case 307: case 315: + calc_ataddr(); + break; + + //garbage attribute fetch + case 260: case 268: case 276: case 284: case 292: case 300: case 308: case 316: + break; + + //calculate address of sprite pattern low + case 261: case 269: case 277: case 285: case 293: case 301: case 309: case 317: + calc_spt0addr(); + break; + + //fetch sprite pattern low + case 262: case 270: case 278: case 286: case 294: case 302: case 310: case 318: + fetch_spt0byte(); + break; + + //calculate address of sprite pattern high + case 263: case 271: case 279: case 287: case 295: case 303: case 311: case 319: + calc_spt1addr(); + break; + + //fetch sprite pattern high (update scroll registers on cycle 304) + case 304: + update_vscroll(); + case 264: + case 272: + case 280: + case 288: + case 296: + case 312: + fetch_spt1byte(); + break; + case 320: + fetch_spt1byte(); +// nes->ppu.oamaddr = 0; + break; + + //nametable byte for next scanline + case 321: case 329: + calc_ntaddr(); + break; + case 322: case 330: + fetch_ntbyte(); + break; + + //attribute byte for next scanline + case 323: case 331: + calc_ataddr(); + break; + case 324: + fetch_atbyte(0); + break; + case 332: + fetch_atbyte(1); + break; + + //pattern low byte tile + case 325: + case 333: + calc_pt0addr(); + break; + case 326: + fetch_pt0byte(0); + break; + case 334: + fetch_pt0byte(1); + break; + + //pattern high byte tile + case 327: case 335: + calc_pt1addr(); + break; + case 328: case 336: + fetch_pt1byte(); + inc_hscroll(); + break; + + //garbage nametable fetches + case 337: + calc_ntaddr(); + break; + case 338: + fetch_ntbyte(); + skip_cycle(); + break; + case 339: +// calc_ntaddr(); + break; + case 340: + fetch_ntbyte(); + break; + } +#ifndef QUICK_SPRITES + if(CONTROL1 & 0x10) + process_sprites(); +#endif +} + +//scanline 0 with rendering disabled +static INLINE void scanline_prerender_norender() +{ + switch(LINECYCLES) { + + case 0: + clear_sp0hit_flag(); + break; + case 1: + clear_nmi_flag(); + break; + case 3: + clear_nmi_line(); + break; + case 320: +// nes->ppu.oamaddr = 0; + break; + case 338: +// skip_cycle(); + break; + } +} + +//scanlines 0-239 +static INLINE void scanline_visible() +{ + switch(LINECYCLES) { + //the idle cycle + case 0: + break; + + //nametable byte read + case 1: case 9: case 17: case 25: case 33: case 41: case 49: case 57: + case 65: case 73: case 81: case 89: case 97: case 105: case 113: case 121: + case 129: case 137: case 145: case 153: case 161: case 169: case 177: case 185: + case 193: case 201: case 209: case 217: case 225: case 233: case 241: case 249: + calc_ntaddr(); + drawpixel(); + break; + case 2: case 10: case 18: case 26: case 34: case 42: case 50: case 58: + case 66: case 74: case 82: case 90: case 98: case 106: case 114: case 122: + case 130: case 138: case 146: case 154: case 162: case 170: case 178: case 186: + case 194: case 202: case 210: case 218: case 226: case 234: case 242: case 250: + fetch_ntbyte(); + drawpixel(); + break; + + //attribute table byte read + case 3: case 11: case 19: case 27: case 35: case 43: case 51: case 59: + case 67: case 75: case 83: case 91: case 99: case 107: case 115: case 123: + case 131: case 139: case 147: case 155: case 163: case 171: case 179: case 187: + case 195: case 203: case 211: case 219: case 227: case 235: case 243: case 251: + calc_ataddr(); + drawpixel(); + break; + + //attribute table byte fetch + case 4: fetch_atbyte(2); drawpixel(); break; + case 12: fetch_atbyte(3); drawpixel(); break; + case 20: fetch_atbyte(4); drawpixel(); break; + case 28: fetch_atbyte(5); drawpixel(); break; + case 36: fetch_atbyte(6); drawpixel(); break; + case 44: fetch_atbyte(7); drawpixel(); break; + case 52: fetch_atbyte(8); drawpixel(); break; + case 60: fetch_atbyte(9); drawpixel(); break; + case 68: fetch_atbyte(10); drawpixel(); break; + case 76: fetch_atbyte(11); drawpixel(); break; + case 84: fetch_atbyte(12); drawpixel(); break; + case 92: fetch_atbyte(13); drawpixel(); break; + case 100: fetch_atbyte(14); drawpixel(); break; + case 108: fetch_atbyte(15); drawpixel(); break; + case 116: fetch_atbyte(16); drawpixel(); break; + case 124: fetch_atbyte(17); drawpixel(); break; + case 132: fetch_atbyte(18); drawpixel(); break; + case 140: fetch_atbyte(19); drawpixel(); break; + case 148: fetch_atbyte(20); drawpixel(); break; + case 156: fetch_atbyte(21); drawpixel(); break; + case 164: fetch_atbyte(22); drawpixel(); break; + case 172: fetch_atbyte(23); drawpixel(); break; + case 180: fetch_atbyte(24); drawpixel(); break; + case 188: fetch_atbyte(25); drawpixel(); break; + case 196: fetch_atbyte(26); drawpixel(); break; + case 204: fetch_atbyte(27); drawpixel(); break; + case 212: fetch_atbyte(28); drawpixel(); break; + case 220: fetch_atbyte(29); drawpixel(); break; + case 228: fetch_atbyte(30); drawpixel(); break; + case 236: fetch_atbyte(31); drawpixel(); break; + case 244: fetch_atbyte(32); drawpixel(); break; + case 252: fetch_atbyte(33); drawpixel(); break; + + //pattern table byte 0 calc + case 5: case 13: case 21: case 29: case 37: case 45: case 53: case 61: + case 69: case 77: case 85: case 93: case 101: case 109: case 117: case 125: + case 133: case 141: case 149: case 157: case 165: case 173: case 181: case 189: + case 197: case 205: case 213: case 221: case 229: case 237: case 245: case 253: + calc_pt0addr(); + drawpixel(); + break; + + //pattern table byte 0 fetch + case 6: fetch_pt0byte(2); drawpixel(); break; + case 14: fetch_pt0byte(3); drawpixel(); break; + case 22: fetch_pt0byte(4); drawpixel(); break; + case 30: fetch_pt0byte(5); drawpixel(); break; + case 38: fetch_pt0byte(6); drawpixel(); break; + case 46: fetch_pt0byte(7); drawpixel(); break; + case 54: fetch_pt0byte(8); drawpixel(); break; + case 62: fetch_pt0byte(9); drawpixel(); break; + case 70: fetch_pt0byte(10); drawpixel(); break; + case 78: fetch_pt0byte(11); drawpixel(); break; + case 86: fetch_pt0byte(12); drawpixel(); break; + case 94: fetch_pt0byte(13); drawpixel(); break; + case 102: fetch_pt0byte(14); drawpixel(); break; + case 110: fetch_pt0byte(15); drawpixel(); break; + case 118: fetch_pt0byte(16); drawpixel(); break; + case 126: fetch_pt0byte(17); drawpixel(); break; + case 134: fetch_pt0byte(18); drawpixel(); break; + case 142: fetch_pt0byte(19); drawpixel(); break; + case 150: fetch_pt0byte(20); drawpixel(); break; + case 158: fetch_pt0byte(21); drawpixel(); break; + case 166: fetch_pt0byte(22); drawpixel(); break; + case 174: fetch_pt0byte(23); drawpixel(); break; + case 182: fetch_pt0byte(24); drawpixel(); break; + case 190: fetch_pt0byte(25); drawpixel(); break; + case 198: fetch_pt0byte(26); drawpixel(); break; + case 206: fetch_pt0byte(27); drawpixel(); break; + case 214: fetch_pt0byte(28); drawpixel(); break; + case 222: fetch_pt0byte(29); drawpixel(); break; + case 230: fetch_pt0byte(30); drawpixel(); break; + case 238: fetch_pt0byte(31); drawpixel(); break; + case 246: fetch_pt0byte(32); drawpixel(); break; + case 254: fetch_pt0byte(33); drawpixel(); break; + + //pattern table byte 1 read + case 7: case 15: case 23: case 31: case 39: case 47: case 55: case 63: + case 71: case 79: case 87: case 95: case 103: case 111: case 119: case 127: + case 135: case 143: case 151: case 159: case 167: case 175: case 183: case 191: + case 199: case 207: case 215: case 223: case 231: case 239: case 247: case 255: + calc_pt1addr(); + drawpixel(); + break; + case 8: case 16: case 24: case 32: case 40: case 48: case 56: case 64: + case 72: case 80: case 88: case 96: case 104: case 112: case 120: case 128: + case 136: case 144: case 152: case 160: case 168: case 176: case 184: case 192: + case 200: case 208: case 216: case 224: case 232: case 240: case 248: + fetch_pt1byte(); + drawpixel(); + inc_hscroll(); + break; + case 256: + fetch_pt1byte(); + drawpixel(); + inc_hscroll(); +// video_updateline(SCANLINE,nes->ppu.linebuffer); + inc_vscroll(); + break; + + //garbage nametable address calc + case 257: +#ifdef QUICK_SPRITES + quick_process_sprites(); +#endif + update_hscroll(); + case 265: case 273: case 281: case 289: case 297: case 305: case 313: + calc_ntaddr(); + break; + + //garbage nametable fetch + case 258: case 266: case 274: case 282: case 290: case 298: case 306: case 314: + break; + + //garbage attribute address calc + case 259: case 267: case 275: case 283: case 291: case 299: case 307: case 315: + calc_ataddr(); + break; + + //garbage attribute fetch + case 260: case 268: case 276: case 284: case 292: case 300: case 308: case 316: + break; + + //calculate address of sprite pattern low + case 261: case 269: case 277: case 285: case 293: case 301: case 309: case 317: + calc_spt0addr(); + break; + + //fetch sprite pattern low + case 262: case 270: case 278: case 286: case 294: case 302: case 310: case 318: + fetch_spt0byte(); + break; + + //calculate address of sprite pattern high + case 263: case 271: case 279: case 287: case 295: case 303: case 311: case 319: + calc_spt1addr(); + break; + + //fetch sprite pattern high + case 264: case 272: case 280: case 288: case 296: case 304: case 312: + fetch_spt1byte(); + break; + case 320: + fetch_spt1byte(); +// nes->ppu.oamaddr = 0; +#ifdef QUICK_SPRITES + quick_draw_sprite_line(); +#endif + break; + + //nametable byte for next scanline + case 321: case 329: + calc_ntaddr(); + break; + case 322: case 330: + fetch_ntbyte(); + break; + + //attribute byte for next scanline + case 323: case 331: + calc_ataddr(); + break; + case 324: + fetch_atbyte(0); + break; + case 332: + fetch_atbyte(1); + break; + + //pattern low byte tile + case 325: case 333: + calc_pt0addr(); + break; + case 326: + fetch_pt0byte(0); + break; + case 334: + fetch_pt0byte(1); + break; + + //pattern high byte tile + case 327: case 335: + calc_pt1addr(); + break; + case 328: case 336: + fetch_pt1byte(); + inc_hscroll(); + break; + + //garbage nametable fetches + case 337: + calc_ntaddr(); + break; + case 338: + fetch_ntbyte(); + break; + case 339: +// calc_ntaddr(); + break; + case 340: + fetch_ntbyte(); + break; + } +#ifndef QUICK_SPRITES + process_sprites(); + draw_sprites(); +#endif +} + +//scanlines in visible area with rendering disabled +static INLINE void scanline_visible_norender() +{ + u8 color; + + //visible pixels + if(LINECYCLES < 256) { + + //palette index 0 (with emphasis bits) + color = nes->ppu.control1 & 0xE0; + + //the 'background palette hack' (see nesdev wiki) + if((SCROLL & 0x3F00) == 0x3F00) { + color |= SCROLL & 0x1F; + } + + //draw pixel + video_updatepixel(SCANLINE,LINECYCLES,color); + } +} + +//post render scanline +static INLINE void scanline_postrender() +{ + if(LINECYCLES == 0) { + nes->ppu.rendering = 0; + } +} + +//first scanline of vblank +static INLINE void scanline_startvblank() +{ + if(LINECYCLES == 0) { + set_nmi(); + } +} + +void ppu_step() +{ + u32 addr; + + //visible scanlines + if(SCANLINE < 240) { + + //rendering is enabled + if(CONTROL1 & 0x18) { + scanline_visible(); + } + + //rendering is turned off + else { + scanline_visible_norender(); + } + } + + //post render scanline + else if(SCANLINE == 240) { + scanline_postrender(); + } + + //first scanline of vblank + else if(SCANLINE == nes->region->vblank_start) { + scanline_startvblank(); + } + + //last line in the frame + else if(SCANLINE == nes->region->end_line) { + if(CONTROL1 & 0x18) + scanline_prerender(); + else + scanline_prerender_norender(); + } + + + if(LINECYCLES & 1) { + if(IOMODE) { + addr = IOADDR & 0x3FFF; + + //call to ppucycle + if(IOMODE >= 5 && nes->ppu.rendering == 0) { + nes->ppu.busaddr = addr; + nes->mapper->ppucycle(); + } + + //perform delayed write + else if(IOMODE == 2) { + if(nes->ppu.rendering == 0) + ppu_memwrite(addr,IODATA); + } + + //perform delayed read + else if(IOMODE == 1) { + IOMODE++; + if(nes->ppu.rendering == 0) { + nes->ppu.latch = ppu_memread(addr); + } + } + IOMODE -= 2; + } + if(nes->ppu.rendering == 0) { + if(IOMODE == 0) { + nes->ppu.busaddr = SCROLL; + nes->mapper->ppucycle(); + } + } + else { + nes->mapper->ppucycle(); + } + } + inc_linecycles(); +} diff --git a/apps/nesemu2/src/nes/ppu/step/calc.hh b/apps/nesemu2/src/nes/ppu/step/calc.hh new file mode 100644 index 00000000..bb6a6688 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step/calc.hh @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//calculate nametable byte address +static INLINE void calc_ntaddr() +{ + nes->ppu.busaddr = 0x2000 | (SCROLL & 0xFFF); +} + +//calculate attribute table byte address +static INLINE void calc_ataddr() +{ + //start with current nametable address + nes->ppu.busaddr &= 0x2C00; + + //offset to attributes + nes->ppu.busaddr += 0x3C0; + + //calculate the correct attribute byte + nes->ppu.busaddr += ((SCROLL >> 2) & 7) + ((SCROLL >> 4) & 0x38); +} + +//calculate pattern table low byte address +static INLINE void calc_pt0addr() +{ + //select correct pattern table as determined by ppu control0 + nes->ppu.busaddr = (CONTROL0 & 0x10) << 8; + + //offset to the correct tile + nes->ppu.busaddr += nes->ppu.ntbyte * 16; + + //account for vertical fine scrolling + nes->ppu.busaddr += SCROLL >> 12; +} + +//calculate pattern table high byte address +static INLINE void calc_pt1addr() +{ + //go to upper bits of tile line + nes->ppu.busaddr += 8; +} + +//calculate sprite tile pattern table low byte address +static INLINE void calc_spt0addr() +{ + //process 8x16 sprite + if(CONTROL0 & 0x20) { + //bank to get tile from + nes->ppu.busaddr = (sprtemp[nes->ppu.cursprite].tile & 1) << 12; + + //tile offset + nes->ppu.busaddr += (sprtemp[nes->ppu.cursprite].tile & 0xFE) * 16; + + //vertical flip offset + nes->ppu.busaddr += (sprtemp[nes->ppu.cursprite].flags & 0x80) >> 3; + + //if this is the lower half of an 8x16 sprite + if(sprtemp[nes->ppu.cursprite].flags & 0x20) { + if(sprtemp[nes->ppu.cursprite].flags & 0x80) + nes->ppu.busaddr -= 16; + else + nes->ppu.busaddr += 16; + } + + } + + //process 8x8 sprite + else { + //bank to get tile from + nes->ppu.busaddr = (CONTROL0 & 8) << 9; + + //tile offset + nes->ppu.busaddr += sprtemp[nes->ppu.cursprite].tile * 16; + } + + //tile line offset + nes->ppu.busaddr += sprtemp[nes->ppu.cursprite].sprline * 2; +} + +//calculate sprite tile pattern table high byte address +static INLINE void calc_spt1addr() +{ + //go to upper bits of tile line + nes->ppu.busaddr += 8; +} diff --git a/apps/nesemu2/src/nes/ppu/step/draw.hh b/apps/nesemu2/src/nes/ppu/step/draw.hh new file mode 100644 index 00000000..323f8ef3 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step/draw.hh @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void drawpixel() +{ + int pos = LINECYCLES - 1; + u8 output,pixel; + + //draw background pixel if visible + if((pos >= 8 || (CONTROL1 & 2)) && (CONTROL1 & 8)) { + pixel = nes->ppu.tilebuffer[pos + nes->ppu.scrollx]; + if((pixel & 3) == 0) + pixel = 0; + output = pixel; + } + + //color used when bg isnt visible + else + output = 0; + + //draw sprite pixel with priority +#ifdef QUICK_SPRITES + if(CONTROL1 & 0x10) { + sprite0_hit_check(); + if(pos >= 8 || (CONTROL1 & 4)) { + pixel = nes->ppu.spritebuffer[pos]; + if(pixel & 3) { + if((pixel & 0x10) == 0) //foreground sprite + output = pixel | 0x10; + else if((output & 3) == 0) //background sprite that is visible + output = pixel | 0x10; + } + } + } +#else + #error QUICK_SPRITES must be defined. +#endif + + //apply color emphasis + output |= nes->ppu.control1 & 0xE0; + + //output pixel to the renderer + video_updatepixel(SCANLINE,pos,output); +} + +static INLINE void quick_draw_sprite_line() +{ + sprtemp_t *spr = (sprtemp_t*)sprtemp + 7; + u64 *spriteline64 = (u64*)nes->ppu.spritebuffer; + int n; + + //clear sprite line + for(n=0;n<(256 / 8);n++) + spriteline64[n] = 0; + + //loop thru all eight possible sprites + for(n=0;n<8;n++,spr--) { + cache_t offs; + u64 *scr64,sp0,sp1,pmask0,pmask1; + u64 shiftright,shiftleft; + + if(spr->flags == 0) + continue; + + //calculate shifting amounts for aligned writes + shiftright = (8 - (spr->x & 7)) * 8; + shiftleft = (spr->x & 7) * 8; + + //get offset in sprite buffer + scr64 = ((u64*)nes->ppu.spritebuffer) + (spr->x / 8); + + //setup to draw the pixel + offs = spr->attr * 0x0404040404040404LL; + sp0 = sp1 = spr->line; + pmask0 = pmask1 = (0x8080808080808080LL - sp0) >> 2; + + //render pixel to sprite line buffer +#ifdef __GNUC__ + if(spr->x & 7) { +#endif + sp0 <<= shiftleft; + sp1 >>= shiftright; + pmask0 <<= shiftleft; + pmask1 >>= shiftright; + scr64[0] = ((sp0 + offs) & pmask0) | (scr64[0] & ~pmask0); + scr64[1] = ((sp1 + offs) & pmask1) | (scr64[1] & ~pmask1); +#ifdef __GNUC__ + } + else + scr64[0] = ((sp0 + offs) & pmask0) | (scr64[0] & ~pmask0); +#endif + } +} diff --git a/apps/nesemu2/src/nes/ppu/step/fetch.hh b/apps/nesemu2/src/nes/ppu/step/fetch.hh new file mode 100644 index 00000000..42621e71 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step/fetch.hh @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void fetch_ntbyte() +{ + //read byte from ppu memory area + nes->ppu.ntbyte = ppu_memread(nes->ppu.busaddr); +} + +static INLINE void fetch_atbyte(int tilenum) +{ + u32 tmp; + + //get attribute byte + tmp = ppu_memread(nes->ppu.busaddr); + + //calculate which set of bits to use for attributes here + tmp = ((tmp >> (((SCROLL & 2) | (((SCROLL >> 5) & 2) << 1)))) & 3); + + //put attributes into the line buffer + ((u64*)nes->ppu.tilebuffer)[tilenum] = tmp * 0x0404040404040404LL; +} + +static INLINE void fetch_pt0byte(int tilenum) +{ + cache_t *cache,pixels; + + //perform the read, but throw the data away + ppu_memread(nes->ppu.busaddr); + + //tile bank cache pointer + cache = nes->ppu.cachepages[(nes->ppu.ntbyte >> 6) | ((CONTROL0 & 0x10) >> 2)]; + + //index to the tile data start, then the tile half (upper or lower half) + cache += ((nes->ppu.ntbyte & 0x3F) * 2) + ((SCROLL >> 14) & 1); + + //retreive the tile pixels used + pixels = *cache >> (((SCROLL >> 12) & 3) << 1); + + //mask off the pixels we need + pixels &= CACHE_MASK; + + //add the pixels to the line buffer + ((u64*)nes->ppu.tilebuffer)[tilenum] += pixels; +} + +static INLINE void fetch_pt1byte() +{ + //perform the read, but throw the data away + ppu_memread(nes->ppu.busaddr); +} + +static INLINE void fetch_spt0byte() +{ + cache_t *cache; + + //perform the read, but throw the data away + ppu_memread(nes->ppu.busaddr); + + //get cache bank used by sprite tile + if(sprtemp[nes->ppu.cursprite].flags & 0x40) + cache = nes->ppu.cachepages_hflip[(nes->ppu.busaddr >> 10) & 7]; + else + cache = nes->ppu.cachepages[(nes->ppu.busaddr >> 10) & 7]; + + //offset to the current tile line + cache += (nes->ppu.busaddr & 0x3FF) / 8; + + //store sprite tile line + sprtemp[nes->ppu.cursprite].line = *cache >> (nes->ppu.busaddr & 6); + sprtemp[nes->ppu.cursprite].line &= CACHE_MASK; +} + +static INLINE void fetch_spt1byte() +{ + //perform the read, but throw the data away + ppu_memread(nes->ppu.busaddr); + + //increase our sprite pointer + nes->ppu.cursprite++; +} diff --git a/apps/nesemu2/src/nes/ppu/step/misc.hh b/apps/nesemu2/src/nes/ppu/step/misc.hh new file mode 100644 index 00000000..16424a72 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step/misc.hh @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void inc_linecycles() +{ + LINECYCLES++; + if(LINECYCLES >= 341) { + LINECYCLES = 0; + SCANLINE++; + if(SCANLINE > nes->region->end_line) { + SCANLINE = 0; + FRAMES++; + } + } +} + +static INLINE void skip_cycle() +{ + //ensure we are not in pal mode + if((nes->region->id & REGION_PAL) == 0) { + + //make sure we are on an odd frame and that the ppu is outputting pixels + if((FRAMES & 1) && nes->ppu.rendering) { + inc_linecycles(); + } + } +} + +static INLINE void clear_sp0hit_flag() +{ + STATUS &= ~(0x40 | 0x20); +} + +static INLINE void clear_nmi_flag() +{ + STATUS &= ~0x80; +} + +static INLINE void clear_nmi_line() +{ + cpu_clear_nmi(); +} + +static INLINE void set_nmi() +{ + STATUS |= 0x80; + if(CONTROL0 & 0x80) + cpu_set_nmi(); +} diff --git a/apps/nesemu2/src/nes/ppu/step/scroll.hh b/apps/nesemu2/src/nes/ppu/step/scroll.hh new file mode 100644 index 00000000..9ab9b440 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step/scroll.hh @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +static INLINE void inc_hscroll() +{ + /* The first one, the horizontal scroll counter, consists of 6 bits, and is + made up by daisy-chaining the HT counter to the H counter. The HT counter is + then clocked every 8 pixel dot clocks (or every 8/3 CPU clock cycles). */ + if((SCROLL & 0x1F) == 0x1F) { //see if HT counter creates carry + SCROLL ^= 0x41F; //yes, clear lower 5 bits and toggle H counter + } + else + SCROLL++; //no, increment address +} + +static INLINE void inc_vscroll() +{ + int n; + + //update y coordinate + if((SCROLL >> 12) == 7) { + SCROLL &= 0xFFF; + n = SCROLL & 0x3E0; + if(n == 0x3A0) + SCROLL ^= 0xBA0; + else if(n == 0x3E0) + SCROLL ^= 0x3E0; + else + SCROLL += 0x20; + } + else + SCROLL += 0x1000; +} + +static INLINE void update_hscroll() +{ + SCROLL &= ~0x041F; + SCROLL |= TMPSCROLL & 0x041F; + nes->ppu.cursprite = 0; +} + +static INLINE void update_vscroll() +{ + SCROLL &= ~0x7BE0; + SCROLL |= TMPSCROLL & 0x7BE0; +} + +static INLINE void update_scroll() +{ + SCROLL = TMPSCROLL; +} diff --git a/apps/nesemu2/src/nes/ppu/step/sprite.hh b/apps/nesemu2/src/nes/ppu/step/sprite.hh new file mode 100644 index 00000000..c6bb9718 --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/step/sprite.hh @@ -0,0 +1,252 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef QUICK_SPRITES + +#define OAM2 nes->ppu.oam2 +#define OAM2POS nes->ppu.oam2pos +#define OAM2MODE nes->ppu.oam2mode +#define OAM2READ nes->ppu.oam2read + +static INLINE void process_sprites() +{ + u32 tmp; + + if(LINECYCLES == 0) { + OAM2POS = 0; + OAM2MODE = 0; + } + + /* Cycles 1-64: Secondary OAM (32-byte buffer for current sprites on scanline) is + initialized to $FF - attempting to read $2004 will return $FF */ + else if(LINECYCLES <= 64) { + if(LINECYCLES & 1) + OAM2[OAM2POS++] = 0xFF; + } + + /* Cycles 65-256: Sprite evaluation + On odd cycles, data is read from (primary) OAM + On even cycles, data is written to secondary OAM (unless writes are inhibited, in + which case it will read the value in secondary OAM instead) + 1. Starting at n = 0, read a sprite's Y-coordinate (OAM[n][0], copying it to the + next open slot in secondary OAM (unless 8 sprites have been found, in which case the write is ignored). + 1a. If Y-coordinate is in range, copy remaining bytes of sprite data (OAM[n][1] thru + OAM[n][3]) into secondary OAM. + 2. Increment n + 2a. If n has overflowed back to zero (all 64 sprites evaluated), go to 4 + 2b. If less than 8 sprites have been found, go to 1 + 2c. If exactly 8 sprites have been found, disable writes to secondary OAM. This causes sprites in back to drop out. + 3. Starting at m = 0, evaluate OAM[n][m] as a Y-coordinate. + 3a. If the value is in range, set the sprite overflow flag in $2002 and read the next 3 entries + of OAM (incrementing 'm' after each byte and incrementing 'n' when 'm' overflows); if m = 3, increment n + 3b. If the value is not in range, increment n AND m (without carry). If n overflows to 0, go to 4; otherwise go to 3 + 4. Attempt (and fail) to copy OAM[n][0] into the next free slot in secondary OAM, and increment n (repeat until HBLANK is reached) */ + else if(LINECYCLES <= 256) { + //read sprite y coord + if(OAM2MODE == 0) { + if(LINECYCLES & 1) { + //read byte from oam + OAM2READ = nes->ppu.oam[nes->ppu.oamaddr]; + } + else { + //store y coordinate into oam2 + OAM2[OAM2POS] = OAM2READ; + + //calculate sprite height + tmp = OAM2READ + (8 << ((CONTROL0 & 0x20) >> 5)); + + //check if sprite line is visible here + if(SCANLINE >= OAM2READ && SCANLINE < tmp) { + + //change mode to fetch the read of teh sprite data + OAM2MODE++; + + //increment oam2 offset + OAM2POS++; + } + + //sprite not visible on this line + else { + + } + } + } + + //fetching the rest of the sprite data + else if(OAM2MODE == 1) { + if(LINECYCLES & 1) { + //read byte from oam + OAM2READ = nes->ppu.oam[nes->ppu.oamaddr++]; + } + else { + //store byte into oam2 + OAM2[OAM2POS++] = OAM2READ; + + //see if this is the last fetch for this sprite + if((OAM2POS & 3) == 0) { + + //if this is the 8th sprite, go into the strange evaluation mode + if(OAM2POS == 32) + OAM2MODE++; + + //otherwise fetch next sprite + else + OAM2MODE = 0; + } + } + } + + //strange sprite evaluation + else if(OAM2MODE == 2) { + + } + } + + /* Cycles 257-320: Sprite fetches (8 sprites total, 8 cycles per sprite) + 1-4: Read the Y-coordinate, tile number, attributes, and X-coordinate + of the selected sprite from secondary OAM + 5-8: Read the X-coordinate of the selected sprite from secondary OAM + 4 times (while the PPU fetches the sprite tile data) + For the first empty sprite slot, this will consist of sprite #63's Y-coordinate + followed by 3 $FF bytes; for subsequent empty sprite slots, this will be four $FF bytes */ + else if(LINECYCLES <= 320) { + + } + + /* Cycles 321-340+0: Background render pipeline initialization + Read the first byte in secondary OAM (while the PPU fetches + the first two background tiles for the next scanline) */ + else { + + } +} + +static INLINE draw_sprites() +{ + +} + +#else + +//process all sprites that belong to the next scanline +static INLINE void quick_process_sprites() +{ + int i,h,sprinrange,sprline; + u8 *s; + int line = SCANLINE == nes->region->end_line ? -1 : SCANLINE; //kludge + + //clear the sprite temp memory + for(i=0;i<8;i++) { + sprtemp[i].line = 0; + sprtemp[i].attr = sprtemp[i].x = sprtemp[i].flags = 0; + sprtemp[i].tile = 0xFF; + sprtemp[i].sprline = 0; + } + + //if sprites disabled, return +// if((CONTROL1 & 0x10) == 0) +// return; + + //determine sprite height + h = 8 + ((CONTROL0 & 0x20) >> 2); + + spr0 = 0; + + //loop thru all 64 visible sprites, keeping note of the first eight visible + for(sprinrange=0,i=0;i<64;i++) { + + //sprite data pointer + s = &nes->ppu.oam[i * 4]; + + //get the sprite tile line to draw + sprline = line - s[0]; + + //check for visibility on this line + if(sprline >= 0 && sprline < h) { + + //if more than 8 sprites are found, set the flag and exit the loop + if(sprinrange == 8) { + STATUS |= 0x20; + break; + } + + //copy sprite data to temp memory + sprtemp[sprinrange].attr = (s[2] & 3) | ((s[2] & 0x20) >> 3); + sprtemp[sprinrange].x = s[3]; + sprtemp[sprinrange].flags = 1 | (s[2] & 0xC0); + sprtemp[sprinrange].tile = s[1]; + + //if sprite0 check is needed + if(i == 0 && (STATUS & 0x40) == 0) { + sprtemp[sprinrange].flags |= 2; + spr0 = &sprtemp[sprinrange]; + } + + //small kludge for 8x16 sprites + if(CONTROL0 & 0x20) { + if(sprline >= 8) { + sprtemp[sprinrange].flags |= 0x20; + sprline &= 7; + } + } + + //if sprite is to be flipped vertically + if((s[2] & 0x80) != 0) + sprline = 7 - sprline; + + //save sprite tile line + sprtemp[sprinrange].sprline = sprline; + + //increment sprite in range counter + sprinrange++; + + } + } +} + +static INLINE void sprite0_hit_check() +{ + if(spr0 != 0) { + u8 *dest = nes->ppu.tilebuffer; + u8 *line; + int xpos; + int x = LINECYCLES - 1; + + //if background is disabled, always miss + if((CONTROL1 & 8) == 0) + return; + //if background is not visible in left 8 pixels, always miss + if(x < 8 && (CONTROL1 & 2) == 0) + return; + xpos = x - spr0->x; + if(xpos >= 0 && xpos < 8) { + if(((CONTROL1 & 4) == 0 && spr0->x == 0) || spr0->x == 255 || x == 255) + return; + dest += x; + line = (u8*)&spr0->line; + if(*dest && line[xpos]) { + STATUS |= 0x40; + spr0 = 0; + } + } + } +} + +#endif diff --git a/apps/nesemu2/src/nes/ppu/tilecache.c b/apps/nesemu2/src/nes/ppu/tilecache.c new file mode 100644 index 00000000..e6d5b43d --- /dev/null +++ b/apps/nesemu2/src/nes/ppu/tilecache.c @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "nes/ppu/tilecache.h" + +//blargg's wonderful cache scheme, found on nesdev forums + +// Expands each of the 8 bits in n into separate bytes of result. +// In: 12345678 Out: 0x87654321 +static INLINE u64 expand(u8 n) +{ + u64 ret = 0; + + ret |= (u64)((n >> 7) & 1) << 0; + ret |= (u64)((n >> 6) & 1) << 8; + ret |= (u64)((n >> 5) & 1) << 16; + ret |= (u64)((n >> 4) & 1) << 24; + ret |= (u64)((n >> 3) & 1) << 32; + ret |= (u64)((n >> 2) & 1) << 40; + ret |= (u64)((n >> 1) & 1) << 48; + ret |= (u64)((n >> 0) & 1) << 56; + return(ret); +} + +// Expands each of the 8 bits in n into separate bytes of result. +// In: 12345678 Out: 0x87654321 +static INLINE u64 expand_hflip(u8 n) +{ + u64 ret = 0; + + ret |= (u64)((n >> 0) & 1) << 0; + ret |= (u64)((n >> 1) & 1) << 8; + ret |= (u64)((n >> 2) & 1) << 16; + ret |= (u64)((n >> 3) & 1) << 24; + ret |= (u64)((n >> 4) & 1) << 32; + ret |= (u64)((n >> 5) & 1) << 40; + ret |= (u64)((n >> 6) & 1) << 48; + ret |= (u64)((n >> 7) & 1) << 56; + return(ret); +} + +// convert one chr tile to a cached tile +void cache_tile(u8 *chr,cache_t *cache) +{ + int n; + + for(n=2;n--;) { + *cache++ = (expand(chr[ 0]) << 0) | + (expand(chr[ 8]) << 1) | + (expand(chr[ 1]) << 2) | + (expand(chr[ 9]) << 3) | + (expand(chr[ 2]) << 4) | + (expand(chr[10]) << 5) | + (expand(chr[ 3]) << 6) | + (expand(chr[11]) << 7); + chr += 4; + } +} + +// convert one chr tile to a cached tile, horizontally flipped +void cache_tile_hflip(u8 *chr,cache_t *cache) +{ + int n; + + for(n=2;n--;) { + *cache++ = (expand_hflip(chr[ 0]) << 0) | + (expand_hflip(chr[ 8]) << 1) | + (expand_hflip(chr[ 1]) << 2) | + (expand_hflip(chr[ 9]) << 3) | + (expand_hflip(chr[ 2]) << 4) | + (expand_hflip(chr[10]) << 5) | + (expand_hflip(chr[ 3]) << 6) | + (expand_hflip(chr[11]) << 7); + chr += 4; + } +} + +void cache_tiles(u8 *chr,cache_t *cache,int num,int hflip) +{ + int n; + + for(n=0;nregion = ®ion_ntsc; + break; + case REGION_PAL: + nes->region = ®ion_pal; + break; + case REGION_DENDY: + nes->region = ®ion_dendy; + break; + } + apu_set_region(r); +} diff --git a/apps/nesemu2/src/nes/region.h b/apps/nesemu2/src/nes/region.h new file mode 100644 index 00000000..cec77e72 --- /dev/null +++ b/apps/nesemu2/src/nes/region.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __nes__region_h__ +#define __nes__region_h__ + +//regions +#define REGION_NTSC 0 +#define REGION_PAL 1 +#define REGION_DENDY 2 + +typedef struct region_s { + + //region id + u32 id; + + //frames per second + u32 fps; + + //master clock + u32 hz; + + //vblank start line + u32 vblank_start; + + //last line + u32 end_line; +} region_t; + +extern region_t region_ntsc; +extern region_t region_pal; +extern region_t region_dendy; + +void nes_set_region(int r); + +#endif diff --git a/apps/nesemu2/src/nes/state/block.c b/apps/nesemu2/src/nes/state/block.c new file mode 100644 index 00000000..4842d397 --- /dev/null +++ b/apps/nesemu2/src/nes/state/block.c @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/memutil.h" +#include "misc/log.h" +#include "nes/state/block.h" + +block_t *block_create(u32 type,u32 size) +{ + block_t *ret = (block_t*)mem_alloc(sizeof(block_t)); + + ret->type = type; + ret->size = size; + ret->data = (u8*)mem_alloc(size); + memset(ret->data,0,size); + return(ret); +} + +void block_destroy(block_t *b) +{ + mem_free(b->data); + mem_free(b); +} + +block_t *block_load(memfile_t *file) +{ + block_t *ret = 0; + u32 type; + u32 size; + + if(memfile_read(&type,1,4,file) != 4) { + log_printf("block_load: error reading block type\n"); + return(0); + } + if(memfile_read(&size,1,4,file) != 4) { + log_printf("block_load: error reading block size\n"); + return(0); + } + ret = block_create(type,size); + if(memfile_read(ret->data,1,size,file) != size) { + log_printf("block_load: error reading block data\n"); + block_destroy(ret); + return(0); + } + return(ret); +} + +int block_save(memfile_t *file,block_t *b) +{ + if(memfile_write(&b->type,1,4,file) != 4) { + log_printf("block_save: error writing block type\n"); + return(-1); + } + if(memfile_write(&b->size,1,4,file) != 4) { + log_printf("block_save: error writing block size\n"); + return(-1); + } + if(memfile_write(b->data,1,b->size,file) != b->size) { + log_printf("block_save: error writing block data\n"); + return(-1); + } + return(b->size + 8); +} diff --git a/apps/nesemu2/src/nes/state/block.h b/apps/nesemu2/src/nes/state/block.h new file mode 100644 index 00000000..23bd20c4 --- /dev/null +++ b/apps/nesemu2/src/nes/state/block.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __blocks_h__ +#define __blocks_h__ + +#include "misc/memfile.h" + +#define MAKEID(c1,c2,c3,c4) (((c1) << 0) | ((c2) << 8) | ((c3) << 16) | ((c4) << 24)) + +//if state data is gzipped +#define STATE_FLAG_GZIP 0x8000 + +//block stored in memory +typedef struct block_s { + u32 type; //block type + u32 size; //block size + u8 *data; //block data +} block_t; + +block_t *block_create(u32 type,u32 size); +void block_destroy(block_t *b); +block_t *block_load(memfile_t *file); +int block_save(memfile_t *file,block_t *b); + +#endif diff --git a/apps/nesemu2/src/nes/state/state.c b/apps/nesemu2/src/nes/state/state.c new file mode 100644 index 00000000..a02a9292 --- /dev/null +++ b/apps/nesemu2/src/nes/state/state.c @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/log.h" +#include "nes/nes.h" +#include "nes/state/state.h" +#include "nes/state/block.h" + +#define readvar(var,sz) \ + if(memfile_read(&var,1,sz,file) != sz) { \ + log_printf("state_load: error reading " #var "\n"); \ + return(1); \ + } + +#define writevar(var,sz) \ + if(memfile_write(&var,1,sz,file) != sz) { \ + log_printf("state_save: error writing " #var "\n"); \ + return(1); \ + } + +typedef struct blockfunc_s { + u32 type; + u32 size; + void (*func)(int,u8*); +} blockfunc_t; + +static blockfunc_t blockinfo[16]; +static u32 ident = MAKEID('N','S','T','\0'); +static u32 version = 0x0100; + +static char *makestr(u32 u) +{ + static char str[5]; + + str[0] = (u8)(u >> 24); + str[1] = (u8)(u >> 16); + str[2] = (u8)(u >> 8); + str[3] = (u8)(u >> 0); + str[4] = 0; + return(str); +} + +int state_init() +{ + memset(blockinfo,0,sizeof(blockfunc_t) * 16); + return(0); +} + +void state_kill() +{ +} + +void state_register(u32 type,void (*func)(int,u8*)) +{ + int i; + + for(i=0;i<16;i++) { + if(blockinfo[i].type == type) { + log_printf("state_register: type '%08X' already registered\n",type); + return; + } + if(blockinfo[i].type == 0) + break; + } + blockinfo[i].type = type; + blockinfo[i].func = func; +} + +void state_unregister(u32 type) +{ + int i; + + for(i=0;i<16;i++) { + if(blockinfo[i].type == type) { + blockinfo[i].type = 0; + blockinfo[i].func = 0; + break; + } + } +} + +statefunc_t state_getfunc(u32 type) +{ + int i; + + for(i=0;i<16;i++) { + if(blockinfo[i].type == type) { + return(blockinfo[i].func); + } + } + return(0); +} + +int state_load(memfile_t *file) +{ + stateheader_t header; + block_t *block; + u32 size = 0; + int i; + + readvar(header.ident,4); + readvar(header.version,2); + readvar(header.flags,2); + readvar(header.usize,4); + readvar(header.csize,4); + readvar(header.crc32,4); + log_printf("state_load: state header loaded. version %04X\n",header.version); + + while(memfile_eof(file) == 0 && size < header.usize) { + if((block = block_load(file)) == 0) + break; + size += 8 + block->size; + log_printf("state_load: loaded block '%4s' (%08X) (%d bytes)\n", makestr(block->type),block->type,block->size); + for(i=0;blockinfo[i].type;i++) { + if(blockinfo[i].type == block->type) { + blockinfo[i].func(STATE_LOAD,block->data); + break; + } + } + if(blockinfo[i].type == 0) { + log_printf("state_load: no handler for block type '%4s' (%d bytes)\n", makestr(block->type),block->size); + } + block_destroy(block); + } + + return(0); +} + +int state_save(memfile_t *file) +{ + stateheader_t header; + block_t *block; + int i; + + //clear the state info + memset(&header,0,sizeof(stateheader_t)); + + //set the ident/version of the state header + header.ident = ident; + header.version = version; + + //calculate total data size + for(i=0;blockinfo[i].type;i++) { + blockinfo[i].size = 0; + blockinfo[i].func(STATE_SIZE,(u8*)&blockinfo[i].size); + if(blockinfo[i].size) { + header.usize += blockinfo[i].size + 8; + } + } + + //write the state header + writevar(header.ident,4); + writevar(header.version,2); + writevar(header.flags,2); + writevar(header.usize,4); + writevar(header.csize,4); + writevar(header.crc32,4); + + //write each block + for(i=0;blockinfo[i].type;i++) { + if(blockinfo[i].size == 0) + continue; + log_printf("saving block '%4s' (%d bytes)\n", makestr(blockinfo[i].type),blockinfo[i].size); + block = block_create(blockinfo[i].type,blockinfo[i].size); + blockinfo[i].func(STATE_SAVE,block->data); + block_save(file,block); + block_destroy(block); + } + + return(0); +} diff --git a/apps/nesemu2/src/nes/state/state.h b/apps/nesemu2/src/nes/state/state.h new file mode 100644 index 00000000..7db013b0 --- /dev/null +++ b/apps/nesemu2/src/nes/state/state.h @@ -0,0 +1,166 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __state_h__ +#define __state_h__ + +#include +#include "nes/state/block.h" + +#define STATE_LOAD 0 //load state +#define STATE_SAVE 1 //save state +#define STATE_SIZE 2 //get state size +#define CFG_LOAD 4 //load config +#define CFG_SAVE 5 //save config +#define CFG_SIZE 6 //get config size + +#define CFG_U8(dat) \ + if(mode == CFG_LOAD) \ + {(dat) = *data++;} \ + else if(mode == CFG_SAVE) \ + {*data++ = (dat);} + +#define STATE_U8(dat) \ + if(mode == STATE_LOAD) \ + {(dat) = *data++;} \ + else if(mode == STATE_SAVE) \ + {*data++ = (dat);} \ + else if(mode == STATE_SIZE) \ + {*((u32*)data) += 1;} + +#define STATE_U16(dat) \ + if(mode == STATE_LOAD) { \ + (dat) = *data++; \ + (dat) |= *data++ << 8; \ + } \ + else if(mode == STATE_SAVE) { \ + *data++ = (u8)((dat) & 0xFF); \ + *data++ = (u8)(((dat) >> 8) & 0xFF); \ + } \ + else if(mode == STATE_SIZE) \ + {*((u32*)data) += 2;} + +#define STATE_U32(dat) \ + if(mode == STATE_LOAD) { \ + (dat) = *data++; \ + (dat) |= *data++ << 8; \ + (dat) |= *data++ << 16; \ + (dat) |= *data++ << 24; \ + } \ + else if(mode == STATE_SAVE) { \ + *data++ = (u8)((dat) & 0xFF); \ + *data++ = (u8)(((dat) >> 8) & 0xFF); \ + *data++ = (u8)(((dat) >> 16) & 0xFF); \ + *data++ = (u8)(((dat) >> 24) & 0xFF); \ + } \ + else if(mode == STATE_SIZE) \ + {*((u32*)data) += 4;} + +#define STATE_U64(dat) \ + if(mode == STATE_LOAD) { \ + (dat) = (u64)*data++; \ + (dat) |= (u64)*data++ << 8; \ + (dat) |= (u64)*data++ << 16; \ + (dat) |= (u64)*data++ << 24; \ + (dat) |= (u64)*data++ << 32; \ + (dat) |= (u64)*data++ << 40; \ + (dat) |= (u64)*data++ << 48; \ + (dat) |= (u64)*data++ << 56; \ + } \ + else if(mode == STATE_SAVE) { \ + *data++ = (u8)((dat) & 0xFF); \ + *data++ = (u8)(((dat) >> 8) & 0xFF); \ + *data++ = (u8)(((dat) >> 16) & 0xFF); \ + *data++ = (u8)(((dat) >> 24) & 0xFF); \ + *data++ = (u8)(((dat) >> 32) & 0xFF); \ + *data++ = (u8)(((dat) >> 40) & 0xFF); \ + *data++ = (u8)(((dat) >> 48) & 0xFF); \ + *data++ = (u8)(((dat) >> 56) & 0xFF); \ + } \ + else if(mode == STATE_SIZE) \ + {*((u32*)data) += 8;} + +#define STATE_INT(dat) \ + if(mode == STATE_LOAD) { \ + u32 _tmpu32; \ + _tmpu32 = *data++; \ + _tmpu32 |= *data++ << 8; \ + _tmpu32 |= *data++ << 16; \ + _tmpu32 |= *data++ << 24; \ + dat = (int)_tmpu32; \ + } \ + else if(mode == STATE_SAVE) { \ + *data++ = (u8)(u32)(dat); \ + *data++ = (u8)((u32)(dat) >> 8); \ + *data++ = (u8)((u32)(dat) >> 16); \ + *data++ = (u8)((u32)(dat) >> 24); \ + } \ + else if(mode == STATE_SIZE) \ + *((u32*)data) += 4; + +#define STATE_ARRAY_U8(arr,siz) { \ + u32 i; \ + for(i=0;i<(siz);i++) \ + STATE_U8((arr)[i]); \ + } + +#define STATE_ARRAY_U16(arr,siz) { \ + u32 i; \ + for(i=0;i<(siz);i++) \ + STATE_U16((arr)[i]); \ + } + +//state block types +enum blocktype_e { + B_UNKNOWN = 0, + B_NES = MAKEID('N', 'E', 'S', '\0'), + B_CPU = MAKEID('C', 'P', 'U', '\0'), + B_PPU = MAKEID('P', 'P', 'U', '\0'), + B_APU = MAKEID('A', 'P', 'U', '\0'), + B_MAPR = MAKEID('M', 'A', 'P', 'R' ), + B_VRAM = MAKEID('V', 'R', 'A', 'M' ), + B_SVRAM = MAKEID('B', 'R', 'A', 'M' ), + B_WRAM = MAKEID('W', 'R', 'A', 'M' ), + B_SRAM = MAKEID('S', 'R', 'A', 'M' ), + B_DISK = MAKEID('D', 'I', 'S', 'K' ), + B_GG = MAKEID('G', 'G', '\0','\0'), + B_PATCH = MAKEID('P', 'A', 'T', '\0'), +}; + +//state header as stored on disk +typedef struct stateheader_s { + u32 ident; //state ident + u16 version; //state version + u16 flags; //flags + u32 usize,csize; //uncompressed/compressed size + u32 crc32; //crc32 of uncompressed data +} stateheader_t; + +typedef void (*statefunc_t)(int,u8*); + +int state_init(); +void state_kill(); +void state_register(u32 type,statefunc_t func); +void state_unregister(u32 type); +statefunc_t state_getfunc(u32 type); +int state_load(memfile_t *file); +int state_save(memfile_t *file); + +#endif diff --git a/apps/nesemu2/src/palette/generator.c b/apps/nesemu2/src/palette/generator.c new file mode 100644 index 00000000..94fd501e --- /dev/null +++ b/apps/nesemu2/src/palette/generator.c @@ -0,0 +1,171 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//palette generator borrowed from nintendulator + +#include +#include "palette/palette.h" + +#ifndef M_PI +#define M_PI 3.14159f +#endif + +#define CLIP(x,min,max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) + +static int getPhase (double *wave) +{ + double max = -999, min = 999; + double amp, offset; + double angle = 0, base; + int i, j, k; + for (i = 0; i < 12; i++) + { + if (wave[i] > max) + max = wave[i]; + if (wave[i] < min) + min = wave[i]; + } + amp = (max - min) / 2; + offset = (max + min) / 2; + + for (k = 0; k < 3; k++) + { + double error[12], curerror = 0; + double segsize = 360; + for (i = 0; i <= k; i++) + segsize /= 12.0; + + for (j = 0; j < 12; j++) + { + error[j] = 0; + for (i = 0; i < 12; i++) + error[j] += fabs((amp * sin((i * 30 + j * segsize + angle) * M_PI / 180.0) + offset) - wave[i]); + curerror += error[j]; + } + base = 0; + for (j = 0; j < 12; j++) + { + if (error[j] < curerror) + { + base = j * segsize; + curerror = error[j]; + } + } + angle += base; + } + + return (int)angle; +} + +static void GenerateNTSC(palette_t *palette,int hue,int sat) +{ + const double black = 0.519; + const double white = 1.443; + const double voltage[2][4] = {{1.094,1.506,1.962,1.962},{0.350,0.519,0.962,1.550}}; + + const char phases[12][12] = { + {1,1,1,1,1,1,0,0,0,0,0,0}, + {1,1,1,1,1,0,0,0,0,0,0,1}, // blue + {1,1,1,1,0,0,0,0,0,0,1,1}, + {1,1,1,0,0,0,0,0,0,1,1,1}, // magenta + {1,1,0,0,0,0,0,0,1,1,1,1}, + {1,0,0,0,0,0,0,1,1,1,1,1}, // red + {0,0,0,0,0,0,1,1,1,1,1,1}, + {0,0,0,0,0,1,1,1,1,1,1,0}, // yellow + {0,0,0,0,1,1,1,1,1,1,0,0}, + {0,0,0,1,1,1,1,1,1,0,0,0}, // green + {0,0,1,1,1,1,1,1,0,0,0,0}, + {0,1,1,1,1,1,1,0,0,0,0,0}, // cyan + }; + const char emphasis[8][12] = { + {0,0,0,0,0,0,0,0,0,0,0,0}, // none + {0,1,1,1,1,1,1,0,0,0,0,0}, // red + {1,1,1,0,0,0,0,0,0,1,1,1}, // green + {1,1,1,1,1,1,1,0,0,1,1,1}, // yellow + {0,0,0,0,0,1,1,1,1,1,1,0}, // blue + {0,1,1,1,1,1,1,1,1,1,1,0}, // magenta + {1,1,1,0,0,1,1,1,1,1,1,1}, // cyan + {1,1,1,1,1,1,1,1,1,1,1,1} // all + }; + + int i, x, y, z; + for (x = 0; x < 8; x++) + { + for (y = 0; y < 4; y++) + { + for (z = 0; z < 16; z++) + { + double wave[12]; + double Y, I, Q; + double R, G, B; + + double H = 0, S = 0; + + for (i = 0; i < 12; i++) + { + if (z == 0) + wave[i] = voltage[0][y]; + else if (z < 13) + wave[i] = phases[z-1][i] ? voltage[0][y] : voltage[1][y]; + else if (z == 13) + wave[i] = voltage[1][y]; + else wave[i] = black; + if ((emphasis[x][i]) && (z < 14)) + wave[i] = wave[i] * 0.75; + } + + Y = 0.0; S = 0; + for (i = 0; i < 12; i++) + Y += wave[i] / 12.0; + for (i = 0; i < 12; i++) + S += (wave[i] - Y) * (wave[i] - Y); + Y = (Y - black) / white; + S = S / white; // don't remove black offset, since this is already relative + S = sqrt(S / 12.0) * sat / 50.0; + + H = M_PI * (270 + getPhase(wave) + hue) / 180.0; + + I = S * sin(H); + Q = S * cos(H); + + R = Y + 0.956 * I + 0.621 * Q; + G = Y - 0.272 * I - 0.647 * Q; + B = Y - 1.107 * I + 1.705 * Q; + + R *= 256; + G *= 256; + B *= 256; + + palette->pal[x][(y << 4) | z].r = (unsigned char)CLIP(R,0,255); + palette->pal[x][(y << 4) | z].g = (unsigned char)CLIP(G,0,255); + palette->pal[x][(y << 4) | z].b = (unsigned char)CLIP(B,0,255); + } + } + } +} + +palette_t *palette_generate(int hue,int sat) //generate nes palette +{ + palette_t *ret = 0; + + ret = palette_create(); + GenerateNTSC(ret,hue,sat); + return(ret); +} diff --git a/apps/nesemu2/src/palette/generator.h b/apps/nesemu2/src/palette/generator.h new file mode 100644 index 00000000..d8b5b08a --- /dev/null +++ b/apps/nesemu2/src/palette/generator.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __generator_h__ +#define __generator_h__ + +#include "palette/palette.h" + +palette_t *palette_generate(int hue,int sat); + +#endif diff --git a/apps/nesemu2/src/palette/palette.c b/apps/nesemu2/src/palette/palette.c new file mode 100644 index 00000000..27010c17 --- /dev/null +++ b/apps/nesemu2/src/palette/palette.c @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/memutil.h" +#include "misc/strutil.h" +#include "misc/log.h" +#include "misc/config.h" +#include "palette/palette.h" +#include "palette/generator.h" +#include "system/video.h" +#include "misc/paths.h" + +static palette_t *pal = 0; + +/* measurement by Quietust */ +static const double emphasis_factor[8][3]={ + {1.00, 1.00, 1.00}, + {1.00, 0.80, 0.81}, + {0.78, 0.94, 0.66}, + {0.79, 0.77, 0.63}, + {0.82, 0.83, 1.12}, + {0.81, 0.71, 0.87}, + {0.68, 0.79, 0.79}, + {0.70, 0.70, 0.70} +}; + +static void generate_emphasis(palette_t *p) +{ + int i,j; + + for(j=1;j<8;j++) { + for(i=0;i<64;i++) { + p->pal[j][i].r = (u8)((double)p->pal[0][i].r * emphasis_factor[j][0]); + p->pal[j][i].g = (u8)((double)p->pal[0][i].g * emphasis_factor[j][1]); + p->pal[j][i].b = (u8)((double)p->pal[0][i].b * emphasis_factor[j][2]); + } + } +} + +palette_t *palette_create() +{ + palette_t *ret = 0; + + ret = (palette_t*)mem_alloc(sizeof(palette_t)); + memset(ret,0,sizeof(palette_t)); + return(ret); +} + +void palette_destroy(palette_t *p) +{ + mem_free(p); +} + +palette_t *palette_load(char *filename) +{ + palette_t *ret = 0; + FILE *fp; + u8 colors[3]; + int i; + + if((fp = fopen(filename,"rb")) == 0) { + log_printf("palette_load: error opening '%s'\n",filename); + return(0); + } + ret = palette_create(); + for(i=0;i<64;i++) { + fread(colors,1,3,fp); + if(feof(fp)) { + log_printf("palette_load: unexpected end of file in '%s'\n",filename); + palette_destroy(ret); + ret = 0; + break; + } + ret->pal[0][i].r = colors[0]; + ret->pal[0][i].g = colors[1]; + ret->pal[0][i].b = colors[2]; + } + fclose(fp); + generate_emphasis(ret); + log_printf("palette_load: loaded palette '%s'\n",filename); + return(ret); +} + +int palette_save(char *filename,palette_t *p) +{ + //not implemented yet + return(1); +} + +int palette_init() +{ + char file[1024]; + + //clear the string + memset(file,0,1024); + + //parse the bios path + config_get_eval_string(file,"path.palette"); + + //append the path seperator + str_appendchar(file,PATH_SEPERATOR); + + //append the bios filename + strcat(file,config_get_string("palette.filename")); + + if(strcmp(config_get_string("palette.source"),"file") == 0) { + pal = palette_load(file); + } + if(pal == 0) { + pal = palette_generate(config_get_int("palette.hue"),config_get_int("palette.saturation")); + } + video_setpalette(pal); + return(0); +} + +void palette_kill() +{ + palette_destroy(pal); +} diff --git a/apps/nesemu2/src/palette/palette.h b/apps/nesemu2/src/palette/palette.h new file mode 100644 index 00000000..3205250c --- /dev/null +++ b/apps/nesemu2/src/palette/palette.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __palette_h__ +#define __palette_h__ + +#include "types.h" + +typedef struct palentry_s { + u8 r,g,b; +} palentry_t; + +typedef struct palette_s { + palentry_t pal[8][64]; +} palette_t; + +palette_t *palette_create(); +void palette_destroy(palette_t *p); +palette_t *palette_load(char *filename); +int palette_save(char *filename,palette_t *p); +int palette_init(); +void palette_kill(); + +#endif diff --git a/apps/nesemu2/src/system/common/filters.c b/apps/nesemu2/src/system/common/filters.c new file mode 100644 index 00000000..f142fd71 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include "system/common/filters.h" +#include "system/common/filters/draw/draw.h" +#include "system/common/filters/interpolate/interpolate.h" +#include "system/common/filters/scale2x/scalebit.h" +#include "system/common/filters/ntsc/ntsc.h" + +#define FILTER_START(name,minw,minh,mins) filter_t filter_ ## name = { #name,minw,minh,mins, { +#define FILTER_MODE(scale,func) {scale,func}, +#define FILTER_END() {0,0}}}; + +FILTER_START(draw,256,240,1) + FILTER_MODE(1,draw1x) + FILTER_MODE(2,draw2x) + FILTER_MODE(3,draw3x) + FILTER_MODE(4,draw4x) + FILTER_MODE(5,draw5x) + FILTER_MODE(6,draw6x) +FILTER_END() + +FILTER_START(interpolate,512,480,2) + FILTER_MODE(2,interpolate2x) + FILTER_MODE(3,interpolate3x) + FILTER_MODE(4,interpolate4x) +FILTER_END() + +FILTER_START(scale,512,480,2) + FILTER_MODE(2,scale2x) + FILTER_MODE(3,scale3x) + FILTER_MODE(4,scale4x) +FILTER_END() + +FILTER_START(ntsc,602,480,2) + FILTER_MODE(2,ntsc2x) + FILTER_MODE(3,ntsc3x) + FILTER_MODE(4,ntsc4x) +FILTER_END() + +int filter_init() +{ + ntsc_init(); + return(0); +} + +void filter_kill() +{ + ntsc_kill(); +} + +void filter_palette_changed() +{ + ntsc_palette_changed(); +} + +int filter_get_int(char *str) +{ + if(stricmp("interpolate",str) == 0) return(F_INTERPOLATE); + if(stricmp("scale",str) == 0) return(F_SCALE); + if(stricmp("ntsc",str) == 0) return(F_NTSC); + return(F_NONE); +} + +filter_t *filter_get(int flt) +{ + filter_t *ret = 0; + + switch(flt) { + default: + case F_NONE: ret = &filter_draw; break; + case F_INTERPOLATE: ret = &filter_interpolate; break; + case F_SCALE: ret = &filter_scale; break; + case F_NTSC: ret = &filter_ntsc; break; + } + return(ret); +} diff --git a/apps/nesemu2/src/system/common/filters.h b/apps/nesemu2/src/system/common/filters.h new file mode 100644 index 00000000..55089974 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __filters_h__ +#define __filters_h__ + +#include "types.h" + +#define FILTER_DECL(name) extern filter_t filter_ ##name ; + +typedef struct filter_s { + + //name of filter + char *name; + + //minimum required output width/height for filter + int minwidth,minheight; + + //minimum scale factor + int minscale; + + struct mode_s { + //scale factor + int scale; + + //function to draw from source to dest +// void (*draw16)(void*,u32,void*,u32,u32,u32); + void (*draw32)(void*,u32,void*,u32,u32,u32); + } modes[8]; + +} filter_t; + +enum filters_e { + + //filters + F_DRAW = 0, + F_INTERPOLATE, + F_SCALE, + F_NTSC, + + //clones + F_NONE = F_DRAW, +}; + +FILTER_DECL(draw); +FILTER_DECL(interpolate); +FILTER_DECL(scale); +FILTER_DECL(ntsc); + +int filter_init(); +void filter_kill(); +void filter_palette_changed(); +int filter_get_int(char *str); +filter_t *filter_get(int flt); + +#endif diff --git a/apps/nesemu2/src/system/common/filters/draw/draw.c b/apps/nesemu2/src/system/common/filters/draw/draw.c new file mode 100644 index 00000000..94b4afd9 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/draw/draw.c @@ -0,0 +1,171 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "draw.h" + +void draw1x_16(u16 *dest,u32 destp,u16 *src,u32 srcp,u32 w,u32 h) +{ + u32 x,y; + + destp /= 4; + srcp /= 4; + for(y=0;y> 1) & 0x7F7F7F7F) + (((p2) >> 1) & 0x7F7F7F7F)) + +void interpolate2x(void *void_dst,u32 dst_slice,void *void_src,u32 src_slice,u32 width,u32 height) +{ + u32 *dest = (u32*)void_dst; + u32 *src = (u32*)void_src; + u32 *p; + u32 x,y; + + dst_slice /= 4; + src_slice /= 4; + for(y=0;ytable [0] + burst * (nes_ntsc_burst_size * sizeof (nes_ntsc_rgb_t));\ + NES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, NES_NTSC_ENTRY_, ktable ) + +/* Begins input pixel */ +#define NES_NTSC_COLOR_IN( in_index, color_in ) \ + NES_NTSC_COLOR_IN_( in_index, color_in, NES_NTSC_ENTRY_, ktable ) + +/* Generates output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) +16: RRRRRGGG GGGBBBBB (5-6-5 RGB) +15: RRRRRGG GGGBBBBB (5-5-5 RGB) + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define NES_NTSC_RGB_OUT( index, rgb_out, bits ) \ + NES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 ) + + +/* private */ +enum { nes_ntsc_entry_size = 128 }; +typedef unsigned long nes_ntsc_rgb_t; +struct nes_ntsc_t { + nes_ntsc_rgb_t table [nes_ntsc_palette_size] [nes_ntsc_entry_size]; +}; +enum { nes_ntsc_burst_size = nes_ntsc_entry_size / nes_ntsc_burst_count }; + +#define NES_NTSC_ENTRY_( ktable, n ) \ + (nes_ntsc_rgb_t const*) (ktable + (n) * (nes_ntsc_entry_size * sizeof (nes_ntsc_rgb_t))) + +/* deprecated */ +#define NES_NTSC_RGB24_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 24 ) +#define NES_NTSC_RGB16_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 16 ) +#define NES_NTSC_RGB15_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 15 ) +#define NES_NTSC_RAW_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 0 ) + +enum { nes_ntsc_min_in_width = 256 }; +enum { nes_ntsc_min_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_min_in_width ) }; + +enum { nes_ntsc_640_in_width = 271 }; +enum { nes_ntsc_640_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_640_in_width ) }; +enum { nes_ntsc_640_overscan_left = 8 }; +enum { nes_ntsc_640_overscan_right = nes_ntsc_640_in_width - 256 - nes_ntsc_640_overscan_left }; + +enum { nes_ntsc_full_in_width = 283 }; +enum { nes_ntsc_full_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_full_in_width ) }; +enum { nes_ntsc_full_overscan_left = 16 }; +enum { nes_ntsc_full_overscan_right = nes_ntsc_full_in_width - 256 - nes_ntsc_full_overscan_left }; + +/* common 3->7 ntsc macros */ +#define NES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ + unsigned const nes_ntsc_pixel0_ = (pixel0);\ + nes_ntsc_rgb_t const* kernel0 = ENTRY( table, nes_ntsc_pixel0_ );\ + unsigned const nes_ntsc_pixel1_ = (pixel1);\ + nes_ntsc_rgb_t const* kernel1 = ENTRY( table, nes_ntsc_pixel1_ );\ + unsigned const nes_ntsc_pixel2_ = (pixel2);\ + nes_ntsc_rgb_t const* kernel2 = ENTRY( table, nes_ntsc_pixel2_ );\ + nes_ntsc_rgb_t const* kernelx0;\ + nes_ntsc_rgb_t const* kernelx1 = kernel0;\ + nes_ntsc_rgb_t const* kernelx2 = kernel0 + +#define NES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ + nes_ntsc_rgb_t raw_ =\ + kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ + kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ + NES_NTSC_CLAMP_( raw_, shift );\ + NES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ +} + +/* common ntsc macros */ +#define nes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define nes_ntsc_clamp_mask (nes_ntsc_rgb_builder * 3 / 2) +#define nes_ntsc_clamp_add (nes_ntsc_rgb_builder * 0x101) +#define NES_NTSC_CLAMP_( io, shift ) {\ + nes_ntsc_rgb_t sub = (io) >> (9-(shift)) & nes_ntsc_clamp_mask;\ + nes_ntsc_rgb_t clamp = nes_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define NES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +#define NES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 )\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 24 || bits == 32 )\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + if ( bits == 15 )\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 0 )\ + rgb_out = raw_ << x;\ +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl new file mode 100644 index 00000000..004abb6f --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl @@ -0,0 +1,289 @@ +/* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +#include "nes_ntsc.h" + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +nes_ntsc_setup_t const nes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0, 0, 0 }; +nes_ntsc_setup_t const nes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; +nes_ntsc_setup_t const nes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0, 0, 0 }; +nes_ntsc_setup_t const nes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0, 0, 0 }; + +#define alignment_count 3 +#define burst_count 3 +#define rescale_in 8 +#define rescale_out 7 + +#define artifacts_mid 1.0f +#define fringing_mid 1.0f +#define std_decoder_hue -15 + +#define STD_HUE_CONDITION( setup ) !(setup->base_palette || setup->palette) + +#include "nes_ntsc_impl.h" + +/* 3 input pixels -> 8 composite samples */ +pixel_info_t const nes_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, + { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, + { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, +}; + +static void merge_kernel_fields( nes_ntsc_rgb_t* io ) +{ + int n; + for ( n = burst_size; n; --n ) + { + nes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; + nes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; + nes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; + /* merge colors without losing precision */ + io [burst_size * 0] = + ((p0 + p1 - ((p0 ^ p1) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 1] = + ((p1 + p2 - ((p1 ^ p2) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 2] = + ((p2 + p0 - ((p2 ^ p0) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; + ++io; + } +} + +static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ) +{ + int n; + for ( n = burst_count; n; --n ) + { + unsigned i; + for ( i = 0; i < rgb_kernel_size / 2; i++ ) + { + nes_ntsc_rgb_t error = color - + out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - + out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; + DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); + } + out += alignment_count * rgb_kernel_size; + } +} + +void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup ) +{ + int merge_fields; + int entry; + init_t impl; + float gamma_factor; + + if ( !setup ) + setup = &nes_ntsc_composite; + init( &impl, setup ); + + /* setup fast gamma */ + { + float gamma = (float) setup->gamma * -0.5f; + if ( STD_HUE_CONDITION( setup ) ) + gamma += 0.1333f; + + gamma_factor = (float) pow( (float) fabs( gamma ), 0.73f ); + if ( gamma < 0 ) + gamma_factor = -gamma_factor; + } + + merge_fields = setup->merge_fields; + if ( setup->artifacts <= -1 && setup->fringing <= -1 ) + merge_fields = 1; + + for ( entry = 0; entry < nes_ntsc_palette_size; entry++ ) + { + /* Base 64-color generation */ + static float const lo_levels [4] = { -0.12f, 0.00f, 0.31f, 0.72f }; + static float const hi_levels [4] = { 0.40f, 0.68f, 1.00f, 1.00f }; + int level = entry >> 4 & 0x03; + float lo = lo_levels [level]; + float hi = hi_levels [level]; + + int color = entry & 0x0F; + if ( color == 0 ) + lo = hi; + if ( color == 0x0D ) + hi = lo; + if ( color > 0x0D ) + hi = lo = 0.0f; + + { + /* phases [i] = cos( i * PI / 6 ) */ + static float const phases [0x10 + 3] = { + -1.0f, -0.866025f, -0.5f, 0.0f, 0.5f, 0.866025f, + 1.0f, 0.866025f, 0.5f, 0.0f, -0.5f, -0.866025f, + -1.0f, -0.866025f, -0.5f, 0.0f, 0.5f, 0.866025f, + 1.0f + }; + #define TO_ANGLE_SIN( color ) phases [color] + #define TO_ANGLE_COS( color ) phases [(color) + 3] + + /* Convert raw waveform to YIQ */ + float sat = (hi - lo) * 0.5f; + float i = TO_ANGLE_SIN( color ) * sat; + float q = TO_ANGLE_COS( color ) * sat; + float y = (hi + lo) * 0.5f; + + /* Optionally use base palette instead */ + if ( setup->base_palette ) + { + unsigned char const* in = &setup->base_palette [(entry & 0x3F) * 3]; + static float const to_float = 1.0f / 0xFF; + float r = to_float * in [0]; + float g = to_float * in [1]; + float b = to_float * in [2]; + q = RGB_TO_YIQ( r, g, b, y, i ); + } + + /* Apply color emphasis */ + #ifdef NES_NTSC_EMPHASIS + { + int tint = entry >> 6 & 7; + if ( tint && color <= 0x0D ) + { + static float const atten_mul = 0.79399f; + static float const atten_sub = 0.0782838f; + + if ( tint == 7 ) + { + y = y * (atten_mul * 1.13f) - (atten_sub * 1.13f); + } + else + { + static unsigned char const tints [8] = { 0, 6, 10, 8, 2, 4, 0, 0 }; + int const tint_color = tints [tint]; + float sat = hi * (0.5f - atten_mul * 0.5f) + atten_sub * 0.5f; + y -= sat * 0.5f; + if ( tint >= 3 && tint != 4 ) + { + /* combined tint bits */ + sat *= 0.6f; + y -= sat; + } + i += TO_ANGLE_SIN( tint_color ) * sat; + q += TO_ANGLE_COS( tint_color ) * sat; + } + } + } + #endif + + /* Optionally use palette instead */ + if ( setup->palette ) + { + unsigned char const* in = &setup->palette [entry * 3]; + static float const to_float = 1.0f / 0xFF; + float r = to_float * in [0]; + float g = to_float * in [1]; + float b = to_float * in [2]; + q = RGB_TO_YIQ( r, g, b, y, i ); + } + + /* Apply brightness, contrast, and gamma */ + y *= (float) setup->contrast * 0.5f + 1; + /* adjustment reduces error when using input palette */ + y += (float) setup->brightness * 0.5f - 0.5f / 256; + + { + float r, g, b = YIQ_TO_RGB( y, i, q, default_decoder, float, r, g ); + + /* fast approximation of n = pow( n, gamma ) */ + r = (r * gamma_factor - gamma_factor) * r + r; + g = (g * gamma_factor - gamma_factor) * g + g; + b = (b * gamma_factor - gamma_factor) * b + b; + + q = RGB_TO_YIQ( r, g, b, y, i ); + } + + i *= rgb_unit; + q *= rgb_unit; + y *= rgb_unit; + y += rgb_offset; + + /* Generate kernel */ + { + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + /* blue tends to overflow, so clamp it */ + nes_ntsc_rgb_t rgb = PACK_RGB( r, g, (b < 0x3E0 ? b: 0x3E0) ); + + if ( setup->palette_out ) + RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); + + if ( ntsc ) + { + nes_ntsc_rgb_t* kernel = ntsc->table [entry]; + gen_kernel( &impl, y, i, q, kernel ); + if ( merge_fields ) + merge_kernel_fields( kernel ); + correct_errors( rgb, kernel ); + } + } + } + } +} + +#ifndef NES_NTSC_NO_BLITTERS + +void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 1) / nes_ntsc_in_chunk; + for ( ; in_height; --in_height ) + { + NES_NTSC_IN_T const* line_in = input; + NES_NTSC_BEGIN_ROW( ntsc, burst_phase, + nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in ) ); + nes_ntsc_out_t* restrict line_out = (nes_ntsc_out_t*) rgb_out; + int n; + ++line_in; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0] ) ); + NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); + + NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1] ) ); + NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); + + NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2] ) ); + NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); + + line_in += 3; + line_out += 7; + } + + /* finish final pixels */ + NES_NTSC_COLOR_IN( 0, nes_ntsc_black ); + NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); + + NES_NTSC_COLOR_IN( 1, nes_ntsc_black ); + NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); + + NES_NTSC_COLOR_IN( 2, nes_ntsc_black ); + NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); + NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % nes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h new file mode 100644 index 00000000..35b5637e --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h @@ -0,0 +1,27 @@ +/* Configure library by modifying this file */ + +#ifndef NES_NTSC_CONFIG_H +#define NES_NTSC_CONFIG_H + +/* Uncomment to enable emphasis support and use a 512 color palette instead +of the base 64 color palette. */ +#define NES_NTSC_EMPHASIS 1 + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define NES_NTSC_OUT_DEPTH 32 + +/* Type of input pixel values. You'll probably use unsigned short +if you enable emphasis above. */ +#define NES_NTSC_IN_T unsigned char + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define NES_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = color_palette [NES_NTSC_ADJ_IN( NES_NTSC_IN_T )] */ + +#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h new file mode 100644 index 00000000..de3672b4 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h @@ -0,0 +1,439 @@ +/* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +/* Common implementation of NTSC filters */ + +#include +#include + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define DISABLE_CORRECTION 0 + +#undef PI +#define PI 3.14159265358979323846f + +#ifndef LUMA_CUTOFF + #define LUMA_CUTOFF 0.20 +#endif +#ifndef gamma_size + #define gamma_size 1 +#endif +#ifndef rgb_bits + #define rgb_bits 8 +#endif +#ifndef artifacts_max + #define artifacts_max (artifacts_mid * 1.5f) +#endif +#ifndef fringing_max + #define fringing_max (fringing_mid * 2) +#endif +#ifndef STD_HUE_CONDITION + #define STD_HUE_CONDITION( setup ) 1 +#endif + +#define ext_decoder_hue (std_decoder_hue + 15) +#define rgb_unit (1 << rgb_bits) +#define rgb_offset (rgb_unit * 2 + 0.5f) + +enum { burst_size = nes_ntsc_entry_size / burst_count }; +enum { kernel_half = 16 }; +enum { kernel_size = kernel_half * 2 + 1 }; + +typedef struct init_t +{ + float to_rgb [burst_count * 6]; + float to_float [gamma_size]; + float contrast; + float brightness; + float artifacts; + float fringing; + float kernel [rescale_out * kernel_size * 2]; +} init_t; + +#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ + float t;\ + t = i * cos_b - q * sin_b;\ + q = i * sin_b + q * cos_b;\ + i = t;\ +} + +static void init_filters( init_t* impl, nes_ntsc_setup_t const* setup ) +{ +#if rescale_out > 1 + float kernels [kernel_size * 2]; +#else + float* const kernels = impl->kernel; +#endif + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; + float const maxh = 32; + float const pow_a_n = (float) pow( rolloff, maxh ); + float sum; + int i; + /* quadratic mapping to reduce negative (blurring) range */ + float to_angle = (float) setup->resolution + 1; + to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); + + kernels [kernel_size * 3 / 2] = maxh; /* default center value */ + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = i - kernel_half; + float angle = x * to_angle; + /* instability occurs at center point with rolloff very close to 1.0 */ + if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) + { + float rolloff_cos_a = rolloff * (float) cos( angle ); + float num = 1 - rolloff_cos_a - + pow_a_n * (float) cos( maxh * angle ) + + pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); + float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + float dsf = num / den; + kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; + } + } + + /* apply blackman window and find sum */ + sum = 0; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + float x = PI * 2 / (kernel_half * 2) * i; + float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); + sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); + } + + /* normalize kernel */ + sum = 1.0f / sum; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = kernel_size * 3 / 2 - kernel_half + i; + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + + /* generate chroma (iq) filter using gaussian kernel */ + { + float const cutoff_factor = -0.03125f; + float cutoff = (float) setup->bleed; + int i; + + if ( cutoff < 0 ) + { + /* keep extreme value accessible only near upper end of scale (1.0) */ + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= -30.0f / 0.65f; + } + cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; + + for ( i = -kernel_half; i <= kernel_half; i++ ) + kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); + + /* normalize even and odd phases separately */ + for ( i = 0; i < 2; i++ ) + { + float sum = 0; + int x; + for ( x = i; x < kernel_size; x += 2 ) + sum += kernels [x]; + + sum = 1.0f / sum; + for ( x = i; x < kernel_size; x += 2 ) + { + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + } + + /* + printf( "luma:\n" ); + for ( i = kernel_size; i < kernel_size * 2; i++ ) + printf( "%f\n", kernels [i] ); + printf( "chroma:\n" ); + for ( i = 0; i < kernel_size; i++ ) + printf( "%f\n", kernels [i] ); + */ + + /* generate linear rescale kernels */ + #if rescale_out > 1 + { + float weight = 1.0f; + float* out = impl->kernel; + int n = rescale_out; + do + { + float remain = 0; + int i; + weight -= 1.0f / rescale_in; + for ( i = 0; i < kernel_size * 2; i++ ) + { + float cur = kernels [i]; + float m = cur * weight; + *out++ = m + remain; + remain = cur - m; + } + } + while ( --n ); + } + #endif +} + +static float const default_decoder [6] = + { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; + +static void init( init_t* impl, nes_ntsc_setup_t const* setup ) +{ + impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; + impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; + #ifdef default_palette_contrast + if ( !setup->palette ) + impl->contrast *= default_palette_contrast; + #endif + + impl->artifacts = (float) setup->artifacts; + if ( impl->artifacts > 0 ) + impl->artifacts *= artifacts_max - artifacts_mid; + impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; + + impl->fringing = (float) setup->fringing; + if ( impl->fringing > 0 ) + impl->fringing *= fringing_max - fringing_mid; + impl->fringing = impl->fringing * fringing_mid + fringing_mid; + + init_filters( impl, setup ); + + /* generate gamma table */ + if ( gamma_size > 1 ) + { + float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); + float const gamma = 1.1333f - (float) setup->gamma * 0.5f; + /* match common PC's 2.2 gamma to TV's 2.65 gamma */ + int i; + for ( i = 0; i < gamma_size; i++ ) + impl->to_float [i] = + (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; + } + + /* setup decoder matricies */ + { + float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; + float sat = (float) setup->saturation + 1; + float const* decoder = setup->decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + if ( STD_HUE_CONDITION( setup ) ) + hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); + } + + { + float s = (float) sin( hue ) * sat; + float c = (float) cos( hue ) * sat; + float* out = impl->to_rgb; + int n; + + n = burst_count; + do + { + float const* in = decoder; + int n = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +/* kernel generation */ + +#define RGB_TO_YIQ( r, g, b, y, i ) (\ + (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ + (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ + ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ +) + +#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ + r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ + g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ + (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ +) + +#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + +enum { rgb_kernel_size = burst_size / alignment_count }; +enum { rgb_bias = rgb_unit * 2 * nes_ntsc_rgb_builder }; + +typedef struct pixel_info_t +{ + int offset; + float negate; + float kernel [4]; +} pixel_info_t; + +#if rescale_in > 1 + #define PIXEL_OFFSET_( ntsc, scaled ) \ + (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ + (kernel_size * 2 * scaled)) + + #define PIXEL_OFFSET( ntsc, scaled ) \ + PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ + (((scaled) + rescale_out * 10) % rescale_out) ),\ + (1.0f - (((ntsc) + 100) & 2)) +#else + #define PIXEL_OFFSET( ntsc, scaled ) \ + (kernel_size / 2 + (ntsc) - (scaled)),\ + (1.0f - (((ntsc) + 100) & 2)) +#endif + +extern pixel_info_t const nes_ntsc_pixels [alignment_count]; + +/* Generate pixel at all burst phases and column alignments */ +static void gen_kernel( init_t* impl, float y, float i, float q, nes_ntsc_rgb_t* out ) +{ + /* generate for each scanline burst phase */ + float const* to_rgb = impl->to_rgb; + int burst_remain = burst_count; + y -= rgb_offset; + do + { + /* Encode yiq into *two* composite signals (to allow control over artifacting). + Convolve these with kernels which: filter respective components, apply + sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack + into integer. Based on algorithm by NewRisingSun. */ + pixel_info_t const* pixel = nes_ntsc_pixels; + int alignment_remain = alignment_count; + do + { + /* negate is -1 when composite starts at odd multiple of 2 */ + float const yy = y * impl->fringing * pixel->negate; + float const ic0 = (i + yy) * pixel->kernel [0]; + float const qc1 = (q + yy) * pixel->kernel [1]; + float const ic2 = (i - yy) * pixel->kernel [2]; + float const qc3 = (q - yy) * pixel->kernel [3]; + + float const factor = impl->artifacts * pixel->negate; + float const ii = i * factor; + float const yc0 = (y + ii) * pixel->kernel [0]; + float const yc2 = (y - ii) * pixel->kernel [2]; + + float const qq = q * factor; + float const yc1 = (y + qq) * pixel->kernel [1]; + float const yc3 = (y - qq) * pixel->kernel [3]; + + float const* k = &impl->kernel [pixel->offset]; + int n; + ++pixel; + for ( n = rgb_kernel_size; n; --n ) + { + float i = k[0]*ic0 + k[2]*ic2; + float q = k[1]*qc1 + k[3]*qc3; + float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + if ( rescale_out <= 1 ) + k--; + else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) + k += kernel_size * 2 - 1; + else + k -= kernel_size * 2 * (rescale_out - 1) + 2; + { + int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); + *out++ = PACK_RGB( r, g, b ) - rgb_bias; + } + } + } + while ( alignment_count > 1 && --alignment_remain ); + + if ( burst_count <= 1 ) + break; + + to_rgb += 6; + + ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ + } + while ( --burst_remain ); +} + +static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ); + +#if DISABLE_CORRECTION + #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } + #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } +#else + #define CORRECT_ERROR( a ) { out [a] += error; } + #define DISTRIBUTE_ERROR( a, b, c ) {\ + nes_ntsc_rgb_t fourth = (error + 2 * nes_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - nes_ntsc_rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } +#endif + +#define RGB_PALETTE_OUT( rgb, out_ )\ +{\ + unsigned char* out = (out_);\ + nes_ntsc_rgb_t clamped = (rgb);\ + NES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + out [0] = (unsigned char) (clamped >> 21);\ + out [1] = (unsigned char) (clamped >> 11);\ + out [2] = (unsigned char) (clamped >> 1);\ +} + +/* blitter related */ + +#ifndef restrict + #if defined (__GNUC__) + #define restrict __restrict__ + #elif defined (_MSC_VER) && _MSC_VER > 1300 + #define restrict __restrict + #else + /* no support for restricted pointers */ + #define restrict + #endif +#endif + +#include + +#if NES_NTSC_OUT_DEPTH <= 16 + #if USHRT_MAX == 0xFFFF + typedef unsigned short nes_ntsc_out_t; + #else + #error "Need 16-bit int type" + #endif + +#else + #if UINT_MAX == 0xFFFFFFFF + typedef unsigned int nes_ntsc_out_t; + #elif ULONG_MAX == 0xFFFFFFFF + typedef unsigned long nes_ntsc_out_t; + #else + #error "Need 32-bit int type" + #endif + +#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c b/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c new file mode 100644 index 00000000..f0827bc9 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include "ntsc.h" +#include "nes_ntsc/nes_ntsc.h" +#include "misc/memutil.h" +#include "system/video.h" +#include "nes/nes.h" + +#define NES_NTSC_NO_BLITTERS +#include "nes_ntsc/nes_ntsc.inl" + +nes_ntsc_t *ntsc = 0; + +static void palette_changed() +{ + nes_ntsc_setup_t setup; + double sharpness = 100.0f; + double resolution = 100.0f; + double artifacts = 100.0f; + double fringing = 100.0f; + double bleed = 100.0f; + int merge_fields = 1; + + setup.hue = 0; + setup.saturation = 0; + setup.contrast = 0; + setup.brightness = 0; + setup.sharpness = sharpness / 100.0; + setup.gamma = 0; + setup.resolution = resolution / 100.0; + setup.artifacts = artifacts / 100.0; + setup.fringing = fringing / 100.0; + setup.bleed = bleed / 100.0; + setup.merge_fields = merge_fields; + setup.decoder_matrix = NULL; + setup.palette_out = NULL; + setup.palette = NULL; + setup.base_palette = video_getpalette(); + nes_ntsc_init(ntsc,&setup); +} + +int ntsc_init() +{ + ntsc_kill(); + ntsc = (nes_ntsc_t*)mem_alloc(sizeof(nes_ntsc_t)); + palette_changed(); + return(0); +} + +void ntsc_kill() +{ + if(ntsc) + mem_free(ntsc); + ntsc = 0; +} + +void ntsc_palette_changed() +{ + if(ntsc) + palette_changed(); +} + +void ntsc2x(void *d,u32 dst_slice,void *s,u32 src_slice,u32 width,u32 height) +{ + static int phase = 0; + int bpp = 32; + int i,h; + int chunk_count = (256 - 1) / nes_ntsc_in_chunk; + u8 *screen = video_getscreen(); + u8 *src = screen; + u32 *dst = (u32*)d; + u8 *pal = nes->ppu.palette; + + for(h=0;h<240;h++) { + u32 *dststart = dst; + + NES_NTSC_BEGIN_ROW( ntsc, phase, nes_ntsc_black, nes_ntsc_black, pal[*src & 0x1F] | ((*src & 0xE0) << 1) ); + src++; + + for(i=0;i +#endif + +#define inline __inline + +#include "scale2x.h" + +#include + +/***************************************************************************/ +/* Scale2x C implementation */ + +/** + * Define the macro USE_SCALE_RANDOMWRITE to enable + * an optimized version which writes memory in random order. + * This version is a little faster if you write in system memory. + * But it's a lot slower if you write in video memory. + * So, enable it only if you are sure to never write directly in video memory. + */ +/* #define USE_SCALE_RANDOMWRITE */ + +static inline void scale2x_8_def_whole(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst0[0] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[0] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst0 += 2; + dst1 += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst0 += 2; + dst1 += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[0] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } +} + +static inline void scale2x_8_def_border(scale2x_uint8* dst, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[0] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } +} + +static inline void scale2x_8_def_center(scale2x_uint8* dst, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0]; + dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } +} + +static inline void scale2x_16_def_whole(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst0[0] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[0] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst0 += 2; + dst1 += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst0 += 2; + dst1 += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[0] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } +} + +static inline void scale2x_16_def_border(scale2x_uint16* dst, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[0] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } +} + +static inline void scale2x_16_def_center(scale2x_uint16* dst, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0]; + dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } +} + +static inline void scale2x_32_def_whole(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst0[0] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[0] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst0 += 2; + dst1 += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[1] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst0 += 2; + dst1 += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst0[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst0[1] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst1[0] = src1[-1] == src2[0] ? src2[0] : src1[0]; + dst1[1] = src1[0] == src2[0] ? src2[0] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + } +} + +static inline void scale2x_32_def_border(scale2x_uint32* dst, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[1] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = src1[-1] == src0[0] ? src0[0] : src1[0]; + dst[1] = src1[0] == src0[0] ? src0[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } +} + +static inline void scale2x_32_def_center(scale2x_uint32* dst, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0]; + dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 2; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 2; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + } +} + +/** + * Scale by a factor of 2 a row of pixels of 8 bits. + * The function is implemented in C. + * The pixels over the left and right borders are assumed of the same color of + * the pixels on the border. + * Note that the implementation is optimized to write data sequentially to + * maximize the bandwidth on video memory. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. + * It must be at least 2. + * \param dst0 First destination row, double length in pixels. + * \param dst1 Second destination row, double length in pixels. + */ +void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_8_def_whole(dst0, dst1, src0, src1, src2, count); +#else + scale2x_8_def_border(dst0, src0, src1, src2, count); + scale2x_8_def_border(dst1, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2 a row of pixels of 16 bits. + * This function operates like scale2x_8_def() but for 16 bits pixels. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. + * It must be at least 2. + * \param dst0 First destination row, double length in pixels. + * \param dst1 Second destination row, double length in pixels. + */ +void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_16_def_whole(dst0, dst1, src0, src1, src2, count); +#else + scale2x_16_def_border(dst0, src0, src1, src2, count); + scale2x_16_def_border(dst1, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2 a row of pixels of 32 bits. + * This function operates like scale2x_8_def() but for 32 bits pixels. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. + * It must be at least 2. + * \param dst0 First destination row, double length in pixels. + * \param dst1 Second destination row, double length in pixels. + */ +void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_32_def_whole(dst0, dst1, src0, src1, src2, count); +#else + scale2x_32_def_border(dst0, src0, src1, src2, count); + scale2x_32_def_border(dst1, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2x3 a row of pixels of 8 bits. + * \note Like scale2x_8_def(); + */ +void scale2x3_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_8_def_whole(dst0, dst2, src0, src1, src2, count); + scale2x_8_def_center(dst1, src0, src1, src2, count); +#else + scale2x_8_def_border(dst0, src0, src1, src2, count); + scale2x_8_def_center(dst1, src0, src1, src2, count); + scale2x_8_def_border(dst2, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2x3 a row of pixels of 16 bits. + * \note Like scale2x_16_def(); + */ +void scale2x3_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_16_def_whole(dst0, dst2, src0, src1, src2, count); + scale2x_16_def_center(dst1, src0, src1, src2, count); +#else + scale2x_16_def_border(dst0, src0, src1, src2, count); + scale2x_16_def_center(dst1, src0, src1, src2, count); + scale2x_16_def_border(dst2, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2x3 a row of pixels of 32 bits. + * \note Like scale2x_32_def(); + */ +void scale2x3_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_32_def_whole(dst0, dst2, src0, src1, src2, count); + scale2x_32_def_center(dst1, src0, src1, src2, count); +#else + scale2x_32_def_border(dst0, src0, src1, src2, count); + scale2x_32_def_center(dst1, src0, src1, src2, count); + scale2x_32_def_border(dst2, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2x4 a row of pixels of 8 bits. + * \note Like scale2x_8_def(); + */ +void scale2x4_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_8_def_whole(dst0, dst3, src0, src1, src2, count); + scale2x_8_def_center(dst1, src0, src1, src2, count); + scale2x_8_def_center(dst2, src0, src1, src2, count); +#else + scale2x_8_def_border(dst0, src0, src1, src2, count); + scale2x_8_def_center(dst1, src0, src1, src2, count); + scale2x_8_def_center(dst2, src0, src1, src2, count); + scale2x_8_def_border(dst3, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2x4 a row of pixels of 16 bits. + * \note Like scale2x_16_def(); + */ +void scale2x4_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_16_def_whole(dst0, dst3, src0, src1, src2, count); + scale2x_16_def_center(dst1, src0, src1, src2, count); + scale2x_16_def_center(dst2, src0, src1, src2, count); +#else + scale2x_16_def_border(dst0, src0, src1, src2, count); + scale2x_16_def_center(dst1, src0, src1, src2, count); + scale2x_16_def_center(dst2, src0, src1, src2, count); + scale2x_16_def_border(dst3, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 2x4 a row of pixels of 32 bits. + * \note Like scale2x_32_def(); + */ +void scale2x4_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale2x_32_def_whole(dst0, dst3, src0, src1, src2, count); + scale2x_32_def_center(dst1, src0, src1, src2, count); + scale2x_32_def_center(dst2, src0, src1, src2, count); +#else + scale2x_32_def_border(dst0, src0, src1, src2, count); + scale2x_32_def_center(dst1, src0, src1, src2, count); + scale2x_32_def_center(dst2, src0, src1, src2, count); + scale2x_32_def_border(dst3, src2, src1, src0, count); +#endif +} + +/***************************************************************************/ +/* Scale2x MMX implementation */ + +#if defined(__GNUC__) && defined(__i386__) + +/* + * Apply the Scale2x effect at a single row. + * This function must be called only by the other scale2x functions. + * + * Considering the pixel map : + * + * ABC (src0) + * DEF (src1) + * GHI (src2) + * + * this functions compute 2 new pixels in substitution of the source pixel E + * like this map : + * + * ab (dst) + * + * with these variables : + * + * ¤t -> E + * ¤t_left -> D + * ¤t_right -> F + * ¤t_upper -> B + * ¤t_lower -> H + * + * %0 -> current_upper + * %1 -> current + * %2 -> current_lower + * %3 -> dst + * %4 -> counter + * + * %mm0 -> *current_left + * %mm1 -> *current_next + * %mm2 -> tmp0 + * %mm3 -> tmp1 + * %mm4 -> tmp2 + * %mm5 -> tmp3 + * %mm6 -> *current_upper + * %mm7 -> *current + */ +static inline void scale2x_8_mmx_border(scale2x_uint8* dst, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + assert(count >= 16); + assert(count % 8 == 0); + + /* always do the first and last run */ + count -= 2*8; + + __asm__ __volatile__( +/* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1), %%mm0\n" + "movq 0(%1), %%mm7\n" + "movq 8(%1), %%mm1\n" + "psllq $56, %%mm0\n" + "psllq $56, %%mm1\n" + "psrlq $56, %%mm0\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $8, %%mm2\n" + "psrlq $8, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqb %%mm6, %%mm2\n" + "pcmpeqb %%mm6, %%mm4\n" + "pcmpeqb (%2), %%mm3\n" + "pcmpeqb (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqb %%mm1, %%mm2\n" + "pcmpeqb %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpcklbw %%mm4, %%mm2\n" + "punpckhbw %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $16, %3\n" + +/* central runs */ + "shrl $3, %4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1), %%mm0\n" + "movq (%1), %%mm7\n" + "movq 8(%1), %%mm1\n" + "psrlq $56, %%mm0\n" + "psllq $56, %%mm1\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $8, %%mm2\n" + "psrlq $8, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqb %%mm6, %%mm2\n" + "pcmpeqb %%mm6, %%mm4\n" + "pcmpeqb (%2), %%mm3\n" + "pcmpeqb (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqb %%mm1, %%mm2\n" + "pcmpeqb %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpcklbw %%mm4, %%mm2\n" + "punpckhbw %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $16, %3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + +/* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1), %%mm1\n" + "movq (%1), %%mm7\n" + "movq -8(%1), %%mm0\n" + "psrlq $56, %%mm1\n" + "psrlq $56, %%mm0\n" + "psllq $56, %%mm1\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $8, %%mm2\n" + "psrlq $8, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqb %%mm6, %%mm2\n" + "pcmpeqb %%mm6, %%mm4\n" + "pcmpeqb (%2), %%mm3\n" + "pcmpeqb (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqb %%mm1, %%mm2\n" + "pcmpeqb %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpcklbw %%mm4, %%mm2\n" + "punpckhbw %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +} + +static inline void scale2x_16_mmx_border(scale2x_uint16* dst, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + assert(count >= 8); + assert(count % 4 == 0); + + /* always do the first and last run */ + count -= 2*4; + + __asm__ __volatile__( +/* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1), %%mm0\n" + "movq 0(%1), %%mm7\n" + "movq 8(%1), %%mm1\n" + "psllq $48, %%mm0\n" + "psllq $48, %%mm1\n" + "psrlq $48, %%mm0\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $16, %%mm2\n" + "psrlq $16, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqw %%mm6, %%mm2\n" + "pcmpeqw %%mm6, %%mm4\n" + "pcmpeqw (%2), %%mm3\n" + "pcmpeqw (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqw %%mm1, %%mm2\n" + "pcmpeqw %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpcklwd %%mm4, %%mm2\n" + "punpckhwd %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $16, %3\n" + +/* central runs */ + "shrl $2, %4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1), %%mm0\n" + "movq (%1), %%mm7\n" + "movq 8(%1), %%mm1\n" + "psrlq $48, %%mm0\n" + "psllq $48, %%mm1\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $16, %%mm2\n" + "psrlq $16, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqw %%mm6, %%mm2\n" + "pcmpeqw %%mm6, %%mm4\n" + "pcmpeqw (%2), %%mm3\n" + "pcmpeqw (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqw %%mm1, %%mm2\n" + "pcmpeqw %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpcklwd %%mm4, %%mm2\n" + "punpckhwd %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $16, %3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + +/* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1), %%mm1\n" + "movq (%1), %%mm7\n" + "movq -8(%1), %%mm0\n" + "psrlq $48, %%mm1\n" + "psrlq $48, %%mm0\n" + "psllq $48, %%mm1\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $16, %%mm2\n" + "psrlq $16, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqw %%mm6, %%mm2\n" + "pcmpeqw %%mm6, %%mm4\n" + "pcmpeqw (%2), %%mm3\n" + "pcmpeqw (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqw %%mm1, %%mm2\n" + "pcmpeqw %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpcklwd %%mm4, %%mm2\n" + "punpckhwd %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +} + +static inline void scale2x_32_mmx_border(scale2x_uint32* dst, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + assert(count >= 4); + assert(count % 2 == 0); + + /* always do the first and last run */ + count -= 2*2; + + __asm__ __volatile__( +/* first run */ + /* set the current, current_pre, current_next registers */ + "movq 0(%1), %%mm0\n" + "movq 0(%1), %%mm7\n" + "movq 8(%1), %%mm1\n" + "psllq $32, %%mm0\n" + "psllq $32, %%mm1\n" + "psrlq $32, %%mm0\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $32, %%mm2\n" + "psrlq $32, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqd %%mm6, %%mm2\n" + "pcmpeqd %%mm6, %%mm4\n" + "pcmpeqd (%2), %%mm3\n" + "pcmpeqd (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqd %%mm1, %%mm2\n" + "pcmpeqd %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpckldq %%mm4, %%mm2\n" + "punpckhdq %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $16, %3\n" + +/* central runs */ + "shrl $1, %4\n" + "jz 1f\n" + + "0:\n" + + /* set the current, current_pre, current_next registers */ + "movq -8(%1), %%mm0\n" + "movq (%1), %%mm7\n" + "movq 8(%1), %%mm1\n" + "psrlq $32, %%mm0\n" + "psllq $32, %%mm1\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $32, %%mm2\n" + "psrlq $32, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqd %%mm6, %%mm2\n" + "pcmpeqd %%mm6, %%mm4\n" + "pcmpeqd (%2), %%mm3\n" + "pcmpeqd (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqd %%mm1, %%mm2\n" + "pcmpeqd %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpckldq %%mm4, %%mm2\n" + "punpckhdq %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + /* next */ + "addl $8, %0\n" + "addl $8, %1\n" + "addl $8, %2\n" + "addl $16, %3\n" + + "decl %4\n" + "jnz 0b\n" + "1:\n" + +/* final run */ + /* set the current, current_pre, current_next registers */ + "movq (%1), %%mm1\n" + "movq (%1), %%mm7\n" + "movq -8(%1), %%mm0\n" + "psrlq $32, %%mm1\n" + "psrlq $32, %%mm0\n" + "psllq $32, %%mm1\n" + "movq %%mm7, %%mm2\n" + "movq %%mm7, %%mm3\n" + "psllq $32, %%mm2\n" + "psrlq $32, %%mm3\n" + "por %%mm2, %%mm0\n" + "por %%mm3, %%mm1\n" + + /* current_upper */ + "movq (%0), %%mm6\n" + + /* compute the upper-left pixel for dst on %%mm2 */ + /* compute the upper-right pixel for dst on %%mm4 */ + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "movq %%mm0, %%mm3\n" + "movq %%mm1, %%mm5\n" + "pcmpeqd %%mm6, %%mm2\n" + "pcmpeqd %%mm6, %%mm4\n" + "pcmpeqd (%2), %%mm3\n" + "pcmpeqd (%2), %%mm5\n" + "pandn %%mm2, %%mm3\n" + "pandn %%mm4, %%mm5\n" + "movq %%mm0, %%mm2\n" + "movq %%mm1, %%mm4\n" + "pcmpeqd %%mm1, %%mm2\n" + "pcmpeqd %%mm0, %%mm4\n" + "pandn %%mm3, %%mm2\n" + "pandn %%mm5, %%mm4\n" + "movq %%mm2, %%mm3\n" + "movq %%mm4, %%mm5\n" + "pand %%mm6, %%mm2\n" + "pand %%mm6, %%mm4\n" + "pandn %%mm7, %%mm3\n" + "pandn %%mm7, %%mm5\n" + "por %%mm3, %%mm2\n" + "por %%mm5, %%mm4\n" + + /* set *dst */ + "movq %%mm2, %%mm3\n" + "punpckldq %%mm4, %%mm2\n" + "punpckhdq %%mm4, %%mm3\n" + "movq %%mm2, (%3)\n" + "movq %%mm3, 8(%3)\n" + + : "+r" (src0), "+r" (src1), "+r" (src2), "+r" (dst), "+r" (count) + : + : "cc" + ); +} + +/** + * Scale by a factor of 2 a row of pixels of 8 bits. + * This is a very fast MMX implementation. + * The implementation uses a combination of cmp/and/not operations to + * completly remove the need of conditional jumps. This trick give the + * major speed improvement. + * Also, using the 8 bytes MMX registers more than one pixel are computed + * at the same time. + * Before calling this function you must ensure that the currenct CPU supports + * the MMX instruction set. After calling it you must be sure to call the EMMS + * instruction before any floating-point operation. + * The pixels over the left and right borders are assumed of the same color of + * the pixels on the border. + * Note that the implementation is optimized to write data sequentially to + * maximize the bandwidth on video memory. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. It must + * be at least 16 and a multiple of 8. + * \param dst0 First destination row, double length in pixels. + * \param dst1 Second destination row, double length in pixels. + */ +void scale2x_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + if (count % 8 != 0 || count < 16) { + scale2x_8_def(dst0, dst1, src0, src1, src2, count); + } else { + scale2x_8_mmx_border(dst0, src0, src1, src2, count); + scale2x_8_mmx_border(dst1, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2 a row of pixels of 16 bits. + * This function operates like scale2x_8_mmx() but for 16 bits pixels. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. It must + * be at least 8 and a multiple of 4. + * \param dst0 First destination row, double length in pixels. + * \param dst1 Second destination row, double length in pixels. + */ +void scale2x_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + if (count % 4 != 0 || count < 8) { + scale2x_16_def(dst0, dst1, src0, src1, src2, count); + } else { + scale2x_16_mmx_border(dst0, src0, src1, src2, count); + scale2x_16_mmx_border(dst1, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2 a row of pixels of 32 bits. + * This function operates like scale2x_8_mmx() but for 32 bits pixels. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. It must + * be at least 4 and a multiple of 2. + * \param dst0 First destination row, double length in pixels. + * \param dst1 Second destination row, double length in pixels. + */ +void scale2x_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + if (count % 2 != 0 || count < 4) { + scale2x_32_def(dst0, dst1, src0, src1, src2, count); + } else { + scale2x_32_mmx_border(dst0, src0, src1, src2, count); + scale2x_32_mmx_border(dst1, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2x3 a row of pixels of 8 bits. + * This function operates like scale2x_8_mmx() but with an expansion + * factor of 2x3 instead of 2x2. + */ +void scale2x3_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + if (count % 8 != 0 || count < 16) { + scale2x3_8_def(dst0, dst1, dst2, src0, src1, src2, count); + } else { + scale2x_8_mmx_border(dst0, src0, src1, src2, count); + scale2x_8_def_center(dst1, src0, src1, src2, count); + scale2x_8_mmx_border(dst2, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2x3 a row of pixels of 16 bits. + * This function operates like scale2x_16_mmx() but with an expansion + * factor of 2x3 instead of 2x2. + */ +void scale2x3_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + if (count % 4 != 0 || count < 8) { + scale2x3_16_def(dst0, dst1, dst2, src0, src1, src2, count); + } else { + scale2x_16_mmx_border(dst0, src0, src1, src2, count); + scale2x_16_def_center(dst1, src0, src1, src2, count); + scale2x_16_mmx_border(dst2, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2x3 a row of pixels of 32 bits. + * This function operates like scale2x_32_mmx() but with an expansion + * factor of 2x3 instead of 2x2. + */ +void scale2x3_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + if (count % 2 != 0 || count < 4) { + scale2x3_32_def(dst0, dst1, dst2, src0, src1, src2, count); + } else { + scale2x_32_mmx_border(dst0, src0, src1, src2, count); + scale2x_32_def_center(dst1, src0, src1, src2, count); + scale2x_32_mmx_border(dst2, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2x4 a row of pixels of 8 bits. + * This function operates like scale2x_8_mmx() but with an expansion + * factor of 2x4 instead of 2x2. + */ +void scale2x4_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count) +{ + if (count % 8 != 0 || count < 16) { + scale2x4_8_def(dst0, dst1, dst2, dst3, src0, src1, src2, count); + } else { + scale2x_8_mmx_border(dst0, src0, src1, src2, count); + scale2x_8_def_center(dst1, src0, src1, src2, count); + scale2x_8_def_center(dst2, src0, src1, src2, count); + scale2x_8_mmx_border(dst3, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2x4 a row of pixels of 16 bits. + * This function operates like scale2x_16_mmx() but with an expansion + * factor of 2x4 instead of 2x2. + */ +void scale2x4_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count) +{ + if (count % 4 != 0 || count < 8) { + scale2x4_16_def(dst0, dst1, dst2, dst3, src0, src1, src2, count); + } else { + scale2x_16_mmx_border(dst0, src0, src1, src2, count); + scale2x_16_def_center(dst1, src0, src1, src2, count); + scale2x_16_def_center(dst2, src0, src1, src2, count); + scale2x_16_mmx_border(dst3, src2, src1, src0, count); + } +} + +/** + * Scale by a factor of 2x4 a row of pixels of 32 bits. + * This function operates like scale2x_32_mmx() but with an expansion + * factor of 2x4 instead of 2x2. + */ +void scale2x4_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count) +{ + if (count % 2 != 0 || count < 4) { + scale2x4_32_def(dst0, dst1, dst2, dst3, src0, src1, src2, count); + } else { + scale2x_32_mmx_border(dst0, src0, src1, src2, count); + scale2x_32_def_center(dst1, src0, src1, src2, count); + scale2x_32_def_center(dst2, src0, src1, src2, count); + scale2x_32_mmx_border(dst3, src2, src1, src0, count); + } +} + +#endif + diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h b/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h new file mode 100644 index 00000000..7a50bfc1 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h @@ -0,0 +1,68 @@ +/* + * This file is part of the Scale2x project. + * + * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SCALE2X_H +#define __SCALE2X_H + +typedef unsigned char scale2x_uint8; +typedef unsigned short scale2x_uint16; +typedef unsigned scale2x_uint32; + +void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +void scale2x3_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +void scale2x3_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +void scale2x3_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +void scale2x4_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +void scale2x4_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +void scale2x4_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +#if defined(__GNUC__) && defined(__i386__) + +void scale2x_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +void scale2x_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +void scale2x_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +void scale2x3_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +void scale2x3_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +void scale2x3_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +void scale2x4_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, scale2x_uint8* dst2, scale2x_uint8* dst3, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +void scale2x4_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, scale2x_uint16* dst2, scale2x_uint16* dst3, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +void scale2x4_32_mmx(scale2x_uint32* dst0, scale2x_uint32* dst1, scale2x_uint32* dst2, scale2x_uint32* dst3, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +/** + * End the use of the MMX instructions. + * This function must be called before using any floating-point operations. + */ +static inline void scale2x_mmx_emms(void) +{ + __asm__ __volatile__ ( + "emms" + ); +} + +#endif + +#endif + diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scale3x.c b/apps/nesemu2/src/system/common/filters/scale2x/scale3x.c new file mode 100644 index 00000000..00b4e6cd --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/scale2x/scale3x.c @@ -0,0 +1,706 @@ +/* + * This file is part of the Scale2x project. + * + * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This file contains a C and MMX implementation of the Scale2x effect. + * + * You can find an high level description of the effect at : + * + * http://scale2x.sourceforge.net/ + * + * Alternatively at the previous license terms, you are allowed to use this + * code in your program with these conditions: + * - the program is not used in commercial activities. + * - the whole source code of the program is released with the binary. + * - derivative works of the program are allowed. + */ + +#if HAVE_CONFIG_H +#include +#endif + +#define inline __inline + +#include "scale3x.h" + +#include + +/***************************************************************************/ +/* Scale3x C implementation */ + +/** + * Define the macro USE_SCALE_RANDOMWRITE to enable + * an optimized version which writes memory in random order. + * This version is a little faster if you write in system memory. + * But it's a lot slower if you write in video memory. + * So, enable it only if you are sure to never write directly in video memory. + */ +/* #define USE_SCALE_RANDOMWRITE */ + +static inline void scale3x_8_def_whole(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst0[0] = src1[0]; + dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; + dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + dst2[0] = src1[0]; + dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; + dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst0 += 3; + dst1 += 3; + dst2 += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; + dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; + dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst0 += 3; + dst1 += 3; + dst2 += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst0[2] = src1[0]; + dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; + dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; + dst2[2] = src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } +} + +static inline void scale3x_8_def_border(scale3x_uint8* dst, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0]; + dst[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; + dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst[2] = src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } +} + +static inline void scale3x_8_def_center(scale3x_uint8* dst, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } +} + +static inline void scale3x_16_def_whole(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst0[0] = src1[0]; + dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; + dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + dst2[0] = src1[0]; + dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; + dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst0 += 3; + dst1 += 3; + dst2 += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; + dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; + dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst0 += 3; + dst1 += 3; + dst2 += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst0[2] = src1[0]; + dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; + dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; + dst2[2] = src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } +} + +static inline void scale3x_16_def_border(scale3x_uint16* dst, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0]; + dst[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; + dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst[2] = src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } +} + +static inline void scale3x_16_def_center(scale3x_uint16* dst, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } +} + +static inline void scale3x_32_def_whole(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst0[0] = src1[0]; + dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; + dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + dst2[0] = src1[0]; + dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0]; + dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst0 += 3; + dst1 += 3; + dst2 += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; + dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; + dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst0 += 3; + dst1 += 3; + dst2 += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst0[2] = src1[0]; + dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst1[1] = src1[0]; + dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0]; + dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0]; + dst2[2] = src1[0]; + } else { + dst0[0] = src1[0]; + dst0[1] = src1[0]; + dst0[2] = src1[0]; + dst1[0] = src1[0]; + dst1[1] = src1[0]; + dst1[2] = src1[0]; + dst2[0] = src1[0]; + dst2[1] = src1[0]; + dst2[2] = src1[0]; + } +} + +static inline void scale3x_32_def_border(scale3x_uint32* dst, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = src1[0]; + dst[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0]; + dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst[2] = src1[1] == src0[0] ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = src1[-1] == src0[0] ? src1[-1] : src1[0]; + dst[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0]; + dst[2] = src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } +} + +static inline void scale3x_32_def_center(scale3x_uint32* dst, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count) +{ + assert(count >= 2); + + /* first pixel */ + if (src0[0] != src2[0] && src1[0] != src1[1]) { + dst[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + ++src0; + ++src1; + ++src2; + dst += 3; + + /* central pixels */ + count -= 2; + while (count) { + if (src0[0] != src2[0] && src1[-1] != src1[1]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } + + ++src0; + ++src1; + ++src2; + dst += 3; + --count; + } + + /* last pixel */ + if (src0[0] != src2[0] && src1[-1] != src1[0]) { + dst[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0]; + dst[1] = src1[0]; + dst[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0]; + } else { + dst[0] = src1[0]; + dst[1] = src1[0]; + dst[2] = src1[0]; + } +} + +/** + * Scale by a factor of 3 a row of pixels of 8 bits. + * The function is implemented in C. + * The pixels over the left and right borders are assumed of the same color of + * the pixels on the border. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. + * It must be at least 2. + * \param dst0 First destination row, triple length in pixels. + * \param dst1 Second destination row, triple length in pixels. + * \param dst2 Third destination row, triple length in pixels. + */ +void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale3x_8_def_whole(dst0, dst1, dst2, src0, src1, src2, count); +#else + scale3x_8_def_border(dst0, src0, src1, src2, count); + scale3x_8_def_center(dst1, src0, src1, src2, count); + scale3x_8_def_border(dst2, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 3 a row of pixels of 16 bits. + * This function operates like scale3x_8_def() but for 16 bits pixels. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. + * It must be at least 2. + * \param dst0 First destination row, triple length in pixels. + * \param dst1 Second destination row, triple length in pixels. + * \param dst2 Third destination row, triple length in pixels. + */ +void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale3x_16_def_whole(dst0, dst1, dst2, src0, src1, src2, count); +#else + scale3x_16_def_border(dst0, src0, src1, src2, count); + scale3x_16_def_center(dst1, src0, src1, src2, count); + scale3x_16_def_border(dst2, src2, src1, src0, count); +#endif +} + +/** + * Scale by a factor of 3 a row of pixels of 32 bits. + * This function operates like scale3x_8_def() but for 32 bits pixels. + * \param src0 Pointer at the first pixel of the previous row. + * \param src1 Pointer at the first pixel of the current row. + * \param src2 Pointer at the first pixel of the next row. + * \param count Length in pixels of the src0, src1 and src2 rows. + * It must be at least 2. + * \param dst0 First destination row, triple length in pixels. + * \param dst1 Second destination row, triple length in pixels. + * \param dst2 Third destination row, triple length in pixels. + */ +void scale3x_32_def(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count) +{ +#ifdef USE_SCALE_RANDOMWRITE + scale3x_32_def_whole(dst0, dst1, dst2, src0, src1, src2, count); +#else + scale3x_32_def_border(dst0, src0, src1, src2, count); + scale3x_32_def_center(dst1, src0, src1, src2, count); + scale3x_32_def_border(dst2, src2, src1, src0, count); +#endif +} + diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h b/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h new file mode 100644 index 00000000..14655d00 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h @@ -0,0 +1,33 @@ +/* + * This file is part of the Scale2x project. + * + * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SCALE3X_H +#define __SCALE3X_H + +typedef unsigned char scale3x_uint8; +typedef unsigned short scale3x_uint16; +typedef unsigned scale3x_uint32; + +void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count); +void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count); +void scale3x_32_def(scale3x_uint32* dst0, scale3x_uint32* dst1, scale3x_uint32* dst2, const scale3x_uint32* src0, const scale3x_uint32* src1, const scale3x_uint32* src2, unsigned count); + +#endif + diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c new file mode 100644 index 00000000..422738d6 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c @@ -0,0 +1,516 @@ +/* + * This file is part of the Scale2x project. + * + * Copyright (C) 2003, 2004 Andrea Mazzoleni + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This file contains an example implementation of the Scale effect + * applyed to a generic bitmap. + * + * You can find an high level description of the effect at : + * + * http://scale2x.sourceforge.net/ + * + * Alternatively at the previous license terms, you are allowed to use this + * code in your program with these conditions: + * - the program is not used in commercial activities. + * - the whole source code of the program is released with the binary. + * - derivative works of the program are allowed. + */ + +#if HAVE_CONFIG_H +#include +#endif + +#define inline __inline + +#include "scale2x.h" +#include "scale3x.h" + +#if HAVE_ALLOCA_H +#include +#endif + +#include +#include + +#define SSDST(bits, num) (scale2x_uint##bits *)dst##num +#define SSSRC(bits, num) (const scale2x_uint##bits *)src##num + +/** + * Apply the Scale2x effect on a group of rows. Used internally. + */ +static inline void stage_scale2x(void* dst0, void* dst1, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) +{ + switch (pixel) { +#if defined(__GNUC__) && defined(__i386__) + case 1 : scale2x_8_mmx(SSDST(8,0), SSDST(8,1), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale2x_16_mmx(SSDST(16,0), SSDST(16,1), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale2x_32_mmx(SSDST(32,0), SSDST(32,1), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; +#else + case 1 : scale2x_8_def(SSDST(8,0), SSDST(8,1), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale2x_16_def(SSDST(16,0), SSDST(16,1), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale2x_32_def(SSDST(32,0), SSDST(32,1), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; +#endif + } +} + +/** + * Apply the Scale2x3 effect on a group of rows. Used internally. + */ +static inline void stage_scale2x3(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) +{ + switch (pixel) { +#if defined(__GNUC__) && defined(__i386__) + case 1 : scale2x3_8_mmx(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale2x3_16_mmx(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale2x3_32_mmx(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; +#else + case 1 : scale2x3_8_def(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale2x3_16_def(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale2x3_32_def(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; +#endif + } +} + +/** + * Apply the Scale2x4 effect on a group of rows. Used internally. + */ +static inline void stage_scale2x4(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) +{ + switch (pixel) { +#if defined(__GNUC__) && defined(__i386__) + case 1 : scale2x4_8_mmx(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSDST(8,3), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale2x4_16_mmx(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSDST(16,3), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale2x4_32_mmx(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSDST(32,3), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; +#else + case 1 : scale2x4_8_def(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSDST(8,3), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale2x4_16_def(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSDST(16,3), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale2x4_32_def(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSDST(32,3), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; +#endif + } +} + +/** + * Apply the Scale3x effect on a group of rows. Used internally. + */ +static inline void stage_scale3x(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) +{ + switch (pixel) { + case 1 : scale3x_8_def(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; + case 2 : scale3x_16_def(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; + case 4 : scale3x_32_def(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; + } +} + +/** + * Apply the Scale4x effect on a group of rows. Used internally. + */ +static inline void stage_scale4x(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, const void* src3, unsigned pixel, unsigned pixel_per_row) +{ + stage_scale2x(dst0, dst1, src0, src1, src2, pixel, 2 * pixel_per_row); + stage_scale2x(dst2, dst3, src1, src2, src3, pixel, 2 * pixel_per_row); +} + +#define SCDST(i) (dst+(i)*dst_slice) +#define SCSRC(i) (src+(i)*src_slice) +#define SCMID(i) (mid[(i)]) + +/** + * Apply the Scale2x effect on a bitmap. + * The destination bitmap is filled with the scaled version of the source bitmap. + * The source bitmap isn't modified. + * The destination bitmap must be manually allocated before calling the function, + * note that the resulting size is exactly 2x2 times the size of the source bitmap. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +void scale2x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) +{ + unsigned char* dst = (unsigned char*)void_dst; + const unsigned char* src = (const unsigned char*)void_src; + unsigned count; + unsigned pixel = 4; + + assert(height >= 2); + + count = height; + + stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); + + dst = SCDST(2); + + count -= 2; + while (count) { + stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); + + dst = SCDST(2); + src = SCSRC(1); + + --count; + } + + stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); + +#if defined(__GNUC__) && defined(__i386__) + scale2x_mmx_emms(); +#endif +} + +/** + * Apply the Scale2x3 effect on a bitmap. + * The destination bitmap is filled with the scaled version of the source bitmap. + * The source bitmap isn't modified. + * The destination bitmap must be manually allocated before calling the function, + * note that the resulting size is exactly 2x3 times the size of the source bitmap. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +static void scale2x3(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) +{ + unsigned char* dst = (unsigned char*)void_dst; + const unsigned char* src = (const unsigned char*)void_src; + unsigned count; + + assert(height >= 2); + + count = height; + + stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); + + dst = SCDST(3); + + count -= 2; + while (count) { + stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); + + dst = SCDST(3); + src = SCSRC(1); + + --count; + } + + stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); + +#if defined(__GNUC__) && defined(__i386__) + scale2x_mmx_emms(); +#endif +} + +/** + * Apply the Scale2x4 effect on a bitmap. + * The destination bitmap is filled with the scaled version of the source bitmap. + * The source bitmap isn't modified. + * The destination bitmap must be manually allocated before calling the function, + * note that the resulting size is exactly 2x4 times the size of the source bitmap. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +static void scale2x4(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) +{ + unsigned char* dst = (unsigned char*)void_dst; + const unsigned char* src = (const unsigned char*)void_src; + unsigned count; + + assert(height >= 2); + + count = height; + + stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); + + dst = SCDST(4); + + count -= 2; + while (count) { + stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); + + dst = SCDST(4); + src = SCSRC(1); + + --count; + } + + stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); + +#if defined(__GNUC__) && defined(__i386__) + scale2x_mmx_emms(); +#endif +} + +/** + * Apply the Scale3x effect on a bitmap. + * The destination bitmap is filled with the scaled version of the source bitmap. + * The source bitmap isn't modified. + * The destination bitmap must be manually allocated before calling the function, + * note that the resulting size is exactly 3x3 times the size of the source bitmap. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +void scale3x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) +{ + unsigned char* dst = (unsigned char*)void_dst; + const unsigned char* src = (const unsigned char*)void_src; + unsigned count; + unsigned pixel = 4; + + assert(height >= 2); + + count = height; + + stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); + + dst = SCDST(3); + + count -= 2; + while (count) { + stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); + + dst = SCDST(3); + src = SCSRC(1); + + --count; + } + + stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); +} + +/** + * Apply the Scale4x effect on a bitmap. + * The destination bitmap is filled with the scaled version of the source bitmap. + * The source bitmap isn't modified. + * The destination bitmap must be manually allocated before calling the function, + * note that the resulting size is exactly 4x4 times the size of the source bitmap. + * \note This function requires also a small buffer bitmap used internally to store + * intermediate results. This bitmap must have at least an horizontal size in bytes of 2*width*pixel, + * and a vertical size of 6 rows. The memory of this buffer must not be allocated + * in video memory because it's also read and not only written. Generally + * a heap (malloc) or a stack (alloca) buffer is the best choice. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_mid Pointer at the first pixel of the buffer bitmap. + * \param mid_slice Size in bytes of a buffer bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +static void scale4x_buf(void* void_dst, unsigned dst_slice, void* void_mid, unsigned mid_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) +{ + unsigned char* dst = (unsigned char*)void_dst; + const unsigned char* src = (const unsigned char*)void_src; + unsigned count; + unsigned char* mid[6]; + + assert(height >= 4); + + count = height; + + /* set the 6 buffer pointers */ + mid[0] = (unsigned char*)void_mid; + mid[1] = mid[0] + mid_slice; + mid[2] = mid[1] + mid_slice; + mid[3] = mid[2] + mid_slice; + mid[4] = mid[3] + mid_slice; + mid[5] = mid[4] + mid_slice; + + stage_scale2x(SCMID(-2+6), SCMID(-1+6), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); + stage_scale2x(SCMID(0), SCMID(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); + stage_scale2x(SCMID(2), SCMID(3), SCSRC(1), SCSRC(2), SCSRC(3), pixel, width); + stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(-2+6), SCMID(-2+6), SCMID(-1+6), SCMID(0), pixel, width); + + dst = SCDST(4); + + stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(-1+6), SCMID(0), SCMID(1), SCMID(2), pixel, width); + + dst = SCDST(4); + + count -= 4; + while (count) { + unsigned char* tmp; + + stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(4), pixel, width); + stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width); + + dst = SCDST(4); + src = SCSRC(1); + + tmp = SCMID(0); /* shift by 2 position */ + SCMID(0) = SCMID(2); + SCMID(2) = SCMID(4); + SCMID(4) = tmp; + tmp = SCMID(1); + SCMID(1) = SCMID(3); + SCMID(3) = SCMID(5); + SCMID(5) = tmp; + + --count; + } + + stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(3), pixel, width); + stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width); + + dst = SCDST(4); + + stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(3), SCMID(4), SCMID(5), SCMID(5), pixel, width); + +#if defined(__GNUC__) && defined(__i386__) + scale2x_mmx_emms(); +#endif +} + +/** + * Apply the Scale4x effect on a bitmap. + * The destination bitmap is filled with the scaled version of the source bitmap. + * The source bitmap isn't modified. + * The destination bitmap must be manually allocated before calling the function, + * note that the resulting size is exactly 4x4 times the size of the source bitmap. + * \note This function operates like ::scale4x_buf() but the intermediate buffer is + * automatically allocated in the stack. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +void scale4x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) +{ + unsigned mid_slice; + void* mid; + unsigned pixel = 4; + + mid_slice = 2 * pixel * width; /* required space for 1 row buffer */ + + mid_slice = (mid_slice + 0x7) & ~0x7; /* align to 8 bytes */ + +#if HAVE_ALLOCA + mid = alloca(6 * mid_slice); /* allocate space for 6 row buffers */ + + assert(mid != 0); /* alloca should never fails */ +#else + mid = malloc(6 * mid_slice); /* allocate space for 6 row buffers */ + + if (!mid) + return; +#endif + + scale4x_buf(void_dst, dst_slice, mid, mid_slice, void_src, src_slice, pixel, width, height); + +#if !HAVE_ALLOCA + free(mid); +#endif +} + +/** + * Check if the scale implementation is applicable at the given arguments. + * \param scale Scale factor. 2, 203 (fox 2x3), 204 (for 2x4), 3 or 4. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + * \return + * - -1 on precondition violated. + * - 0 on success. + */ +int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height) +{ + if (pixel != 1 && pixel != 2 && pixel != 4) + return -1; + + switch (scale) { + case 202 : + case 203 : + case 204 : + case 2 : + case 303 : + case 3 : + if (height < 2) + return -1; + break; + case 404 : + case 4 : + if (height < 4) + return -1; + break; + default: + return -1; + } + + if (width < 2) + return -1; + + return 0; +} + +/** + * Apply the Scale effect on a bitmap. + * This function is simply a common interface for ::scale2x(), ::scale3x() and ::scale4x(). + * \param scale Scale factor. 2, 203 (fox 2x3), 204 (for 2x4), 3 or 4. + * \param void_dst Pointer at the first pixel of the destination bitmap. + * \param dst_slice Size in bytes of a destination bitmap row. + * \param void_src Pointer at the first pixel of the source bitmap. + * \param src_slice Size in bytes of a source bitmap row. + * \param pixel Bytes per pixel of the source and destination bitmap. + * \param width Horizontal size in pixels of the source bitmap. + * \param height Vertical size in pixels of the source bitmap. + */ +void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) +{ + switch (scale) { + case 202 : + case 2 : + scale2x(void_dst, dst_slice, void_src, src_slice, width, height); + break; + case 203 : + scale2x3(void_dst, dst_slice, void_src, src_slice, 4, width, height); + break; + case 204 : + scale2x4(void_dst, dst_slice, void_src, src_slice, 4, width, height); + break; + case 303 : + case 3 : + scale3x(void_dst, dst_slice, void_src, src_slice, width, height); + break; + case 404 : + case 4 : + scale4x(void_dst, dst_slice, void_src, src_slice, width, height); + break; + } +} + diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h new file mode 100644 index 00000000..0c1479a5 --- /dev/null +++ b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h @@ -0,0 +1,54 @@ +/* + * This file is part of the Scale2x project. + * + * Copyright (C) 2003 Andrea Mazzoleni + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This file contains an example implementation of the Scale effect + * applyed to a generic bitmap. + * + * You can find an high level description of the effect at : + * + * http://scale2x.sourceforge.net/ + * + * Alternatively at the previous license terms, you are allowed to use this + * code in your program with these conditions: + * - the program is not used in commercial activities. + * - the whole source code of the program is released with the binary. + * - derivative works of the program are allowed. + */ + +#ifndef __SCALEBIT_H +#define __SCALEBIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height); +void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height); +void scale2x(void* void_dst, unsigned dst_slice, void* void_src, unsigned src_slice, unsigned width, unsigned height); +void scale3x(void* void_dst, unsigned dst_slice, void* void_src, unsigned src_slice, unsigned width, unsigned height); +void scale4x(void* void_dst, unsigned dst_slice, void* void_src, unsigned src_slice, unsigned width, unsigned height); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/apps/nesemu2/src/system/input.h b/apps/nesemu2/src/system/input.h new file mode 100644 index 00000000..0f1bc244 --- /dev/null +++ b/apps/nesemu2/src/system/input.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __system__input_h__ +#define __system__input_h__ + +#include "types.h" + +//system keys +#define INPUT_HARDRESET 0x00100000 +#define INPUT_SOFTRESET 0x00200000 +#define INPUT_SCREENSHOT 0x00400000 +#define INPUT_DISKFLIP 0x00800000 +#define INPUT_MENU 0x01000000 +#define INPUT_FASTFORWARD 0x02000000 +#define INPUT_PAGEDOWN 0x04000000 +#define INPUT_PAGEUP 0x08000000 +#define INPUT_LOADSTATE 0x10000000 +#define INPUT_SAVESTATE 0x20000000 +#define INPUT_LOADDISKSTATE 0x40000000 +#define INPUT_SAVEDISKSTATE 0x80000000 + +/*typedef struct inputdata_s { + int mousex,mousey; //x/y coords of the mouse + u8 mouseb; //mouse buttons + u8 keyboard[370]; //keyboard state +} inputdata_t;*/ + +extern int joyx,joyy; //x and y coords for paddle/mouse +extern u8 joyzap; //zapper trigger +extern u8 joykeys[]; //keyboard state +extern u8 joystate[]; //joypad state +extern int joyconfig[4][8]; //joypad keys + +int input_init(); +void input_kill(); +void input_poll(); +int input_poll_mouse(int *x,int *y); +void input_update_config(); + +#endif diff --git a/apps/nesemu2/src/system/linux/stricmp.c b/apps/nesemu2/src/system/linux/stricmp.c new file mode 100644 index 00000000..a71d58b8 --- /dev/null +++ b/apps/nesemu2/src/system/linux/stricmp.c @@ -0,0 +1,16 @@ +#include + +int stricmp(char *s1,char *s2) +{ + char c1,c2; + + for(;*s1 && *s2;s1++,s2++) { + c1 = tolower(*s1); + c2 = tolower(*s2); + if(c1 > c2) + return(1); + if(c1 < c2) + return(-1); + } + return(0); +} diff --git a/apps/nesemu2/src/system/main.h b/apps/nesemu2/src/system/main.h new file mode 100644 index 00000000..0651ee28 --- /dev/null +++ b/apps/nesemu2/src/system/main.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __main_h__ +#define __main_h__ + +//default filename for configuration +#define CONFIG_FILENAME "nesemu2.cfg" + +extern int quit; +extern int running; +extern char configfilename[]; +extern char exepath[]; + +#endif diff --git a/apps/nesemu2/src/system/sdl/console/console.c b/apps/nesemu2/src/system/sdl/console/console.c new file mode 100644 index 00000000..cc37b1d2 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/console/console.c @@ -0,0 +1,335 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//#include +#include +#include "emu/commands.h" +#include "misc/log.h" +#include "misc/memutil.h" +#include "palette/palette.h" +#include "system/input.h" +#include "system/video.h" +#include "system/sdl/console/console.h" +#include "system/sdl/console/font.h" +#include "system/sdl/console/linebuffer.h" + +#define YPOS_STEP 13 + +#define CONSOLE_BGCOLOR 0x0F0FC0 +#define CONSOLE_BORDER 0x0F0F80 + +//showing flag +static int showing; + +//console video buffer +static u32 *screen; + +//width and height of console +static int width,height; + +//y position of bottom of console +static int ypos; + +//cursor position +//static int cursorpos = 0; + +//last message outputted +static char statusmsg[1024]; +static int statustime = 0; + +//input buffer +static char inputbuf[1024]; +static int inputbuflen,inputbufpos; +static int cursorblink; + +void console_loghook(char *str) +{ + linebuffer_add(str); + strncpy(statusmsg,str,1024); + statustime = 60 * 3; +} + +int console_init() +{ + int ret = 0; + + memset(inputbuf,0,1024); + inputbuflen = inputbufpos = 0; + cursorblink = 0; + showing = 0; + width = video_getwidth(); + height = video_getheight() * 3 / 5 + 4; + screen = mem_alloc(width * height * sizeof(u32)); + ret += linebuffer_init((width - 8) / 8); + log_sethook(console_loghook); + log_printf("console init'd: width, height = %d, %d\n",width,height); + return(ret); +} + +void console_kill() +{ + log_sethook(0); + linebuffer_kill(); + if(screen) + mem_free(screen); +} + +void console_draw(u32 *dest,int w,int h) +{ + int x,y,i,n; + u32 *src = screen; + u32 pixel; + + w /= 4; + //clear console screen + for(y=0;y<(height-4);y++) { + for(x=0;x> 3) & 0x1F1F1F; + pixel |= src[x] & 0xE0E0E0; + dest[x] = pixel; + } + dest += w; + src += width; + } + + //show status message + if(ypos == 0 && statustime) { + statusmsg[w / 8] = 0; + font_drawstr(statusmsg,dest,w); + } +} + +void console_print(char *str) +{ + +} + +void console_show() +{ + showing = 1; +} + +void console_hide() +{ + showing = 0; +} + +//call 60 times a second +#if 0 +void console_update() +{ + static int keydown = 0; + + if(joykeys[SDLK_BACKQUOTE] && (keydown & 1) == 0) { + if(showing == 0) + console_show(); + else + console_hide(); + keydown |= 1; + } + else if(joykeys[SDLK_BACKQUOTE] == 0) + keydown &= ~1; + if(showing) { + if(ypos < height) ypos += YPOS_STEP; + if(ypos > height) ypos = height; + } + else { + if(ypos > 0) ypos -= YPOS_STEP; + if(ypos < 0) ypos = 0; + } + if(statustime) + statustime--; + cursorblink = (cursorblink + 1) & 0x3F; +// cursorblink++; +} + +//static int isignored(int ch) +//{ +// if (ch == '`' || ch == '~') +// return(1); +// return(0); +//} + +static int isletter(int ch) +{ + if (ch >= SDLK_a && ch <= SDLK_z) + return(1); + return(0); +} + +static int isnumber(int ch) +{ + if (ch >= SDLK_0 && ch <= SDLK_9) + return(1); + return(0); +} + +void console_keyevent(int state,int sym) +{ + int modstate = SDL_GetModState(); + int isshift = modstate & (KMOD_LSHIFT | KMOD_RSHIFT); + int iscaps = modstate & KMOD_CAPS; + int i,ch = -1; + + //ignore keyup events + if(state == SDL_KEYUP) + return; + + //if shift is pressed invert caps lock state + if(isshift) + iscaps ^= KMOD_CAPS; + + //cursor left + if (sym == SDLK_LEFT) { + if (inputbufpos) + inputbufpos--; + return; + } + + //cursor right + else if (sym == SDLK_RIGHT) { + if (inputbufpos < inputbuflen) + inputbufpos++; + return; + } + + //if this is a letter + else if(isletter(sym) || isnumber(sym)) { + ch = sym - SDLK_a; + if(iscaps) + ch += 'A'; + else + ch += 'a'; + } + + //if this is a printable character and we are not ignoring it +// else if(isprint(sym) && isignored(sym) == 0) { +// ch = sym; +// } + + //backspace + else if(sym == SDLK_BACKSPACE) { + if(inputbufpos) { + inputbuflen--; + inputbufpos--; + for(i=inputbufpos;i? + @ABCDEFGHIJKLMNO + PQRSTUVWXYZ[\]^_ + `abcdefghijklmno + pqrstuvwxyz{|}~ +*/ + +u8 fontmap[] = { + ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', + '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?', + '@','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','{','|','}','~',' ', + 0 +}; + +//dimensions: w=128 h=48 bpp=1 +u8 fontbits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00, + 0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00, + 0x00,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x00, + 0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00, + 0x62,0x66,0x0C,0x18,0x30,0x66,0x46,0x00, + 0x1C,0x36,0x1C,0x38,0x6B,0x66,0x3B,0x00, + 0x0C,0x0C,0x0C,0x00,0x00,0x00,0x00,0x00, + 0x06,0x0C,0x18,0x18,0x18,0x0C,0x06,0x00, + 0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00, + 0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00, + 0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60, + 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00, + 0x02,0x06,0x0C,0x18,0x30,0x60,0x40,0x00, + 0x38,0x4C,0xCE,0xC6,0xE6,0x64,0x38,0x00, + 0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00, + 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00, + 0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00, + 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00, + 0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00, + 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00, + 0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00, + 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00, + 0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00, + 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, + 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x60, + 0x00,0x18,0x30,0x60,0x30,0x18,0x00,0x00, + 0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00, + 0x00,0x18,0x0C,0x06,0x0C,0x18,0x00,0x00, + 0x7C,0xC6,0x06,0x1C,0x30,0x00,0x30,0x00, + 0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3E,0x00, + 0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00, + 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00, + 0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00, + 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00, + 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00, + 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00, + 0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00, + 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00, + 0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00, + 0x1E,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00, + 0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x00, + 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00, + 0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00, + 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00, + 0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, + 0xFC,0xC6,0xC6,0xFC,0xC0,0xC0,0xC0,0x00, + 0x7C,0xC6,0xC6,0xC6,0xDA,0xCC,0x76,0x00, + 0xFC,0xC6,0xC6,0xFC,0xD8,0xCC,0xC6,0x00, + 0x7C,0xC6,0xC0,0x7C,0x06,0xC6,0x7C,0x00, + 0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x00, + 0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, + 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00, + 0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00, + 0xC6,0xEE,0x7C,0x38,0x7C,0xEE,0xC6,0x00, + 0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00, + 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00, + 0x1E,0x18,0x18,0x18,0x18,0x18,0x1E,0x00, + 0x40,0x60,0x30,0x18,0x0C,0x06,0x02,0x00, + 0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00, + 0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00, + 0xC0,0x60,0x30,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x78,0x0C,0x7C,0xCC,0x76,0x00, + 0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xF8,0x00, + 0x00,0x00,0x78,0xCC,0xC0,0xCC,0x78,0x00, + 0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x7C,0x00, + 0x00,0x00,0x78,0xCC,0xFC,0xC0,0x78,0x00, + 0x1C,0x30,0x30,0x78,0x30,0x30,0x30,0x00, + 0x00,0x00,0x7C,0xCC,0xCC,0x7C,0x0C,0x78, + 0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xCC,0x00, + 0x00,0x30,0x00,0x30,0x30,0x30,0x30,0x00, + 0x00,0x18,0x00,0x18,0x18,0x18,0x18,0x70, + 0xC0,0xC0,0xCC,0xD8,0xF0,0xD8,0xCC,0x00, + 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00, + 0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0x00, + 0x00,0x00,0xD8,0xEC,0xCC,0xCC,0xCC,0x00, + 0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00, + 0x00,0x00,0xF8,0xCC,0xCC,0xF8,0xC0,0xC0, + 0x00,0x00,0x7C,0xCC,0xCC,0x7C,0x0C,0x0C, + 0x00,0x00,0xD8,0xEC,0xC0,0xC0,0xC0,0x00, + 0x00,0x00,0x78,0xC0,0x78,0x0C,0xF8,0x00, + 0x00,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00, + 0x00,0x00,0xCC,0xCC,0xCC,0xDC,0x6C,0x00, + 0x00,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x00, + 0x00,0x00,0xD6,0xD6,0xD6,0xFE,0x6C,0x00, + 0x00,0x00,0xCC,0x78,0x30,0x78,0xCC,0x00, + 0x00,0x00,0xCC,0xCC,0xCC,0x7C,0x0C,0x78, + 0x00,0x00,0xFC,0x18,0x30,0x60,0xFC,0x00, + 0x0E,0x18,0x18,0x30,0x18,0x18,0x0E,0x00, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00, + 0x70,0x18,0x18,0x0C,0x18,0x18,0x70,0x00, + 0x60,0xF2,0x9E,0x0C,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; diff --git a/apps/nesemu2/src/system/sdl/console/fontdata.h b/apps/nesemu2/src/system/sdl/console/fontdata.h new file mode 100644 index 00000000..420b33c4 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/console/fontdata.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __fontdata_h__ +#define __fontdata_h__ + +#include "types.h" + +extern u8 fontmap[]; +extern u8 fontbits[]; + +#endif diff --git a/apps/nesemu2/src/system/sdl/console/linebuffer.c b/apps/nesemu2/src/system/sdl/console/linebuffer.c new file mode 100644 index 00000000..9835b935 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/console/linebuffer.c @@ -0,0 +1,139 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "misc/memutil.h" +#include "system/sdl/console/linebuffer.h" + +#define CONSOLE_LINEBUF_SIZE 128 + +//line buffer (circular) +static char **linebuf = 0; + +//max width of strings in the buffer (for chopping them) +static size_t maxlen; + +//next available line for use in the buffer +static int nextline; + +//is the buffer full? are we circling? +static int full; + +static void increment_nextline() +{ + nextline++; + if(nextline == CONSOLE_LINEBUF_SIZE) { + nextline = 0; + full = 1; + } +} + +static void freeline(int n) +{ + if(linebuf[n]) + mem_free(linebuf[n]); + linebuf[n] = 0; +} + +int linebuffer_init(int len) +{ + linebuf = (char**)mem_alloc(sizeof(char*) * CONSOLE_LINEBUF_SIZE); + memset(linebuf,0,sizeof(char*) * CONSOLE_LINEBUF_SIZE); + nextline = 0; + maxlen = len; + return(0); +} + +void linebuffer_kill() +{ + if(linebuf) { + linebuffer_clear(); + mem_free(linebuf); + } +} + +static void addline(char *str) +{ + freeline(nextline); + linebuf[nextline] = mem_strdup(str); + increment_nextline(); +} +void linebuffer_add(char *str) +{ + //free line (if it is already used) + if(strlen(str) < maxlen) { + addline(str); + } + else { + char *tmp = mem_strdup(str); + char *tmpp,*p = tmp; + size_t len = strlen(p); + + while(len >= maxlen) { + tmpp = mem_strdup(p); + tmpp[maxlen] = 0; + addline(tmpp); + mem_free(tmpp); + p[maxlen-1] = 0; +// printf("adding wrapped line: '%s' (len = %d, maxlen = %d)\n",p,len,maxlen); + p += maxlen; + len -= maxlen; + } +// printf("adding wrapped line (tail): '%s' (len = %d, maxlen = %d)\n",p,len,maxlen); + addline(p); + mem_free(tmp); + } +} + +void linebuffer_clear() +{ + int i; + + for(i=0;i= CONSOLE_LINEBUF_SIZE) + return(0); + return(linebuf[index]); +} + +//get a line relative to the last line added +char *linebuffer_getrelative(int index) +{ + int line = nextline - (1 + index); + + if(line < 0) + line = CONSOLE_LINEBUF_SIZE - (1 + index); + return(linebuffer_get(line)); +} + +//returns the number of lines used +int linebuffer_count() +{ + return(full ? CONSOLE_LINEBUF_SIZE : nextline); +} diff --git a/apps/nesemu2/src/system/sdl/console/linebuffer.h b/apps/nesemu2/src/system/sdl/console/linebuffer.h new file mode 100644 index 00000000..9459c402 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/console/linebuffer.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __linebuffer_h__ +#define __linebuffer_h__ + +int linebuffer_init(int len); +void linebuffer_kill(); +void linebuffer_add(char *str); +void linebuffer_clear(); +char *linebuffer_get(int index); +char *linebuffer_getrelative(int index); +int linebuffer_count(); + +#endif diff --git a/apps/nesemu2/src/system/sdl/input.c b/apps/nesemu2/src/system/sdl/input.c new file mode 100644 index 00000000..f5eba632 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/input.c @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//#include +#include "system/input.h" +#include "system/system.h" +#include "misc/config.h" +#include +#include +#include + +/* +todo: rewrite input. only need a few global input variables: + +todo: new input system. stores input data into the nes struct + +required variables: + mouse x,y + mouse buttons + keyboard state + joystick state (merged into keyboard state, possibly) +*/ + +//these global variables provide information for the device input code +int joyx,joyy; //x and y coords for paddle/mouse +u8 joytrigger; +u8 joykeys[370]; //keyboard state +int joyconfig[4][8]; //joypad button configuration + +// this will map joystick axises/buttons to unused keyboard buttons +#define FIRSTJOYSTATEKEY (350) // ideally should be SDLK_LAST +u8 joystate[32]; // dpad + 8 buttons is enuff' for me but let's be sure :-) + +int input_init() +{ + int i; + + //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL); + for(i=0;i<20;i++) { + joystate[i] = 0; + } + input_update_config(); + return(0); +} + +void input_kill() +{ +} + +void input_poll() +{ +#if 0 + Uint8 *keystate = SDL_GetKeyState(NULL); + int i,x,y; + + //need to update mousex/mousey/mousebuttons here + joytrigger = (u8)(SDL_GetMouseState(&x,&y) & 1) << 4; + joyx = x; + joyy = y; + + //now update key/mouse state, the input device logic will + //decode the key/mouse data into the correct input for the nes + for(i=0;i<300;i++) + joykeys[i] = keystate[i]; + + //put joypad buttons in the struct + for(i=0;i<20;i++) { + joykeys[FIRSTJOYSTATEKEY + i] = joystate[i]; + } +#endif + int keycode; + do { +#define KEYDOWN_MASK 0x8000 + int key = read_key(); + keycode = key & ~KEYDOWN_MASK; + int keydown = (key & KEYDOWN_MASK) != 0; + if (keycode != _KEY_NONE) { + printf("key = %x\n", key); + } + switch (keycode) { + case _KEY_J: joykeys['j'] = keydown; break; + case _KEY_K: joykeys['k'] = keydown; break; + case _KEY_U: joykeys['u'] = keydown; break; + case _KEY_I: joykeys['i'] = keydown; break; + case _KEY_W: joykeys['w'] = keydown; break; + case _KEY_S: joykeys['s'] = keydown; break; + case _KEY_A: joykeys['a'] = keydown; break; + case _KEY_D: joykeys['d'] = keydown; break; + } + } while (keycode != _KEY_NONE); +} + +extern int video_getxoffset(); +extern int video_getyoffset(); +extern int video_getscale(); + +int input_poll_mouse(int *x,int *y) +{ + return 0; +} + +void input_update_config() +{ + joyconfig[0][0] = config_get_int("input.joypad0.a"); + joyconfig[0][1] = config_get_int("input.joypad0.b"); + joyconfig[0][2] = config_get_int("input.joypad0.select"); + joyconfig[0][3] = config_get_int("input.joypad0.start"); + joyconfig[0][4] = config_get_int("input.joypad0.up"); + joyconfig[0][5] = config_get_int("input.joypad0.down"); + joyconfig[0][6] = config_get_int("input.joypad0.left"); + joyconfig[0][7] = config_get_int("input.joypad0.right"); + joyconfig[1][0] = config_get_int("input.joypad1.a"); + joyconfig[1][1] = config_get_int("input.joypad1.b"); + joyconfig[1][2] = config_get_int("input.joypad1.select"); + joyconfig[1][3] = config_get_int("input.joypad1.start"); + joyconfig[1][4] = config_get_int("input.joypad1.up"); + joyconfig[1][5] = config_get_int("input.joypad1.down"); + joyconfig[1][6] = config_get_int("input.joypad1.left"); + joyconfig[1][7] = config_get_int("input.joypad1.right"); + + int i; + for (i = 0; i < 8; i ++) { + printf("joyconfig[0][%d] = %d\n", i, joyconfig[0][i]); + } + + printf("joyconfig[0][1] = %d\n", joyconfig[0][1]); +} diff --git a/apps/nesemu2/src/system/sdl/main.c b/apps/nesemu2/src/system/sdl/main.c new file mode 100644 index 00000000..a782362a --- /dev/null +++ b/apps/nesemu2/src/system/sdl/main.c @@ -0,0 +1,178 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//#include +#include +#include "version.h" +#include "emu/emu.h" +#include "emu/commands.h" +#include "emu/events.h" +#include "misc/log.h" +#include "misc/config.h" +#include "misc/paths.h" +#include "misc/memutil.h" +#include "nes/nes.h" +#include "system/main.h" +#include "system/system.h" +#include "system/video.h" +#include "system/input.h" +#include "palette/palette.h" +#include "palette/generator.h" +#include "system/sdl/console/console.h" + +#include +#include + +//required +char configfilename[1024] = CONFIG_FILENAME; +char exepath[1024] = ""; + +#if 0 +static void usage(char *argv0) +{ + printf("\nnesemu2 v%s - Copyright 2013 James Holodnak\n\n",VERSION); + printf("Usage: %s [options] filename\n\n",argv0); + printf("Supported ROM formats: iNES, NES 2.0, UNIF, FDS, NSF\n\n"); + printf("Options:\n\n"); + printf(" --help : Show this message and exit.\n"); + printf(" --mappers : Show supported mappers and exit.\n"); + printf(" --config : Use 'file' as configuration file.\n"); + printf(" --patch : Specify patch file for ROM.\n"); + printf(" --movie : Specify movie file.\n"); + printf(" --record : After loading rom, start recording movie (used with --movie).\n"); + printf(" --recordtest : After loading rom, start recording test (used with --movie).\n"); + printf(" --test : Specify automated testing script.\n"); + printf("\n"); +} +#endif + +//todo: this is getting ugly +int main(int argc,char *argv[]) +{ + //int i; + int ret; + int recordmovie = 0; + //char *p; + char romfilename[1024] = ""; + char patchfilename[1024] = ""; + char moviefilename[1024] = ""; + char testfilename[1024] = ""; + + _ioe_init(); + + //clear the tmp strings and configfile string + memset(romfilename,0,1024); + memset(patchfilename,0,1024); + memset(moviefilename,0,1024); + memset(configfilename,0,1024); + memset(testfilename,0,1024); + +#if 0 + //make the exe path variable + strcpy(exepath,argv[0]); + if((p = strrchr(exepath,PATH_SEPERATOR)) != 0) { + *p = 0; + } + + //process the command line + for(i=1;icart == 0) + log_printf("main: cannot record movie without rom loaded.\n"); + else { + emu_event(E_SAVEMOVIE,(void*)moviefilename); + emu_event(E_RECORDMOVIE,0); + nes->movie.mode |= (recordmovie > 1) ? MOVIE_TEST : 0; + } + } + else { + emu_event(E_LOADMOVIE,(void*)moviefilename); + emu_event(E_PLAYMOVIE,0); + } + } + + //begin automated tests + if(strcmp(testfilename,"") != 0) + ret = emu_mainloop_test(testfilename); + + //or begin the main loop + else + ret = emu_mainloop(); + + //destroy emulator + emu_kill(); + + //return to os + return(emu_exit(ret)); +} diff --git a/apps/nesemu2/src/system/sdl/sound.c b/apps/nesemu2/src/system/sdl/sound.c new file mode 100644 index 00000000..84327329 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/sound.c @@ -0,0 +1,411 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#if 0 +#include +#include "types.h" +#include "misc/log.h" +#include "misc/config.h" +#include "misc/memutil.h" +#include "system/sound.h" + +static int sound_samplerate = 44100; + +// number of samples per SDL callback +#define SDL_XFER_SAMPLES 512 +#define MAX_AUDIO_LATENCY 4 + +static int sdl_xfer_samples = SDL_XFER_SAMPLES; +static int stream_in_initialized = 0; +static int stream_loop = 0; + +static int initialized_audio = 0; +static int buf_locked; + +static s8 *stream_buffer; +static volatile s32 stream_playpos; + +static int stream_buffer_size; +static int stream_buffer_in; + +// buffer over/underflow counts +static int buffer_underflows; +static int buffer_overflows; + +// sound enable +static int snd_enabled = 1; + +static int sdl_create_buffers(void); +static void sdl_destroy_buffers(void); +static void sdl_cleanup_audio(); +static void SDLCALL sdl_callback(void *userdata, Uint8 *stream, int len); + +static void sdl_callback(void *userdata, Uint8 *stream, int len) +{ + int len1, len2, sb_in; + + sb_in = stream_buffer_in; + if (stream_loop) + sb_in += stream_buffer_size; + + if (sb_in < (stream_playpos+len)) + { + return; + } + else if ((stream_playpos+len) > stream_buffer_size) + { + len1 = stream_buffer_size - stream_playpos; + len2 = len - len1; + } + else + { + len1 = len; + len2 = 0; + } + + if (snd_enabled) + { + memcpy(stream, stream_buffer + stream_playpos, len1); + memset(stream_buffer + stream_playpos, 0, len1); // no longer needed + if (len2) + { + memcpy(stream+len1, stream_buffer, len2); + memset(stream_buffer, 0, len2); // no longer needed + } + + } + else + { + memset(stream, 0, len); + } + + // move the play cursor + stream_playpos += len1 + len2; + if (stream_playpos >= stream_buffer_size) + { + stream_playpos -= stream_buffer_size; + stream_loop = 0; + } +} + +//============================================================ +// lock_buffer +//============================================================ +static INLINE int lock_buffer(long offset, long size, void **buffer1, long *length1, void **buffer2, long *length2) +{ +// volatile long pstart, pend, lstart, lend; + + if (!buf_locked) + { +/* if (machine.video().throttled()) + { + pstart = stream_playpos; + pend = (pstart + sdl_xfer_samples); + lstart = offset; + lend = lstart+size; + while (((pstart >= lstart) && (pstart <= lend)) || + ((pend >= lstart) && (pend <= lend))) + { + pstart = stream_playpos; + pend = pstart + sdl_xfer_samples; + } + }*/ + + SDL_LockAudio(); + buf_locked++; + } + + // init lengths + *length1 = *length2 = 0; + + if ((offset + size) > stream_buffer_size) + { + // 2-piece case + *length1 = stream_buffer_size - offset; + *buffer1 = &stream_buffer[offset]; + *length2 = size - *length1; + *buffer2 = stream_buffer; + } + else + { + // normal 1-piece case + *length1 = size; + *buffer1 = &stream_buffer[offset]; + } + + return 0; +} + +//============================================================ +// unlock_buffer +//============================================================ +static INLINE void unlock_buffer(void) +{ + buf_locked--; + if(buf_locked == 0) + SDL_UnlockAudio(); +} + +static INLINE void copy_sample_data(s16 *data, int bytes_to_copy) +{ + void *buffer1, *buffer2 = (void *)NULL; + long length1, length2; + int cur_bytes; + + // attempt to lock the stream buffer + if (lock_buffer(stream_buffer_in, bytes_to_copy, &buffer1, &length1, &buffer2, &length2) < 0) + { + buffer_underflows++; + return; + } + + // adjust the input pointer + stream_buffer_in += bytes_to_copy; + if (stream_buffer_in >= stream_buffer_size) + { + stream_buffer_in -= stream_buffer_size; + stream_loop = 1; + } + + // copy the first chunk + cur_bytes = (bytes_to_copy > length1) ? length1 : bytes_to_copy; + memcpy(buffer1, data, cur_bytes); + + // adjust for the number of bytes + bytes_to_copy -= cur_bytes; + data = (s16 *)((u8 *)data + cur_bytes); + + // copy the second chunk + if (bytes_to_copy != 0) + { + cur_bytes = (bytes_to_copy > length2) ? length2 : bytes_to_copy; + memcpy(buffer2, data, cur_bytes); + } + + // unlock + unlock_buffer(); +} + +static INLINE void update_audio_stream(s16 *buffer, int samples_this_frame) +{ + // if nothing to do, don't do it + if (sound_samplerate != 0 && stream_buffer) + { + int bytes_this_frame = samples_this_frame * sizeof(s16); + int play_position, write_position, stream_in; + //int orig_write; // used in LOG + + play_position = stream_playpos; + + write_position = stream_playpos + ((sound_samplerate / 60) * sizeof(s16)); + //orig_write = write_position; + + if (!stream_in_initialized) + { + stream_in = stream_buffer_in = (write_position + stream_buffer_size); + + log_printf("stream_in = %d, ", (int)stream_in); + log_printf("stream_playpos = %d, ", (int)stream_playpos); + log_printf("write_position = %d\n", (int)write_position); + + // start playing + SDL_PauseAudio(0); + + stream_in_initialized = 1; + } + else + { + // normalize the stream in position + stream_in = stream_buffer_in; + if (stream_in < write_position && stream_loop == 1) + stream_in += stream_buffer_size; + + // now we should have, in order: + // <------pp---wp---si---------------> + + // if we're between play and write positions, then bump forward, but only in full chunks + while (stream_in < write_position) + { +// log_printf("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); + buffer_underflows++; + stream_in += bytes_this_frame; + } + + // if we're going to overlap the play position, just skip this chunk + if (stream_in + bytes_this_frame > play_position + stream_buffer_size) + { +// log_printf("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); + buffer_overflows++; + return; + } + } + + if (stream_in >= stream_buffer_size) + { + stream_in -= stream_buffer_size; + stream_loop = 1; + } + + // now we know where to copy; let's do it + stream_buffer_in = stream_in; + copy_sample_data(buffer, bytes_this_frame); + } +} + + +static void sdl_cleanup_audio() +{ + // if nothing to do, don't do it + if (sound_samplerate == 0) + return; + + // kill the buffers and dsound + sdl_destroy_buffers(); + + // print out over/underflow stats + if(buffer_overflows || buffer_underflows) + log_printf("sdl_cleanup_audio: overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows); +} + +//============================================================ +// dsound_create_buffers +//============================================================ + +static int sdl_create_buffers(void) +{ + log_printf("sdl_create_buffers: creating stream buffer of %u bytes\n", stream_buffer_size); + stream_buffer = (s8*)mem_alloc(stream_buffer_size); + stream_playpos = 0; + buf_locked = 0; + return 0; +} + +//============================================================ +// sdl_destroy_buffers +//============================================================ + +static void sdl_destroy_buffers(void) +{ + // release the buffer + if(stream_buffer) + mem_free(stream_buffer); + stream_buffer = NULL; +} + +int sound_init() +{ + int audio_latency; + SDL_AudioSpec aspec, obtained; + char audio_driver[16] = ""; + + if(config_get_bool("sound.enabled") == 0) + return(0); + + if(initialized_audio) { + sound_kill(); + } + + log_printf("sound_init: Start initialization\n"); + SDL_AudioDriverName(audio_driver,sizeof(audio_driver)); + log_printf("sound_init: Driver is %s\n",audio_driver); + initialized_audio = 0; + sdl_xfer_samples = SDL_XFER_SAMPLES; + stream_in_initialized = 0; + stream_loop = 0; + + // set up the audio specs + aspec.freq = sound_samplerate; + aspec.format = AUDIO_S16SYS; // keep endian independent + aspec.channels = 1; + aspec.samples = sdl_xfer_samples; + aspec.callback = sdl_callback; + aspec.userdata = 0; + + if(SDL_OpenAudio(&aspec,&obtained) < 0) + goto cant_start_audio; + + initialized_audio = 1; + snd_enabled = 1; + + log_printf("sound_init: frequency: %d, channels: %d, samples: %d\n",obtained.freq,obtained.channels,obtained.samples); + + sdl_xfer_samples = obtained.samples; + + audio_latency = 1; + + // compute the buffer sizes + stream_buffer_size = sound_samplerate * sizeof(s16) * audio_latency / MAX_AUDIO_LATENCY; + stream_buffer_size = (stream_buffer_size / 1024) * 1024; + if(stream_buffer_size < 1024) + stream_buffer_size = 1024; + + // create the buffers + if (sdl_create_buffers()) + goto cant_create_buffers; + + log_printf("sound_init: End initialization, frame latency = %d\n",audio_latency); + return(0); + + // error handling +cant_create_buffers: +cant_start_audio: + log_printf("sound_init: Initialization failed. SDL error: %s\n", SDL_GetError()); + return(1); +} + +void sound_kill() +{ + if(initialized_audio) { + log_printf("sound_kill: closing audio\n"); + SDL_CloseAudio(); + sdl_cleanup_audio(); + } +} + +void sound_play() +{ + if(initialized_audio) + SDL_PauseAudio(0); +} + +void sound_pause() +{ + if(initialized_audio) + SDL_PauseAudio(1); +} + +void sound_update(void *buf,int size) +{ + if(initialized_audio) + update_audio_stream((s16*)buf,size); +} + +void sound_setfps(int fps) +{ + +} +#endif + +int sound_init() { return 0; } +void sound_kill() { } +void sound_play() { } +void sound_pause() { } +void sound_update(void *buf,int size) { } +void sound_setfps(int fps) { } diff --git a/apps/nesemu2/src/system/sdl/system.c b/apps/nesemu2/src/system/sdl/system.c new file mode 100644 index 00000000..9efde26c --- /dev/null +++ b/apps/nesemu2/src/system/sdl/system.c @@ -0,0 +1,178 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Dead_Body * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include + +uint32_t uptime(); + +#ifndef _MAX_PATH + #define _MAX_PATH 4096 +#endif + + +#if 0 +#include +#include "emu/events.h" +#include "system/main.h" +#include "system/video.h" +#include "system/input.h" +#include "system/sound.h" +#include "system/sdl/console/console.h" +#include "nes/nes.h" +#include "nes/state/state.h" +#include "misc/paths.h" +#include "misc/log.h" +#include "misc/config.h" + +static SDL_Joystick *joystick = 0; + +int system_init() +{ + if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { + printf("error at sdl init!\n"); + return(1); + } + if(SDL_NumJoysticks() > 0) { + SDL_JoystickEventState(SDL_ENABLE); + joystick = SDL_JoystickOpen(0); + } + return(0); +} + +void system_kill() +{ + if(joystick) + SDL_JoystickClose(joystick); + SDL_Quit(); +} + +#define bit(n) (1 << (n)) +#define checkkey(key,n,evt) \ + if(joykeys[key] && (keydown & bit(n)) == 0) { \ + keydown |= bit(n); \ + emu_event(evt,0); \ + } \ + else if(joykeys[key] == 0) { \ + keydown &= ~bit(n); \ + } + +void system_checkevents() +{ + static int keydown = 0; + SDL_Event event; /* Event structure */ + + //check out sdl events + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + quit++; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + console_keyevent(event.key.type,event.key.keysym.sym); + break; + case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ + if (event.jaxis.axis == 0) + { + joystate[0] = 0; + joystate[1] = 0; + if(event.jaxis.value < -3200) + joystate[0] = 1; + else if (event.jaxis.value > 3200) + joystate[1] = 1; + } + else if (event.jaxis.axis == 1) + { + joystate[2] = 0; + joystate[3] = 0; + if (event.jaxis.value < -3200) + joystate[2] = 1; + else if (event.jaxis.value > 3200) + joystate[3] = 1; + } + break; + case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */ + joystate[event.jbutton.button + 4] = 1; + break; + case SDL_JOYBUTTONUP: /* Handle Joystick Button Releases */ + joystate[event.jbutton.button + 4] = 0; + break; + } + } + + //update the console + console_update(); + + //check for system key presses + checkkey(SDLK_ESCAPE, 0, E_QUIT); + checkkey(SDLK_F1, 1, E_TOGGLERUNNING); + checkkey(SDLK_F4, 2, E_TOGGLEFULLSCREEN); + checkkey(SDLK_F5, 3, E_SAVESTATE); + checkkey(SDLK_F8, 4, E_LOADSTATE); + checkkey(SDLK_F9, 5, E_FLIPDISK); + +// checkkey(SDLK_p, 1, E_SOFTRESET); +// checkkey(SDLK_o, 2, E_HARDRESET); +// checkkey(SDLK_F3, 4, E_PLAYMOVIE); +// checkkey(SDLK_F6, 7, E_RECORDMOVIE); +// checkkey(SDLK_F7, 8, E_STOPMOVIE); +// checkkey(SDLK_F10, 11, E_DUMPDISK); +} + +char *system_getcwd() +{ + static char buf[_MAX_PATH]; + + if(getcwd(buf,_MAX_PATH) == NULL) + memset(buf,0,_MAX_PATH); + return(buf); +} + +u64 system_gettick() +{ + return(SDL_GetTicks()); +} + +u64 system_getfrequency() +{ + return(1000); +} + +#else + +int system_init() { return 0; } +void system_kill() { } + +void system_checkevents() { } + +char *system_getcwd() +{ + static char buf[_MAX_PATH]; + + if(getcwd(buf,_MAX_PATH) == NULL) + memset(buf,0,_MAX_PATH); + return(buf); +} + +uint64_t system_gettick() { return uptime(); } +uint64_t system_getfrequency() { return 1000; } +#endif diff --git a/apps/nesemu2/src/system/sdl/video.c b/apps/nesemu2/src/system/sdl/video.c new file mode 100644 index 00000000..f2d3d4e9 --- /dev/null +++ b/apps/nesemu2/src/system/sdl/video.c @@ -0,0 +1,406 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//#include +#include +#include "misc/log.h" +#include "palette/palette.h" +#include "nes/nes.h" +#include "system/system.h" +#include "system/video.h" +#include "system/sdl/console/console.h" +#include "misc/memutil.h" +#include "misc/config.h" +#include "system/common/filters.h" + +//system related variables +//static SDL_Surface *surface = 0; +//static int flags = 0; //SDL_DOUBLEBUF | SDL_HWSURFACE; +static int screenw,screenh,screenbpp; +static int screenscale; + +//palette with emphasis applied +static u8 palette[8][64 * 3]; + +//palette data fed to video system +static u32 palette32[8][256]; //32 bit color + +//caches of all available colors +static u16 palettecache16[256]; +static u32 palettecache32[256]; + +//actual values written to nes palette ram +static u8 palettecache[32]; + +//for frame limiting +static double interval = 0; +static u64 lasttime = 0; + +//pointer to scree and copy of the nes screen +static u32 *screen = 0; +static u8 *nesscreen = 0; + +//draw function pointer and pointer to current video filter +static void (*drawfunc)(void*,u32,void*,u32,u32,u32); //dest,destpitch,src,srcpitch,width,height +static filter_t *filter; + +//for correct colors +//static int rshift,gshift,bshift; +//static int rloss,gloss,bloss; +#define rshift 16 +#define gshift 8 +#define bshift 0 + +static int find_drawfunc(int scale,int bpp) +{ + int i; + + for(i=0;filter->modes[i].scale;i++) { + if(filter->modes[i].scale == scale) { + switch(bpp) { + case 32: + drawfunc = filter->modes[i].draw32; + return(0); + case 16: + case 15: + // drawfunc = filter->modes[i].draw16; + // return(0); + default: + log_printf("find_drawfunc: unsupported bit depth (%d)\n",bpp); + return(2); + } + } + } + return(1); +} + +#if 0 +static void get_surface_info(SDL_Surface *s) +{ + SDL_PixelFormat *pf = s->format; + + log_printf("get_surface_info: sdl surface info:\n"); + log_printf(" bits per pixel: %d\n",pf->BitsPerPixel); + log_printf(" red: mask: %08X shift: %d loss: %d\n",pf->Rmask,pf->Rshift,pf->Rloss); + log_printf(" green: mask: %08X shift: %d loss: %d\n",pf->Gmask,pf->Gshift,pf->Gloss); + log_printf(" blue: mask: %08X shift: %d loss: %d\n",pf->Bmask,pf->Bshift,pf->Bloss); + + rshift = pf->Rshift; + gshift = pf->Gshift; + bshift = pf->Bshift; + rloss = pf->Rloss; + gloss = pf->Gloss; + bloss = pf->Bloss; +} + +//return absolute value +static int absolute_value(int v) +{ + return((v < 0) ? (0 - v) : v); +} + +int find_video_mode(int wantw,int wanth,int flags2,int *w,int *h) +{ + SDL_Rect **modes,*mode; + int i,diffw[2],diffh[2]; + + //get list of modes from sdl + modes = SDL_ListModes(NULL, flags2); + *w = *h = 0; + + //if nothing returned + if(modes == (SDL_Rect**)0) { + log_printf("find_video_mode: fatal error: no modes available\n"); + return(1); + } + + //see if any mode is available (windowed mode) + if(modes == (SDL_Rect**)-1) { + log_printf("find_video_mode: all resolutions available\n"); + *w = wantw; + *h = wanth; + return(0); + } + + //output modes + log_printf("find_video_mode: available modes:\n"); + for(i=0;modes[i];i++) { + log_printf("find_video_mode: %d x %d\n",modes[i]->w,modes[i]->h); + } + + //search for closest video mode + for(mode=0,i=0;modes[i];i++) { + if(modes[i]->w >= wantw && modes[i]->h >= wanth) { + if(mode == 0) { + mode = modes[i]; + } + else { + diffw[0] = absolute_value(mode->w - wantw); + diffh[0] = absolute_value(mode->h - wanth); + diffw[1] = absolute_value(modes[i]->w - wantw); + diffh[1] = absolute_value(modes[i]->h - wanth); + if((diffw[1] + diffh[1]) < (diffw[0] + diffh[0])) { + mode = modes[i]; + } + } + } + } + + //if a mode was found set the return variables + if(mode) { + *w = mode->w; + *h = mode->h; + } + + return(0); +} + +static int get_desktop_bpp() +{ + const SDL_VideoInfo *vi = SDL_GetVideoInfo(); + + log_printf("get_desktop_bpp: current display mode is %d x %d, %d bpp\n",vi->current_w,vi->current_h,vi->vfmt->BitsPerPixel); + return(vi->vfmt->BitsPerPixel); +} +#endif + +int video_init() +{ + if(nesscreen == 0) + nesscreen = (u8*)mem_alloc(256 * (240 + 16)); + + //setup timer to limit frames + interval = (double)system_getfrequency() / 60.0f; + lasttime = system_gettick(); + + //clear palette caches + memset(palettecache16,0,256*sizeof(u16)); + memset(palettecache32,0,256*sizeof(u32)); + + //set screen info + //flags &= ~SDL_FULLSCREEN; + //flags |= config_get_bool("video.fullscreen") ? SDL_FULLSCREEN : 0; + screenscale = config_get_int("video.scale"); + +#if 0 + //fullscreen mode + if(flags & SDL_FULLSCREEN) { + screenscale = (screenscale < 2) ? 2 : screenscale; + screenbpp = 32; + } + + //windowed mode + else { + screenbpp = get_desktop_bpp(); + } +#endif + + screenbpp = 32; + + //initialize the video filters + filter_init(); + + //get pointer to video filter + filter = filter_get((screenscale == 1) ? F_NONE : filter_get_int(config_get_string("video.filter"))); + + if(find_drawfunc(screenscale,screenbpp) != 0) { + log_printf("video_init: error finding appropriate draw func, using draw1x\n"); + filter = &filter_draw; + drawfunc = filter->modes[0].draw32; + } + + //calculate desired screen dimensions + screenw = filter->minwidth / filter->minscale * screenscale; + screenh = filter->minheight / filter->minscale * screenscale; + + screenw = screen_width(); + screenh = screen_height(); + +#if 0 + //fullscreen mode + if(flags & SDL_FULLSCREEN) { + int w,h; + + if(find_video_mode(screenw,screenh,flags | SDL_FULLSCREEN,&w,&h) == 0) { + screenw = w; + screenh = h; + log_printf("video_init: best display mode: %d x %d\n",w,h); + } + } + + //initialize surface/window + surface = SDL_SetVideoMode(screenw,screenh,screenbpp,flags); + SDL_WM_SetCaption("nesemu2",NULL); + SDL_ShowCursor(0); + get_surface_info(surface); +#endif + + //allocate memory for temp screen buffer + screen = (u32*)mem_realloc(screen,256 * (240 + 16) * (screenbpp / 8) * 4); + + //print information + //log_printf("video initialized: %dx%dx%d %s\n",surface->w,surface->h,surface->format->BitsPerPixel,(flags & SDL_FULLSCREEN) ? "fullscreen" : "windowed"); + + return(0); +} + +void video_kill() +{ + filter_kill(); + //SDL_ShowCursor(1); + if(screen) + mem_free(screen); + if(nesscreen) + mem_free(nesscreen); + screen = 0; + nesscreen = 0; +} + +int video_reinit() +{ + video_kill(); + return(video_init()); +} + +void video_startframe() +{ + //lock sdl surface + //SDL_LockSurface(surface); +} + +void video_endframe() +{ + u64 t; + + //draw everything + //drawfunc(surface->pixels,surface->pitch,screen,256*4,256,240); + draw_rect(screen, 0, 0, 256, 240); + //console_draw((u32*)surface->pixels,surface->pitch,screenh); + console_draw(screen, 256*4,screenh); + + //flip buffers and unlock surface + //SDL_Flip(surface); + //SDL_Flip() + //SDL_UnlockSurface(surface); + + //simple frame limiter + if(config_get_bool("video.framelimit")) { + do { + t = system_gettick(); + } while((double)(t - lasttime) < interval); + lasttime = t; + } +} + +//this handles lines for gui/status messages +void video_updateline(int line,u8 *s) +{ + u32 *dest = screen + (line * 256); + int i; + + memcpy(nesscreen + (line * 256),s,256); + if(line >= 8 && line < 232) { + for(i=0;i<256;i++) { + *dest++ = palettecache32[*s++]; + } + } + else { + for(i=0;i<256;i++) { + *dest++ = 0; + } + } +} + +//this handles pixels coming directly from the nes engine +void video_updatepixel(int line,int pixel,u8 s) +{ + int offset = (line * 256) + pixel; + + nesscreen[offset] = s; + if(line >= 8 && line < 232) { + screen[offset] = palettecache32[s]; + } + else { + screen[offset] = 0; + } +} + +//this handles palette changes from the nes engine +void video_updatepalette(u8 addr,u8 data) +{ + palettecache32[addr+0x00] = palette32[0][data]; + palettecache32[addr+0x20] = palette32[1][data]; + palettecache32[addr+0x40] = palette32[2][data]; + palettecache32[addr+0x60] = palette32[3][data]; + palettecache32[addr+0x80] = palette32[4][data]; + palettecache32[addr+0xA0] = palette32[5][data]; + palettecache32[addr+0xC0] = palette32[6][data]; + palettecache32[addr+0xE0] = palette32[7][data]; + palettecache[addr] = data; +} + +//must be called AFTER video_init +void video_setpalette(palette_t *p) +{ + int i,j; + palentry_t *e; + + for(j=0;j<8;j++) { + for(i=0;i<64;i++) { + palette[j][(i * 3) + 0] = p->pal[j][i].r; + palette[j][(i * 3) + 1] = p->pal[j][i].g; + palette[j][(i * 3) + 2] = p->pal[j][i].b; + } + } + + for(j=0;j<8;j++) { + for(i=0;i<256;i++) { + e = &p->pal[j][i & 0x3F]; + palette32[j][i] = (e->r << rshift) | (e->g << gshift) | (e->b << bshift); + } + } + + filter_palette_changed(); +} + +int video_getwidth() { return(screenw); } +int video_getheight() { return(screenh); } +int video_getbpp() { return(screenbpp); } +u8 *video_getscreen() { return(nesscreen); } +u8 *video_getpalette() { return((u8*)palette); } + +int video_zapperhit(int x,int y) +{ + int ret = 0; + u8 *e; + u8 color; + + color = palettecache[nesscreen[x + y * 256]]; + e = &palette[(color >> 5) & 7][(color & 0x3F) * 3]; + ret += (int)(e[0] * 0.299f); + ret += (int)(e[1] * 0.587f); + ret += (int)(e[2] * 0.114f); + return((ret >= 0x40) ? 1 : 0); +} + +//kludge-city! +int video_getxoffset() { return(0); } +int video_getyoffset() { return(0); } +int video_getscale() { return(screenscale); } diff --git a/apps/nesemu2/src/system/sound.h b/apps/nesemu2/src/system/sound.h new file mode 100644 index 00000000..8a7c0018 --- /dev/null +++ b/apps/nesemu2/src/system/sound.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __sound_h__ +#define __sound_h__ + +int sound_init(); +void sound_kill(); +void sound_pause(); +void sound_play(); +void sound_update(void *buffer,int length); +void sound_setfps(int fps); + +#endif diff --git a/apps/nesemu2/src/system/system.h b/apps/nesemu2/src/system/system.h new file mode 100644 index 00000000..76b79234 --- /dev/null +++ b/apps/nesemu2/src/system/system.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __system_h__ +#define __system_h__ + +#include "types.h" + +int system_init(); +void system_kill(); +void system_checkevents(); +char *system_getcwd(); +u64 system_gettick(); +u64 system_getfrequency(); + +//this needs to be dealt with +int system_findconfig(char *dest); + +#endif diff --git a/apps/nesemu2/src/system/video.h b/apps/nesemu2/src/system/video.h new file mode 100644 index 00000000..bc475ac9 --- /dev/null +++ b/apps/nesemu2/src/system/video.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __video_h__ +#define __video_h__ + +#include "palette/palette.h" + +int video_init(); +void video_kill(); +int video_reinit(); +void video_startframe(); +void video_endframe(); +void video_updatepixel(int line,int pixel,u8 s); +void video_updatepalette(u8 addr,u8 data); +void video_setpalette(palette_t *p); +int video_getwidth(); +int video_getheight(); +int video_getbpp(); +u8 *video_getscreen(); +u8 *video_getpalette(); + +#endif diff --git a/apps/nesemu2/src/types.h b/apps/nesemu2/src/types.h new file mode 100644 index 00000000..2295e7da --- /dev/null +++ b/apps/nesemu2/src/types.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __types_h__ +#define __types_h__ + +#if defined(_MSC_VER) + #define int64 __int64 + #define INLINE __inline +#elif defined(__GNUC__) + #define int64 long long + #define INLINE inline +#else + #error unknown compiler. please #define int64. +#endif + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed int64 s64; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned int64 u64; + +typedef u8 (*readfunc_t)(u32); +typedef void (*writefunc_t)(u32,u8); + +int stricmp(char *s1,char *s2); + +#endif diff --git a/apps/nesemu2/src/version.h b/apps/nesemu2/src/version.h new file mode 100644 index 00000000..e5338830 --- /dev/null +++ b/apps/nesemu2/src/version.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2013 by James Holodnak * + * jamesholodnak@gmail.com * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef __version_h__ +#define __version_h__ + +#define VERSION "0.6.1" + +#endif From 0e56568ad0224d52e68c96c90e17d6ed793655c4 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Thu, 28 Nov 2019 16:51:34 +0800 Subject: [PATCH 005/234] apps,nesemu2: clear up SDL code --- apps/nesemu2/src/system/input.h | 4 - apps/nesemu2/src/system/sdl/console/console.c | 146 ------- apps/nesemu2/src/system/sdl/input.c | 117 ++---- apps/nesemu2/src/system/sdl/sound.c | 385 ------------------ apps/nesemu2/src/system/sdl/system.c | 132 ------ apps/nesemu2/src/system/sdl/video.c | 140 ------- 6 files changed, 32 insertions(+), 892 deletions(-) diff --git a/apps/nesemu2/src/system/input.h b/apps/nesemu2/src/system/input.h index 0f1bc244..6d7e0954 100644 --- a/apps/nesemu2/src/system/input.h +++ b/apps/nesemu2/src/system/input.h @@ -43,16 +43,12 @@ u8 keyboard[370]; //keyboard state } inputdata_t;*/ -extern int joyx,joyy; //x and y coords for paddle/mouse -extern u8 joyzap; //zapper trigger extern u8 joykeys[]; //keyboard state -extern u8 joystate[]; //joypad state extern int joyconfig[4][8]; //joypad keys int input_init(); void input_kill(); void input_poll(); int input_poll_mouse(int *x,int *y); -void input_update_config(); #endif diff --git a/apps/nesemu2/src/system/sdl/console/console.c b/apps/nesemu2/src/system/sdl/console/console.c index cc37b1d2..06a35a1f 100644 --- a/apps/nesemu2/src/system/sdl/console/console.c +++ b/apps/nesemu2/src/system/sdl/console/console.c @@ -18,7 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -//#include #include #include "emu/commands.h" #include "misc/log.h" @@ -185,151 +184,6 @@ void console_hide() showing = 0; } -//call 60 times a second -#if 0 -void console_update() -{ - static int keydown = 0; - - if(joykeys[SDLK_BACKQUOTE] && (keydown & 1) == 0) { - if(showing == 0) - console_show(); - else - console_hide(); - keydown |= 1; - } - else if(joykeys[SDLK_BACKQUOTE] == 0) - keydown &= ~1; - if(showing) { - if(ypos < height) ypos += YPOS_STEP; - if(ypos > height) ypos = height; - } - else { - if(ypos > 0) ypos -= YPOS_STEP; - if(ypos < 0) ypos = 0; - } - if(statustime) - statustime--; - cursorblink = (cursorblink + 1) & 0x3F; -// cursorblink++; -} - -//static int isignored(int ch) -//{ -// if (ch == '`' || ch == '~') -// return(1); -// return(0); -//} - -static int isletter(int ch) -{ - if (ch >= SDLK_a && ch <= SDLK_z) - return(1); - return(0); -} - -static int isnumber(int ch) -{ - if (ch >= SDLK_0 && ch <= SDLK_9) - return(1); - return(0); -} - -void console_keyevent(int state,int sym) -{ - int modstate = SDL_GetModState(); - int isshift = modstate & (KMOD_LSHIFT | KMOD_RSHIFT); - int iscaps = modstate & KMOD_CAPS; - int i,ch = -1; - - //ignore keyup events - if(state == SDL_KEYUP) - return; - - //if shift is pressed invert caps lock state - if(isshift) - iscaps ^= KMOD_CAPS; - - //cursor left - if (sym == SDLK_LEFT) { - if (inputbufpos) - inputbufpos--; - return; - } - - //cursor right - else if (sym == SDLK_RIGHT) { - if (inputbufpos < inputbuflen) - inputbufpos++; - return; - } - - //if this is a letter - else if(isletter(sym) || isnumber(sym)) { - ch = sym - SDLK_a; - if(iscaps) - ch += 'A'; - else - ch += 'a'; - } - - //if this is a printable character and we are not ignoring it -// else if(isprint(sym) && isignored(sym) == 0) { -// ch = sym; -// } - - //backspace - else if(sym == SDLK_BACKSPACE) { - if(inputbufpos) { - inputbuflen--; - inputbufpos--; - for(i=inputbufpos;i -#include "system/input.h" -#include "system/system.h" #include "misc/config.h" #include -#include #include /* @@ -39,84 +35,10 @@ required variables: */ //these global variables provide information for the device input code -int joyx,joyy; //x and y coords for paddle/mouse -u8 joytrigger; -u8 joykeys[370]; //keyboard state +uint8_t joykeys[370]; //keyboard state int joyconfig[4][8]; //joypad button configuration -// this will map joystick axises/buttons to unused keyboard buttons -#define FIRSTJOYSTATEKEY (350) // ideally should be SDLK_LAST -u8 joystate[32]; // dpad + 8 buttons is enuff' for me but let's be sure :-) - int input_init() -{ - int i; - - //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL); - for(i=0;i<20;i++) { - joystate[i] = 0; - } - input_update_config(); - return(0); -} - -void input_kill() -{ -} - -void input_poll() -{ -#if 0 - Uint8 *keystate = SDL_GetKeyState(NULL); - int i,x,y; - - //need to update mousex/mousey/mousebuttons here - joytrigger = (u8)(SDL_GetMouseState(&x,&y) & 1) << 4; - joyx = x; - joyy = y; - - //now update key/mouse state, the input device logic will - //decode the key/mouse data into the correct input for the nes - for(i=0;i<300;i++) - joykeys[i] = keystate[i]; - - //put joypad buttons in the struct - for(i=0;i<20;i++) { - joykeys[FIRSTJOYSTATEKEY + i] = joystate[i]; - } -#endif - int keycode; - do { -#define KEYDOWN_MASK 0x8000 - int key = read_key(); - keycode = key & ~KEYDOWN_MASK; - int keydown = (key & KEYDOWN_MASK) != 0; - if (keycode != _KEY_NONE) { - printf("key = %x\n", key); - } - switch (keycode) { - case _KEY_J: joykeys['j'] = keydown; break; - case _KEY_K: joykeys['k'] = keydown; break; - case _KEY_U: joykeys['u'] = keydown; break; - case _KEY_I: joykeys['i'] = keydown; break; - case _KEY_W: joykeys['w'] = keydown; break; - case _KEY_S: joykeys['s'] = keydown; break; - case _KEY_A: joykeys['a'] = keydown; break; - case _KEY_D: joykeys['d'] = keydown; break; - } - } while (keycode != _KEY_NONE); -} - -extern int video_getxoffset(); -extern int video_getyoffset(); -extern int video_getscale(); - -int input_poll_mouse(int *x,int *y) -{ - return 0; -} - -void input_update_config() { joyconfig[0][0] = config_get_int("input.joypad0.a"); joyconfig[0][1] = config_get_int("input.joypad0.b"); @@ -135,10 +57,35 @@ void input_update_config() joyconfig[1][6] = config_get_int("input.joypad1.left"); joyconfig[1][7] = config_get_int("input.joypad1.right"); - int i; - for (i = 0; i < 8; i ++) { - printf("joyconfig[0][%d] = %d\n", i, joyconfig[0][i]); - } - - printf("joyconfig[0][1] = %d\n", joyconfig[0][1]); + return(0); +} + +void input_kill() +{ +} + +void input_poll() +{ + int keycode; + do { +#define KEYDOWN_MASK 0x8000 + int key = read_key(); + keycode = key & ~KEYDOWN_MASK; + int keydown = (key & KEYDOWN_MASK) != 0; + switch (keycode) { + case _KEY_J: joykeys['j'] = keydown; break; + case _KEY_K: joykeys['k'] = keydown; break; + case _KEY_U: joykeys['u'] = keydown; break; + case _KEY_I: joykeys['i'] = keydown; break; + case _KEY_W: joykeys['w'] = keydown; break; + case _KEY_S: joykeys['s'] = keydown; break; + case _KEY_A: joykeys['a'] = keydown; break; + case _KEY_D: joykeys['d'] = keydown; break; + } + } while (keycode != _KEY_NONE); +} + +int input_poll_mouse(int *x,int *y) +{ + return 0; } diff --git a/apps/nesemu2/src/system/sdl/sound.c b/apps/nesemu2/src/system/sdl/sound.c index 84327329..213b5cde 100644 --- a/apps/nesemu2/src/system/sdl/sound.c +++ b/apps/nesemu2/src/system/sdl/sound.c @@ -18,391 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#if 0 -#include -#include "types.h" -#include "misc/log.h" -#include "misc/config.h" -#include "misc/memutil.h" -#include "system/sound.h" - -static int sound_samplerate = 44100; - -// number of samples per SDL callback -#define SDL_XFER_SAMPLES 512 -#define MAX_AUDIO_LATENCY 4 - -static int sdl_xfer_samples = SDL_XFER_SAMPLES; -static int stream_in_initialized = 0; -static int stream_loop = 0; - -static int initialized_audio = 0; -static int buf_locked; - -static s8 *stream_buffer; -static volatile s32 stream_playpos; - -static int stream_buffer_size; -static int stream_buffer_in; - -// buffer over/underflow counts -static int buffer_underflows; -static int buffer_overflows; - -// sound enable -static int snd_enabled = 1; - -static int sdl_create_buffers(void); -static void sdl_destroy_buffers(void); -static void sdl_cleanup_audio(); -static void SDLCALL sdl_callback(void *userdata, Uint8 *stream, int len); - -static void sdl_callback(void *userdata, Uint8 *stream, int len) -{ - int len1, len2, sb_in; - - sb_in = stream_buffer_in; - if (stream_loop) - sb_in += stream_buffer_size; - - if (sb_in < (stream_playpos+len)) - { - return; - } - else if ((stream_playpos+len) > stream_buffer_size) - { - len1 = stream_buffer_size - stream_playpos; - len2 = len - len1; - } - else - { - len1 = len; - len2 = 0; - } - - if (snd_enabled) - { - memcpy(stream, stream_buffer + stream_playpos, len1); - memset(stream_buffer + stream_playpos, 0, len1); // no longer needed - if (len2) - { - memcpy(stream+len1, stream_buffer, len2); - memset(stream_buffer, 0, len2); // no longer needed - } - - } - else - { - memset(stream, 0, len); - } - - // move the play cursor - stream_playpos += len1 + len2; - if (stream_playpos >= stream_buffer_size) - { - stream_playpos -= stream_buffer_size; - stream_loop = 0; - } -} - -//============================================================ -// lock_buffer -//============================================================ -static INLINE int lock_buffer(long offset, long size, void **buffer1, long *length1, void **buffer2, long *length2) -{ -// volatile long pstart, pend, lstart, lend; - - if (!buf_locked) - { -/* if (machine.video().throttled()) - { - pstart = stream_playpos; - pend = (pstart + sdl_xfer_samples); - lstart = offset; - lend = lstart+size; - while (((pstart >= lstart) && (pstart <= lend)) || - ((pend >= lstart) && (pend <= lend))) - { - pstart = stream_playpos; - pend = pstart + sdl_xfer_samples; - } - }*/ - - SDL_LockAudio(); - buf_locked++; - } - - // init lengths - *length1 = *length2 = 0; - - if ((offset + size) > stream_buffer_size) - { - // 2-piece case - *length1 = stream_buffer_size - offset; - *buffer1 = &stream_buffer[offset]; - *length2 = size - *length1; - *buffer2 = stream_buffer; - } - else - { - // normal 1-piece case - *length1 = size; - *buffer1 = &stream_buffer[offset]; - } - - return 0; -} - -//============================================================ -// unlock_buffer -//============================================================ -static INLINE void unlock_buffer(void) -{ - buf_locked--; - if(buf_locked == 0) - SDL_UnlockAudio(); -} - -static INLINE void copy_sample_data(s16 *data, int bytes_to_copy) -{ - void *buffer1, *buffer2 = (void *)NULL; - long length1, length2; - int cur_bytes; - - // attempt to lock the stream buffer - if (lock_buffer(stream_buffer_in, bytes_to_copy, &buffer1, &length1, &buffer2, &length2) < 0) - { - buffer_underflows++; - return; - } - - // adjust the input pointer - stream_buffer_in += bytes_to_copy; - if (stream_buffer_in >= stream_buffer_size) - { - stream_buffer_in -= stream_buffer_size; - stream_loop = 1; - } - - // copy the first chunk - cur_bytes = (bytes_to_copy > length1) ? length1 : bytes_to_copy; - memcpy(buffer1, data, cur_bytes); - - // adjust for the number of bytes - bytes_to_copy -= cur_bytes; - data = (s16 *)((u8 *)data + cur_bytes); - - // copy the second chunk - if (bytes_to_copy != 0) - { - cur_bytes = (bytes_to_copy > length2) ? length2 : bytes_to_copy; - memcpy(buffer2, data, cur_bytes); - } - - // unlock - unlock_buffer(); -} - -static INLINE void update_audio_stream(s16 *buffer, int samples_this_frame) -{ - // if nothing to do, don't do it - if (sound_samplerate != 0 && stream_buffer) - { - int bytes_this_frame = samples_this_frame * sizeof(s16); - int play_position, write_position, stream_in; - //int orig_write; // used in LOG - - play_position = stream_playpos; - - write_position = stream_playpos + ((sound_samplerate / 60) * sizeof(s16)); - //orig_write = write_position; - - if (!stream_in_initialized) - { - stream_in = stream_buffer_in = (write_position + stream_buffer_size); - - log_printf("stream_in = %d, ", (int)stream_in); - log_printf("stream_playpos = %d, ", (int)stream_playpos); - log_printf("write_position = %d\n", (int)write_position); - - // start playing - SDL_PauseAudio(0); - - stream_in_initialized = 1; - } - else - { - // normalize the stream in position - stream_in = stream_buffer_in; - if (stream_in < write_position && stream_loop == 1) - stream_in += stream_buffer_size; - - // now we should have, in order: - // <------pp---wp---si---------------> - - // if we're between play and write positions, then bump forward, but only in full chunks - while (stream_in < write_position) - { -// log_printf("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); - buffer_underflows++; - stream_in += bytes_this_frame; - } - - // if we're going to overlap the play position, just skip this chunk - if (stream_in + bytes_this_frame > play_position + stream_buffer_size) - { -// log_printf("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)stream_buffer_in, (int)bytes_this_frame); - buffer_overflows++; - return; - } - } - - if (stream_in >= stream_buffer_size) - { - stream_in -= stream_buffer_size; - stream_loop = 1; - } - - // now we know where to copy; let's do it - stream_buffer_in = stream_in; - copy_sample_data(buffer, bytes_this_frame); - } -} - - -static void sdl_cleanup_audio() -{ - // if nothing to do, don't do it - if (sound_samplerate == 0) - return; - - // kill the buffers and dsound - sdl_destroy_buffers(); - - // print out over/underflow stats - if(buffer_overflows || buffer_underflows) - log_printf("sdl_cleanup_audio: overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows); -} - -//============================================================ -// dsound_create_buffers -//============================================================ - -static int sdl_create_buffers(void) -{ - log_printf("sdl_create_buffers: creating stream buffer of %u bytes\n", stream_buffer_size); - stream_buffer = (s8*)mem_alloc(stream_buffer_size); - stream_playpos = 0; - buf_locked = 0; - return 0; -} - -//============================================================ -// sdl_destroy_buffers -//============================================================ - -static void sdl_destroy_buffers(void) -{ - // release the buffer - if(stream_buffer) - mem_free(stream_buffer); - stream_buffer = NULL; -} - -int sound_init() -{ - int audio_latency; - SDL_AudioSpec aspec, obtained; - char audio_driver[16] = ""; - - if(config_get_bool("sound.enabled") == 0) - return(0); - - if(initialized_audio) { - sound_kill(); - } - - log_printf("sound_init: Start initialization\n"); - SDL_AudioDriverName(audio_driver,sizeof(audio_driver)); - log_printf("sound_init: Driver is %s\n",audio_driver); - initialized_audio = 0; - sdl_xfer_samples = SDL_XFER_SAMPLES; - stream_in_initialized = 0; - stream_loop = 0; - - // set up the audio specs - aspec.freq = sound_samplerate; - aspec.format = AUDIO_S16SYS; // keep endian independent - aspec.channels = 1; - aspec.samples = sdl_xfer_samples; - aspec.callback = sdl_callback; - aspec.userdata = 0; - - if(SDL_OpenAudio(&aspec,&obtained) < 0) - goto cant_start_audio; - - initialized_audio = 1; - snd_enabled = 1; - - log_printf("sound_init: frequency: %d, channels: %d, samples: %d\n",obtained.freq,obtained.channels,obtained.samples); - - sdl_xfer_samples = obtained.samples; - - audio_latency = 1; - - // compute the buffer sizes - stream_buffer_size = sound_samplerate * sizeof(s16) * audio_latency / MAX_AUDIO_LATENCY; - stream_buffer_size = (stream_buffer_size / 1024) * 1024; - if(stream_buffer_size < 1024) - stream_buffer_size = 1024; - - // create the buffers - if (sdl_create_buffers()) - goto cant_create_buffers; - - log_printf("sound_init: End initialization, frame latency = %d\n",audio_latency); - return(0); - - // error handling -cant_create_buffers: -cant_start_audio: - log_printf("sound_init: Initialization failed. SDL error: %s\n", SDL_GetError()); - return(1); -} - -void sound_kill() -{ - if(initialized_audio) { - log_printf("sound_kill: closing audio\n"); - SDL_CloseAudio(); - sdl_cleanup_audio(); - } -} - -void sound_play() -{ - if(initialized_audio) - SDL_PauseAudio(0); -} - -void sound_pause() -{ - if(initialized_audio) - SDL_PauseAudio(1); -} - -void sound_update(void *buf,int size) -{ - if(initialized_audio) - update_audio_stream((s16*)buf,size); -} - -void sound_setfps(int fps) -{ - -} -#endif - int sound_init() { return 0; } void sound_kill() { } void sound_play() { } diff --git a/apps/nesemu2/src/system/sdl/system.c b/apps/nesemu2/src/system/sdl/system.c index 9efde26c..8f7dcf95 100644 --- a/apps/nesemu2/src/system/sdl/system.c +++ b/apps/nesemu2/src/system/sdl/system.c @@ -28,137 +28,6 @@ uint32_t uptime(); #define _MAX_PATH 4096 #endif - -#if 0 -#include -#include "emu/events.h" -#include "system/main.h" -#include "system/video.h" -#include "system/input.h" -#include "system/sound.h" -#include "system/sdl/console/console.h" -#include "nes/nes.h" -#include "nes/state/state.h" -#include "misc/paths.h" -#include "misc/log.h" -#include "misc/config.h" - -static SDL_Joystick *joystick = 0; - -int system_init() -{ - if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { - printf("error at sdl init!\n"); - return(1); - } - if(SDL_NumJoysticks() > 0) { - SDL_JoystickEventState(SDL_ENABLE); - joystick = SDL_JoystickOpen(0); - } - return(0); -} - -void system_kill() -{ - if(joystick) - SDL_JoystickClose(joystick); - SDL_Quit(); -} - -#define bit(n) (1 << (n)) -#define checkkey(key,n,evt) \ - if(joykeys[key] && (keydown & bit(n)) == 0) { \ - keydown |= bit(n); \ - emu_event(evt,0); \ - } \ - else if(joykeys[key] == 0) { \ - keydown &= ~bit(n); \ - } - -void system_checkevents() -{ - static int keydown = 0; - SDL_Event event; /* Event structure */ - - //check out sdl events - while(SDL_PollEvent(&event)) { - switch(event.type) { - case SDL_QUIT: - quit++; - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - console_keyevent(event.key.type,event.key.keysym.sym); - break; - case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ - if (event.jaxis.axis == 0) - { - joystate[0] = 0; - joystate[1] = 0; - if(event.jaxis.value < -3200) - joystate[0] = 1; - else if (event.jaxis.value > 3200) - joystate[1] = 1; - } - else if (event.jaxis.axis == 1) - { - joystate[2] = 0; - joystate[3] = 0; - if (event.jaxis.value < -3200) - joystate[2] = 1; - else if (event.jaxis.value > 3200) - joystate[3] = 1; - } - break; - case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */ - joystate[event.jbutton.button + 4] = 1; - break; - case SDL_JOYBUTTONUP: /* Handle Joystick Button Releases */ - joystate[event.jbutton.button + 4] = 0; - break; - } - } - - //update the console - console_update(); - - //check for system key presses - checkkey(SDLK_ESCAPE, 0, E_QUIT); - checkkey(SDLK_F1, 1, E_TOGGLERUNNING); - checkkey(SDLK_F4, 2, E_TOGGLEFULLSCREEN); - checkkey(SDLK_F5, 3, E_SAVESTATE); - checkkey(SDLK_F8, 4, E_LOADSTATE); - checkkey(SDLK_F9, 5, E_FLIPDISK); - -// checkkey(SDLK_p, 1, E_SOFTRESET); -// checkkey(SDLK_o, 2, E_HARDRESET); -// checkkey(SDLK_F3, 4, E_PLAYMOVIE); -// checkkey(SDLK_F6, 7, E_RECORDMOVIE); -// checkkey(SDLK_F7, 8, E_STOPMOVIE); -// checkkey(SDLK_F10, 11, E_DUMPDISK); -} - -char *system_getcwd() -{ - static char buf[_MAX_PATH]; - - if(getcwd(buf,_MAX_PATH) == NULL) - memset(buf,0,_MAX_PATH); - return(buf); -} - -u64 system_gettick() -{ - return(SDL_GetTicks()); -} - -u64 system_getfrequency() -{ - return(1000); -} - -#else - int system_init() { return 0; } void system_kill() { } @@ -175,4 +44,3 @@ char *system_getcwd() uint64_t system_gettick() { return uptime(); } uint64_t system_getfrequency() { return 1000; } -#endif diff --git a/apps/nesemu2/src/system/sdl/video.c b/apps/nesemu2/src/system/sdl/video.c index f2d3d4e9..bcf3da8a 100644 --- a/apps/nesemu2/src/system/sdl/video.c +++ b/apps/nesemu2/src/system/sdl/video.c @@ -18,7 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -//#include #include #include "misc/log.h" #include "palette/palette.h" @@ -31,8 +30,6 @@ #include "system/common/filters.h" //system related variables -//static SDL_Surface *surface = 0; -//static int flags = 0; //SDL_DOUBLEBUF | SDL_HWSURFACE; static int screenw,screenh,screenbpp; static int screenscale; @@ -91,96 +88,6 @@ static int find_drawfunc(int scale,int bpp) return(1); } -#if 0 -static void get_surface_info(SDL_Surface *s) -{ - SDL_PixelFormat *pf = s->format; - - log_printf("get_surface_info: sdl surface info:\n"); - log_printf(" bits per pixel: %d\n",pf->BitsPerPixel); - log_printf(" red: mask: %08X shift: %d loss: %d\n",pf->Rmask,pf->Rshift,pf->Rloss); - log_printf(" green: mask: %08X shift: %d loss: %d\n",pf->Gmask,pf->Gshift,pf->Gloss); - log_printf(" blue: mask: %08X shift: %d loss: %d\n",pf->Bmask,pf->Bshift,pf->Bloss); - - rshift = pf->Rshift; - gshift = pf->Gshift; - bshift = pf->Bshift; - rloss = pf->Rloss; - gloss = pf->Gloss; - bloss = pf->Bloss; -} - -//return absolute value -static int absolute_value(int v) -{ - return((v < 0) ? (0 - v) : v); -} - -int find_video_mode(int wantw,int wanth,int flags2,int *w,int *h) -{ - SDL_Rect **modes,*mode; - int i,diffw[2],diffh[2]; - - //get list of modes from sdl - modes = SDL_ListModes(NULL, flags2); - *w = *h = 0; - - //if nothing returned - if(modes == (SDL_Rect**)0) { - log_printf("find_video_mode: fatal error: no modes available\n"); - return(1); - } - - //see if any mode is available (windowed mode) - if(modes == (SDL_Rect**)-1) { - log_printf("find_video_mode: all resolutions available\n"); - *w = wantw; - *h = wanth; - return(0); - } - - //output modes - log_printf("find_video_mode: available modes:\n"); - for(i=0;modes[i];i++) { - log_printf("find_video_mode: %d x %d\n",modes[i]->w,modes[i]->h); - } - - //search for closest video mode - for(mode=0,i=0;modes[i];i++) { - if(modes[i]->w >= wantw && modes[i]->h >= wanth) { - if(mode == 0) { - mode = modes[i]; - } - else { - diffw[0] = absolute_value(mode->w - wantw); - diffh[0] = absolute_value(mode->h - wanth); - diffw[1] = absolute_value(modes[i]->w - wantw); - diffh[1] = absolute_value(modes[i]->h - wanth); - if((diffw[1] + diffh[1]) < (diffw[0] + diffh[0])) { - mode = modes[i]; - } - } - } - } - - //if a mode was found set the return variables - if(mode) { - *w = mode->w; - *h = mode->h; - } - - return(0); -} - -static int get_desktop_bpp() -{ - const SDL_VideoInfo *vi = SDL_GetVideoInfo(); - - log_printf("get_desktop_bpp: current display mode is %d x %d, %d bpp\n",vi->current_w,vi->current_h,vi->vfmt->BitsPerPixel); - return(vi->vfmt->BitsPerPixel); -} -#endif - int video_init() { if(nesscreen == 0) @@ -195,23 +102,8 @@ int video_init() memset(palettecache32,0,256*sizeof(u32)); //set screen info - //flags &= ~SDL_FULLSCREEN; - //flags |= config_get_bool("video.fullscreen") ? SDL_FULLSCREEN : 0; screenscale = config_get_int("video.scale"); -#if 0 - //fullscreen mode - if(flags & SDL_FULLSCREEN) { - screenscale = (screenscale < 2) ? 2 : screenscale; - screenbpp = 32; - } - - //windowed mode - else { - screenbpp = get_desktop_bpp(); - } -#endif - screenbpp = 32; //initialize the video filters @@ -233,38 +125,15 @@ int video_init() screenw = screen_width(); screenh = screen_height(); -#if 0 - //fullscreen mode - if(flags & SDL_FULLSCREEN) { - int w,h; - - if(find_video_mode(screenw,screenh,flags | SDL_FULLSCREEN,&w,&h) == 0) { - screenw = w; - screenh = h; - log_printf("video_init: best display mode: %d x %d\n",w,h); - } - } - - //initialize surface/window - surface = SDL_SetVideoMode(screenw,screenh,screenbpp,flags); - SDL_WM_SetCaption("nesemu2",NULL); - SDL_ShowCursor(0); - get_surface_info(surface); -#endif - //allocate memory for temp screen buffer screen = (u32*)mem_realloc(screen,256 * (240 + 16) * (screenbpp / 8) * 4); - //print information - //log_printf("video initialized: %dx%dx%d %s\n",surface->w,surface->h,surface->format->BitsPerPixel,(flags & SDL_FULLSCREEN) ? "fullscreen" : "windowed"); - return(0); } void video_kill() { filter_kill(); - //SDL_ShowCursor(1); if(screen) mem_free(screen); if(nesscreen) @@ -281,8 +150,6 @@ int video_reinit() void video_startframe() { - //lock sdl surface - //SDL_LockSurface(surface); } void video_endframe() @@ -290,16 +157,9 @@ void video_endframe() u64 t; //draw everything - //drawfunc(surface->pixels,surface->pitch,screen,256*4,256,240); draw_rect(screen, 0, 0, 256, 240); - //console_draw((u32*)surface->pixels,surface->pitch,screenh); console_draw(screen, 256*4,screenh); - //flip buffers and unlock surface - //SDL_Flip(surface); - //SDL_Flip() - //SDL_UnlockSurface(surface); - //simple frame limiter if(config_get_bool("video.framelimit")) { do { From 1e85b4390c423eef781ab4717d81783f938325a3 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Fri, 29 Nov 2019 16:58:52 +0800 Subject: [PATCH 006/234] apps,nesemu2: remove most libc calls and run on x86-nemu and mips32-nemu * riscv32-nemu needs __clzsi2() and __ctzsi2() to run * there is still a render bug for x86-nemu --- apps/litenes/src/roms/build-roms.py | 4 +- apps/nesemu2/Makefile | 7 +- apps/nesemu2/src/cartdb/cartdb.c | 10 +- apps/nesemu2/src/cartdb/expat/ascii.h | 92 - apps/nesemu2/src/cartdb/expat/asciitab.h | 36 - apps/nesemu2/src/cartdb/expat/expat.h | 1050 --- .../nesemu2/src/cartdb/expat/expat_external.h | 115 - apps/nesemu2/src/cartdb/expat/iasciitab.h | 37 - apps/nesemu2/src/cartdb/expat/intconfig.h | 19 - apps/nesemu2/src/cartdb/expat/internal.h | 73 - apps/nesemu2/src/cartdb/expat/latin1tab.h | 36 - apps/nesemu2/src/cartdb/expat/nametab.h | 150 - apps/nesemu2/src/cartdb/expat/utf8tab.h | 37 - apps/nesemu2/src/cartdb/expat/xmlparse.c | 6393 ----------------- apps/nesemu2/src/cartdb/expat/xmlrole.c | 1324 ---- apps/nesemu2/src/cartdb/expat/xmlrole.h | 114 - apps/nesemu2/src/cartdb/expat/xmltok.c | 1639 ----- apps/nesemu2/src/cartdb/expat/xmltok.h | 316 - apps/nesemu2/src/cartdb/expat/xmltok_impl.c | 1783 ----- apps/nesemu2/src/cartdb/expat/xmltok_impl.h | 46 - apps/nesemu2/src/cartdb/expat/xmltok_ns.c | 115 - apps/nesemu2/src/cartdb/parser.c | 313 - apps/nesemu2/src/cartdb/parser.h | 48 - apps/nesemu2/src/emu/emu.c | 10 +- apps/nesemu2/src/emu/events.c | 2 + apps/nesemu2/src/inputdev/zapper.c | 6 +- .../src/mappers/boards/other/bmc/15in1.c | 2 +- apps/nesemu2/src/mappers/fds/calls/vector.c | 6 +- apps/nesemu2/src/mappers/fds/hle.c | 1 - apps/nesemu2/src/mappers/nsf/nsf.c | 4 +- apps/nesemu2/src/mappers/sound/s_FDS.c | 10 + apps/nesemu2/src/mappers/sound/s_VRC7.c | 9 + apps/nesemu2/src/mappers/unif.c | 15 +- apps/nesemu2/src/misc/config.c | 28 +- apps/nesemu2/src/misc/log.c | 11 +- apps/nesemu2/src/misc/memfile.c | 47 +- apps/nesemu2/src/misc/memutil.c | 20 +- apps/nesemu2/src/misc/paths.c | 4 + apps/nesemu2/src/misc/slre/slre.c | 879 --- apps/nesemu2/src/misc/slre/slre.h | 94 - apps/nesemu2/src/misc/vars.c | 13 +- apps/nesemu2/src/nes/cart/nsf.c | 4 + apps/nesemu2/src/nes/genie.c | 6 +- apps/nesemu2/src/nes/state/state.h | 2 +- apps/nesemu2/src/palette/generator.c | 171 - apps/nesemu2/src/palette/generator.h | 28 - apps/nesemu2/src/palette/palette.c | 127 +- apps/nesemu2/src/palette/palette.h | 9 +- apps/nesemu2/src/roms/.gitignore | 2 + apps/nesemu2/src/roms/build-roms.py | 1 + apps/nesemu2/src/roms/rom | 1 + .../common/filters/ntsc/nes_ntsc/nes_ntsc.h | 192 - .../common/filters/ntsc/nes_ntsc/nes_ntsc.inl | 289 - .../filters/ntsc/nes_ntsc/nes_ntsc_config.h | 27 - .../filters/ntsc/nes_ntsc/nes_ntsc_impl.h | 439 -- .../src/system/common/filters/ntsc/ntsc.c | 11 +- .../system/common/filters/scale2x/scale2x.h | 8 +- .../system/common/filters/scale2x/scale3x.h | 8 +- .../system/common/filters/scale2x/scalebit.c | 10 + .../system/common/filters/scale2x/scalebit.h | 9 +- apps/nesemu2/src/system/linux/stricmp.c | 4 +- apps/nesemu2/src/system/sdl/main.c | 136 +- apps/nesemu2/src/system/sdl/system.c | 22 +- apps/nesemu2/src/system/sdl/video.c | 28 +- apps/nesemu2/src/types.h | 34 +- libs/klib/include/klib.h | 6 +- libs/klib/src/stdlib.c | 38 + libs/klib/src/string.c | 10 + 68 files changed, 443 insertions(+), 16097 deletions(-) delete mode 100644 apps/nesemu2/src/cartdb/expat/ascii.h delete mode 100644 apps/nesemu2/src/cartdb/expat/asciitab.h delete mode 100644 apps/nesemu2/src/cartdb/expat/expat.h delete mode 100644 apps/nesemu2/src/cartdb/expat/expat_external.h delete mode 100644 apps/nesemu2/src/cartdb/expat/iasciitab.h delete mode 100644 apps/nesemu2/src/cartdb/expat/intconfig.h delete mode 100644 apps/nesemu2/src/cartdb/expat/internal.h delete mode 100644 apps/nesemu2/src/cartdb/expat/latin1tab.h delete mode 100644 apps/nesemu2/src/cartdb/expat/nametab.h delete mode 100644 apps/nesemu2/src/cartdb/expat/utf8tab.h delete mode 100644 apps/nesemu2/src/cartdb/expat/xmlparse.c delete mode 100644 apps/nesemu2/src/cartdb/expat/xmlrole.c delete mode 100644 apps/nesemu2/src/cartdb/expat/xmlrole.h delete mode 100644 apps/nesemu2/src/cartdb/expat/xmltok.c delete mode 100644 apps/nesemu2/src/cartdb/expat/xmltok.h delete mode 100644 apps/nesemu2/src/cartdb/expat/xmltok_impl.c delete mode 100644 apps/nesemu2/src/cartdb/expat/xmltok_impl.h delete mode 100644 apps/nesemu2/src/cartdb/expat/xmltok_ns.c delete mode 100644 apps/nesemu2/src/cartdb/parser.c delete mode 100644 apps/nesemu2/src/cartdb/parser.h delete mode 100644 apps/nesemu2/src/misc/slre/slre.c delete mode 100644 apps/nesemu2/src/misc/slre/slre.h delete mode 100644 apps/nesemu2/src/palette/generator.c delete mode 100644 apps/nesemu2/src/palette/generator.h create mode 100644 apps/nesemu2/src/roms/.gitignore create mode 120000 apps/nesemu2/src/roms/build-roms.py create mode 120000 apps/nesemu2/src/roms/rom delete mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.h delete mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl delete mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h delete mode 100644 apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h diff --git a/apps/litenes/src/roms/build-roms.py b/apps/litenes/src/roms/build-roms.py index 0b3a6b39..3efd7245 100644 --- a/apps/litenes/src/roms/build-roms.py +++ b/apps/litenes/src/roms/build-roms.py @@ -20,15 +20,17 @@ for (root, dirs, files) in os.walk('gen'): def h_file(): for name in roms: yield 'extern unsigned char rom_%s_nes[];' % (name) + yield 'extern unsigned int rom_%s_nes_len;' % (name) yield ''' struct rom { const char *name; void *body; + unsigned int *size; }; struct rom roms[] = {''' for name in roms: - yield ' { .name = "%s", .body = rom_%s_nes, },' % (name, name) + yield ' { .name = "%s", .body = rom_%s_nes, .size = &rom_%s_nes_len, },' % (name, name, name) yield '};' yield 'int nroms = %d;' % (len(roms)) diff --git a/apps/nesemu2/Makefile b/apps/nesemu2/Makefile index 69c28a82..69c6054b 100644 --- a/apps/nesemu2/Makefile +++ b/apps/nesemu2/Makefile @@ -1,6 +1,9 @@ NAME := nesemu2 SRCS := $(shell find -L ./src/ -name "*.c") - -CFLAGS += -DCPU_UNDOC -DQUICK_SPRITES -DSDL -DLINUX -DRELEASE -Isrc +CFLAGS += -DCPU_UNDOC -DQUICK_SPRITES -DSDL -DLINUX -DRELEASE -DNO_FILE_SYSTEM -Isrc include $(AM_HOME)/Makefile.app + +.PHONY: rom +rom: + @cd src/roms && python3 build-roms.py diff --git a/apps/nesemu2/src/cartdb/cartdb.c b/apps/nesemu2/src/cartdb/cartdb.c index b27da4f2..288e96a2 100644 --- a/apps/nesemu2/src/cartdb/cartdb.c +++ b/apps/nesemu2/src/cartdb/cartdb.c @@ -18,9 +18,11 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include "cartdb/cartdb.h" + +#if 0 #include #include -#include "cartdb/cartdb.h" #include "cartdb/parser.h" #include "misc/memutil.h" #include "misc/strutil.h" @@ -31,7 +33,6 @@ #include "mappers/mapperid.h" static xml_t *cartxml = 0; - int cartdb_init() { char filename[1024]; @@ -500,3 +501,8 @@ int cartdb_find(cart_t *cart) log_printf("cartdb_find: cart not found in database.\n"); return(1); } +#endif + +int cartdb_init() { return 0; } +void cartdb_kill() { } +int cartdb_find(cart_t *cart) { return 1; } diff --git a/apps/nesemu2/src/cartdb/expat/ascii.h b/apps/nesemu2/src/cartdb/expat/ascii.h deleted file mode 100644 index d10530b0..00000000 --- a/apps/nesemu2/src/cartdb/expat/ascii.h +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#define ASCII_A 0x41 -#define ASCII_B 0x42 -#define ASCII_C 0x43 -#define ASCII_D 0x44 -#define ASCII_E 0x45 -#define ASCII_F 0x46 -#define ASCII_G 0x47 -#define ASCII_H 0x48 -#define ASCII_I 0x49 -#define ASCII_J 0x4A -#define ASCII_K 0x4B -#define ASCII_L 0x4C -#define ASCII_M 0x4D -#define ASCII_N 0x4E -#define ASCII_O 0x4F -#define ASCII_P 0x50 -#define ASCII_Q 0x51 -#define ASCII_R 0x52 -#define ASCII_S 0x53 -#define ASCII_T 0x54 -#define ASCII_U 0x55 -#define ASCII_V 0x56 -#define ASCII_W 0x57 -#define ASCII_X 0x58 -#define ASCII_Y 0x59 -#define ASCII_Z 0x5A - -#define ASCII_a 0x61 -#define ASCII_b 0x62 -#define ASCII_c 0x63 -#define ASCII_d 0x64 -#define ASCII_e 0x65 -#define ASCII_f 0x66 -#define ASCII_g 0x67 -#define ASCII_h 0x68 -#define ASCII_i 0x69 -#define ASCII_j 0x6A -#define ASCII_k 0x6B -#define ASCII_l 0x6C -#define ASCII_m 0x6D -#define ASCII_n 0x6E -#define ASCII_o 0x6F -#define ASCII_p 0x70 -#define ASCII_q 0x71 -#define ASCII_r 0x72 -#define ASCII_s 0x73 -#define ASCII_t 0x74 -#define ASCII_u 0x75 -#define ASCII_v 0x76 -#define ASCII_w 0x77 -#define ASCII_x 0x78 -#define ASCII_y 0x79 -#define ASCII_z 0x7A - -#define ASCII_0 0x30 -#define ASCII_1 0x31 -#define ASCII_2 0x32 -#define ASCII_3 0x33 -#define ASCII_4 0x34 -#define ASCII_5 0x35 -#define ASCII_6 0x36 -#define ASCII_7 0x37 -#define ASCII_8 0x38 -#define ASCII_9 0x39 - -#define ASCII_TAB 0x09 -#define ASCII_SPACE 0x20 -#define ASCII_EXCL 0x21 -#define ASCII_QUOT 0x22 -#define ASCII_AMP 0x26 -#define ASCII_APOS 0x27 -#define ASCII_MINUS 0x2D -#define ASCII_PERIOD 0x2E -#define ASCII_COLON 0x3A -#define ASCII_SEMI 0x3B -#define ASCII_LT 0x3C -#define ASCII_EQUALS 0x3D -#define ASCII_GT 0x3E -#define ASCII_LSQB 0x5B -#define ASCII_RSQB 0x5D -#define ASCII_UNDERSCORE 0x5F -#define ASCII_LPAREN 0x28 -#define ASCII_RPAREN 0x29 -#define ASCII_FF 0x0C -#define ASCII_SLASH 0x2F -#define ASCII_HASH 0x23 -#define ASCII_PIPE 0x7C -#define ASCII_COMMA 0x2C diff --git a/apps/nesemu2/src/cartdb/expat/asciitab.h b/apps/nesemu2/src/cartdb/expat/asciitab.h deleted file mode 100644 index 79a15c28..00000000 --- a/apps/nesemu2/src/cartdb/expat/asciitab.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, -/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, -/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, -/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, -/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, -/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, -/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, -/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, -/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, -/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, -/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/apps/nesemu2/src/cartdb/expat/expat.h b/apps/nesemu2/src/cartdb/expat/expat.h deleted file mode 100644 index 5e0c6e37..00000000 --- a/apps/nesemu2/src/cartdb/expat/expat.h +++ /dev/null @@ -1,1050 +0,0 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#ifndef Expat_INCLUDED -#define Expat_INCLUDED 1 - -//force building static library -#define XML_STATIC - -#ifdef __VMS -/* 0 1 2 3 0 1 2 3 - 1234567890123456789012345678901 1234567890123456789012345678901 */ -#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler -#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler -#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler -#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg -#endif - -#include -#include "expat_external.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct XML_ParserStruct; -typedef struct XML_ParserStruct *XML_Parser; - -/* Should this be defined using stdbool.h when C99 is available? */ -typedef unsigned char XML_Bool; -#define XML_TRUE ((XML_Bool) 1) -#define XML_FALSE ((XML_Bool) 0) - -/* The XML_Status enum gives the possible return values for several - API functions. The preprocessor #defines are included so this - stanza can be added to code that still needs to support older - versions of Expat 1.95.x: - - #ifndef XML_STATUS_OK - #define XML_STATUS_OK 1 - #define XML_STATUS_ERROR 0 - #endif - - Otherwise, the #define hackery is quite ugly and would have been - dropped. -*/ -enum XML_Status { - XML_STATUS_ERROR = 0, -#define XML_STATUS_ERROR XML_STATUS_ERROR - XML_STATUS_OK = 1, -#define XML_STATUS_OK XML_STATUS_OK - XML_STATUS_SUSPENDED = 2 -#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED -}; - -enum XML_Error { - XML_ERROR_NONE, - XML_ERROR_NO_MEMORY, - XML_ERROR_SYNTAX, - XML_ERROR_NO_ELEMENTS, - XML_ERROR_INVALID_TOKEN, - XML_ERROR_UNCLOSED_TOKEN, - XML_ERROR_PARTIAL_CHAR, - XML_ERROR_TAG_MISMATCH, - XML_ERROR_DUPLICATE_ATTRIBUTE, - XML_ERROR_JUNK_AFTER_DOC_ELEMENT, - XML_ERROR_PARAM_ENTITY_REF, - XML_ERROR_UNDEFINED_ENTITY, - XML_ERROR_RECURSIVE_ENTITY_REF, - XML_ERROR_ASYNC_ENTITY, - XML_ERROR_BAD_CHAR_REF, - XML_ERROR_BINARY_ENTITY_REF, - XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, - XML_ERROR_MISPLACED_XML_PI, - XML_ERROR_UNKNOWN_ENCODING, - XML_ERROR_INCORRECT_ENCODING, - XML_ERROR_UNCLOSED_CDATA_SECTION, - XML_ERROR_EXTERNAL_ENTITY_HANDLING, - XML_ERROR_NOT_STANDALONE, - XML_ERROR_UNEXPECTED_STATE, - XML_ERROR_ENTITY_DECLARED_IN_PE, - XML_ERROR_FEATURE_REQUIRES_XML_DTD, - XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, - /* Added in 1.95.7. */ - XML_ERROR_UNBOUND_PREFIX, - /* Added in 1.95.8. */ - XML_ERROR_UNDECLARING_PREFIX, - XML_ERROR_INCOMPLETE_PE, - XML_ERROR_XML_DECL, - XML_ERROR_TEXT_DECL, - XML_ERROR_PUBLICID, - XML_ERROR_SUSPENDED, - XML_ERROR_NOT_SUSPENDED, - XML_ERROR_ABORTED, - XML_ERROR_FINISHED, - XML_ERROR_SUSPEND_PE, - /* Added in 2.0. */ - XML_ERROR_RESERVED_PREFIX_XML, - XML_ERROR_RESERVED_PREFIX_XMLNS, - XML_ERROR_RESERVED_NAMESPACE_URI -}; - -enum XML_Content_Type { - XML_CTYPE_EMPTY = 1, - XML_CTYPE_ANY, - XML_CTYPE_MIXED, - XML_CTYPE_NAME, - XML_CTYPE_CHOICE, - XML_CTYPE_SEQ -}; - -enum XML_Content_Quant { - XML_CQUANT_NONE, - XML_CQUANT_OPT, - XML_CQUANT_REP, - XML_CQUANT_PLUS -}; - -/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be - XML_CQUANT_NONE, and the other fields will be zero or NULL. - If type == XML_CTYPE_MIXED, then quant will be NONE or REP and - numchildren will contain number of elements that may be mixed in - and children point to an array of XML_Content cells that will be - all of XML_CTYPE_NAME type with no quantification. - - If type == XML_CTYPE_NAME, then the name points to the name, and - the numchildren field will be zero and children will be NULL. The - quant fields indicates any quantifiers placed on the name. - - CHOICE and SEQ will have name NULL, the number of children in - numchildren and children will point, recursively, to an array - of XML_Content cells. - - The EMPTY, ANY, and MIXED types will only occur at top level. -*/ - -typedef struct XML_cp XML_Content; - -struct XML_cp { - enum XML_Content_Type type; - enum XML_Content_Quant quant; - XML_Char * name; - unsigned int numchildren; - XML_Content * children; -}; - - -/* This is called for an element declaration. See above for - description of the model argument. It's the caller's responsibility - to free model when finished with it. -*/ -typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, - const XML_Char *name, - XML_Content *model); - -XMLPARSEAPI(void) -XML_SetElementDeclHandler(XML_Parser parser, - XML_ElementDeclHandler eldecl); - -/* The Attlist declaration handler is called for *each* attribute. So - a single Attlist declaration with multiple attributes declared will - generate multiple calls to this handler. The "default" parameter - may be NULL in the case of the "#IMPLIED" or "#REQUIRED" - keyword. The "isrequired" parameter will be true and the default - value will be NULL in the case of "#REQUIRED". If "isrequired" is - true and default is non-NULL, then this is a "#FIXED" default. -*/ -typedef void (XMLCALL *XML_AttlistDeclHandler) ( - void *userData, - const XML_Char *elname, - const XML_Char *attname, - const XML_Char *att_type, - const XML_Char *dflt, - int isrequired); - -XMLPARSEAPI(void) -XML_SetAttlistDeclHandler(XML_Parser parser, - XML_AttlistDeclHandler attdecl); - -/* The XML declaration handler is called for *both* XML declarations - and text declarations. The way to distinguish is that the version - parameter will be NULL for text declarations. The encoding - parameter may be NULL for XML declarations. The standalone - parameter will be -1, 0, or 1 indicating respectively that there - was no standalone parameter in the declaration, that it was given - as no, or that it was given as yes. -*/ -typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, - const XML_Char *version, - const XML_Char *encoding, - int standalone); - -XMLPARSEAPI(void) -XML_SetXmlDeclHandler(XML_Parser parser, - XML_XmlDeclHandler xmldecl); - - -typedef struct { - void *(*malloc_fcn)(size_t size); - void *(*realloc_fcn)(void *ptr, size_t size); - void (*free_fcn)(void *ptr); -} XML_Memory_Handling_Suite; - -/* Constructs a new parser; encoding is the encoding specified by the - external protocol or NULL if there is none specified. -*/ -XMLPARSEAPI(XML_Parser) -XML_ParserCreate(const XML_Char *encoding); - -/* Constructs a new parser and namespace processor. Element type - names and attribute names that belong to a namespace will be - expanded; unprefixed attribute names are never expanded; unprefixed - element type names are expanded only if there is a default - namespace. The expanded name is the concatenation of the namespace - URI, the namespace separator character, and the local part of the - name. If the namespace separator is '\0' then the namespace URI - and the local part will be concatenated without any separator. - It is a programming error to use the separator '\0' with namespace - triplets (see XML_SetReturnNSTriplet). -*/ -XMLPARSEAPI(XML_Parser) -XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); - - -/* Constructs a new parser using the memory management suite referred to - by memsuite. If memsuite is NULL, then use the standard library memory - suite. If namespaceSeparator is non-NULL it creates a parser with - namespace processing as described above. The character pointed at - will serve as the namespace separator. - - All further memory operations used for the created parser will come from - the given suite. -*/ -XMLPARSEAPI(XML_Parser) -XML_ParserCreate_MM(const XML_Char *encoding, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *namespaceSeparator); - -/* Prepare a parser object to be re-used. This is particularly - valuable when memory allocation overhead is disproportionatly high, - such as when a large number of small documnents need to be parsed. - All handlers are cleared from the parser, except for the - unknownEncodingHandler. The parser's external state is re-initialized - except for the values of ns and ns_triplets. - - Added in Expat 1.95.3. -*/ -XMLPARSEAPI(XML_Bool) -XML_ParserReset(XML_Parser parser, const XML_Char *encoding); - -/* atts is array of name/value pairs, terminated by 0; - names and values are 0 terminated. -*/ -typedef void (XMLCALL *XML_StartElementHandler) (void *userData, - const XML_Char *name, - const XML_Char **atts); - -typedef void (XMLCALL *XML_EndElementHandler) (void *userData, - const XML_Char *name); - - -/* s is not 0 terminated. */ -typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, - const XML_Char *s, - int len); - -/* target and data are 0 terminated */ -typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( - void *userData, - const XML_Char *target, - const XML_Char *data); - -/* data is 0 terminated */ -typedef void (XMLCALL *XML_CommentHandler) (void *userData, - const XML_Char *data); - -typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); -typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); - -/* This is called for any characters in the XML document for which - there is no applicable handler. This includes both characters that - are part of markup which is of a kind that is not reported - (comments, markup declarations), or characters that are part of a - construct which could be reported but for which no handler has been - supplied. The characters are passed exactly as they were in the XML - document except that they will be encoded in UTF-8 or UTF-16. - Line boundaries are not normalized. Note that a byte order mark - character is not passed to the default handler. There are no - guarantees about how characters are divided between calls to the - default handler: for example, a comment might be split between - multiple calls. -*/ -typedef void (XMLCALL *XML_DefaultHandler) (void *userData, - const XML_Char *s, - int len); - -/* This is called for the start of the DOCTYPE declaration, before - any DTD or internal subset is parsed. -*/ -typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( - void *userData, - const XML_Char *doctypeName, - const XML_Char *sysid, - const XML_Char *pubid, - int has_internal_subset); - -/* This is called for the start of the DOCTYPE declaration when the - closing > is encountered, but after processing any external - subset. -*/ -typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); - -/* This is called for entity declarations. The is_parameter_entity - argument will be non-zero if the entity is a parameter entity, zero - otherwise. - - For internal entities (), value will - be non-NULL and systemId, publicID, and notationName will be NULL. - The value string is NOT nul-terminated; the length is provided in - the value_length argument. Since it is legal to have zero-length - values, do not use this argument to test for internal entities. - - For external entities, value will be NULL and systemId will be - non-NULL. The publicId argument will be NULL unless a public - identifier was provided. The notationName argument will have a - non-NULL value only for unparsed entity declarations. - - Note that is_parameter_entity can't be changed to XML_Bool, since - that would break binary compatibility. -*/ -typedef void (XMLCALL *XML_EntityDeclHandler) ( - void *userData, - const XML_Char *entityName, - int is_parameter_entity, - const XML_Char *value, - int value_length, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName); - -XMLPARSEAPI(void) -XML_SetEntityDeclHandler(XML_Parser parser, - XML_EntityDeclHandler handler); - -/* OBSOLETE -- OBSOLETE -- OBSOLETE - This handler has been superceded by the EntityDeclHandler above. - It is provided here for backward compatibility. - - This is called for a declaration of an unparsed (NDATA) entity. - The base argument is whatever was set by XML_SetBase. The - entityName, systemId and notationName arguments will never be - NULL. The other arguments may be. -*/ -typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( - void *userData, - const XML_Char *entityName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId, - const XML_Char *notationName); - -/* This is called for a declaration of notation. The base argument is - whatever was set by XML_SetBase. The notationName will never be - NULL. The other arguments can be. -*/ -typedef void (XMLCALL *XML_NotationDeclHandler) ( - void *userData, - const XML_Char *notationName, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId); - -/* When namespace processing is enabled, these are called once for - each namespace declaration. The call to the start and end element - handlers occur between the calls to the start and end namespace - declaration handlers. For an xmlns attribute, prefix will be - NULL. For an xmlns="" attribute, uri will be NULL. -*/ -typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( - void *userData, - const XML_Char *prefix, - const XML_Char *uri); - -typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( - void *userData, - const XML_Char *prefix); - -/* This is called if the document is not standalone, that is, it has an - external subset or a reference to a parameter entity, but does not - have standalone="yes". If this handler returns XML_STATUS_ERROR, - then processing will not continue, and the parser will return a - XML_ERROR_NOT_STANDALONE error. - If parameter entity parsing is enabled, then in addition to the - conditions above this handler will only be called if the referenced - entity was actually read. -*/ -typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); - -/* This is called for a reference to an external parsed general - entity. The referenced entity is not automatically parsed. The - application can parse it immediately or later using - XML_ExternalEntityParserCreate. - - The parser argument is the parser parsing the entity containing the - reference; it can be passed as the parser argument to - XML_ExternalEntityParserCreate. The systemId argument is the - system identifier as specified in the entity declaration; it will - not be NULL. - - The base argument is the system identifier that should be used as - the base for resolving systemId if systemId was relative; this is - set by XML_SetBase; it may be NULL. - - The publicId argument is the public identifier as specified in the - entity declaration, or NULL if none was specified; the whitespace - in the public identifier will have been normalized as required by - the XML spec. - - The context argument specifies the parsing context in the format - expected by the context argument to XML_ExternalEntityParserCreate; - context is valid only until the handler returns, so if the - referenced entity is to be parsed later, it must be copied. - context is NULL only when the entity is a parameter entity. - - The handler should return XML_STATUS_ERROR if processing should not - continue because of a fatal error in the handling of the external - entity. In this case the calling parser will return an - XML_ERROR_EXTERNAL_ENTITY_HANDLING error. - - Note that unlike other handlers the first argument is the parser, - not userData. -*/ -typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( - XML_Parser parser, - const XML_Char *context, - const XML_Char *base, - const XML_Char *systemId, - const XML_Char *publicId); - -/* This is called in two situations: - 1) An entity reference is encountered for which no declaration - has been read *and* this is not an error. - 2) An internal entity reference is read, but not expanded, because - XML_SetDefaultHandler has been called. - Note: skipped parameter entities in declarations and skipped general - entities in attribute values cannot be reported, because - the event would be out of sync with the reporting of the - declarations or attribute values -*/ -typedef void (XMLCALL *XML_SkippedEntityHandler) ( - void *userData, - const XML_Char *entityName, - int is_parameter_entity); - -/* This structure is filled in by the XML_UnknownEncodingHandler to - provide information to the parser about encodings that are unknown - to the parser. - - The map[b] member gives information about byte sequences whose - first byte is b. - - If map[b] is c where c is >= 0, then b by itself encodes the - Unicode scalar value c. - - If map[b] is -1, then the byte sequence is malformed. - - If map[b] is -n, where n >= 2, then b is the first byte of an - n-byte sequence that encodes a single Unicode scalar value. - - The data member will be passed as the first argument to the convert - function. - - The convert function is used to convert multibyte sequences; s will - point to a n-byte sequence where map[(unsigned char)*s] == -n. The - convert function must return the Unicode scalar value represented - by this byte sequence or -1 if the byte sequence is malformed. - - The convert function may be NULL if the encoding is a single-byte - encoding, that is if map[b] >= -1 for all bytes b. - - When the parser is finished with the encoding, then if release is - not NULL, it will call release passing it the data member; once - release has been called, the convert function will not be called - again. - - Expat places certain restrictions on the encodings that are supported - using this mechanism. - - 1. Every ASCII character that can appear in a well-formed XML document, - other than the characters - - $@\^`{}~ - - must be represented by a single byte, and that byte must be the - same byte that represents that character in ASCII. - - 2. No character may require more than 4 bytes to encode. - - 3. All characters encoded must have Unicode scalar values <= - 0xFFFF, (i.e., characters that would be encoded by surrogates in - UTF-16 are not allowed). Note that this restriction doesn't - apply to the built-in support for UTF-8 and UTF-16. - - 4. No Unicode character may be encoded by more than one distinct - sequence of bytes. -*/ -typedef struct { - int map[256]; - void *data; - int (XMLCALL *convert)(void *data, const char *s); - void (XMLCALL *release)(void *data); -} XML_Encoding; - -/* This is called for an encoding that is unknown to the parser. - - The encodingHandlerData argument is that which was passed as the - second argument to XML_SetUnknownEncodingHandler. - - The name argument gives the name of the encoding as specified in - the encoding declaration. - - If the callback can provide information about the encoding, it must - fill in the XML_Encoding structure, and return XML_STATUS_OK. - Otherwise it must return XML_STATUS_ERROR. - - If info does not describe a suitable encoding, then the parser will - return an XML_UNKNOWN_ENCODING error. -*/ -typedef int (XMLCALL *XML_UnknownEncodingHandler) ( - void *encodingHandlerData, - const XML_Char *name, - XML_Encoding *info); - -XMLPARSEAPI(void) -XML_SetElementHandler(XML_Parser parser, - XML_StartElementHandler start, - XML_EndElementHandler end); - -XMLPARSEAPI(void) -XML_SetStartElementHandler(XML_Parser parser, - XML_StartElementHandler handler); - -XMLPARSEAPI(void) -XML_SetEndElementHandler(XML_Parser parser, - XML_EndElementHandler handler); - -XMLPARSEAPI(void) -XML_SetCharacterDataHandler(XML_Parser parser, - XML_CharacterDataHandler handler); - -XMLPARSEAPI(void) -XML_SetProcessingInstructionHandler(XML_Parser parser, - XML_ProcessingInstructionHandler handler); -XMLPARSEAPI(void) -XML_SetCommentHandler(XML_Parser parser, - XML_CommentHandler handler); - -XMLPARSEAPI(void) -XML_SetCdataSectionHandler(XML_Parser parser, - XML_StartCdataSectionHandler start, - XML_EndCdataSectionHandler end); - -XMLPARSEAPI(void) -XML_SetStartCdataSectionHandler(XML_Parser parser, - XML_StartCdataSectionHandler start); - -XMLPARSEAPI(void) -XML_SetEndCdataSectionHandler(XML_Parser parser, - XML_EndCdataSectionHandler end); - -/* This sets the default handler and also inhibits expansion of - internal entities. These entity references will be passed to the - default handler, or to the skipped entity handler, if one is set. -*/ -XMLPARSEAPI(void) -XML_SetDefaultHandler(XML_Parser parser, - XML_DefaultHandler handler); - -/* This sets the default handler but does not inhibit expansion of - internal entities. The entity reference will not be passed to the - default handler. -*/ -XMLPARSEAPI(void) -XML_SetDefaultHandlerExpand(XML_Parser parser, - XML_DefaultHandler handler); - -XMLPARSEAPI(void) -XML_SetDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start, - XML_EndDoctypeDeclHandler end); - -XMLPARSEAPI(void) -XML_SetStartDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start); - -XMLPARSEAPI(void) -XML_SetEndDoctypeDeclHandler(XML_Parser parser, - XML_EndDoctypeDeclHandler end); - -XMLPARSEAPI(void) -XML_SetUnparsedEntityDeclHandler(XML_Parser parser, - XML_UnparsedEntityDeclHandler handler); - -XMLPARSEAPI(void) -XML_SetNotationDeclHandler(XML_Parser parser, - XML_NotationDeclHandler handler); - -XMLPARSEAPI(void) -XML_SetNamespaceDeclHandler(XML_Parser parser, - XML_StartNamespaceDeclHandler start, - XML_EndNamespaceDeclHandler end); - -XMLPARSEAPI(void) -XML_SetStartNamespaceDeclHandler(XML_Parser parser, - XML_StartNamespaceDeclHandler start); - -XMLPARSEAPI(void) -XML_SetEndNamespaceDeclHandler(XML_Parser parser, - XML_EndNamespaceDeclHandler end); - -XMLPARSEAPI(void) -XML_SetNotStandaloneHandler(XML_Parser parser, - XML_NotStandaloneHandler handler); - -XMLPARSEAPI(void) -XML_SetExternalEntityRefHandler(XML_Parser parser, - XML_ExternalEntityRefHandler handler); - -/* If a non-NULL value for arg is specified here, then it will be - passed as the first argument to the external entity ref handler - instead of the parser object. -*/ -XMLPARSEAPI(void) -XML_SetExternalEntityRefHandlerArg(XML_Parser parser, - void *arg); - -XMLPARSEAPI(void) -XML_SetSkippedEntityHandler(XML_Parser parser, - XML_SkippedEntityHandler handler); - -XMLPARSEAPI(void) -XML_SetUnknownEncodingHandler(XML_Parser parser, - XML_UnknownEncodingHandler handler, - void *encodingHandlerData); - -/* This can be called within a handler for a start element, end - element, processing instruction or character data. It causes the - corresponding markup to be passed to the default handler. -*/ -XMLPARSEAPI(void) -XML_DefaultCurrent(XML_Parser parser); - -/* If do_nst is non-zero, and namespace processing is in effect, and - a name has a prefix (i.e. an explicit namespace qualifier) then - that name is returned as a triplet in a single string separated by - the separator character specified when the parser was created: URI - + sep + local_name + sep + prefix. - - If do_nst is zero, then namespace information is returned in the - default manner (URI + sep + local_name) whether or not the name - has a prefix. - - Note: Calling XML_SetReturnNSTriplet after XML_Parse or - XML_ParseBuffer has no effect. -*/ - -XMLPARSEAPI(void) -XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); - -/* This value is passed as the userData argument to callbacks. */ -XMLPARSEAPI(void) -XML_SetUserData(XML_Parser parser, void *userData); - -/* Returns the last value set by XML_SetUserData or NULL. */ -#define XML_GetUserData(parser) (*(void **)(parser)) - -/* This is equivalent to supplying an encoding argument to - XML_ParserCreate. On success XML_SetEncoding returns non-zero, - zero otherwise. - Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer - has no effect and returns XML_STATUS_ERROR. -*/ -XMLPARSEAPI(enum XML_Status) -XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); - -/* If this function is called, then the parser will be passed as the - first argument to callbacks instead of userData. The userData will - still be accessible using XML_GetUserData. -*/ -XMLPARSEAPI(void) -XML_UseParserAsHandlerArg(XML_Parser parser); - -/* If useDTD == XML_TRUE is passed to this function, then the parser - will assume that there is an external subset, even if none is - specified in the document. In such a case the parser will call the - externalEntityRefHandler with a value of NULL for the systemId - argument (the publicId and context arguments will be NULL as well). - Note: For the purpose of checking WFC: Entity Declared, passing - useDTD == XML_TRUE will make the parser behave as if the document - had a DTD with an external subset. - Note: If this function is called, then this must be done before - the first call to XML_Parse or XML_ParseBuffer, since it will - have no effect after that. Returns - XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. - Note: If the document does not have a DOCTYPE declaration at all, - then startDoctypeDeclHandler and endDoctypeDeclHandler will not - be called, despite an external subset being parsed. - Note: If XML_DTD is not defined when Expat is compiled, returns - XML_ERROR_FEATURE_REQUIRES_XML_DTD. -*/ -XMLPARSEAPI(enum XML_Error) -XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); - - -/* Sets the base to be used for resolving relative URIs in system - identifiers in declarations. Resolving relative identifiers is - left to the application: this value will be passed through as the - base argument to the XML_ExternalEntityRefHandler, - XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base - argument will be copied. Returns XML_STATUS_ERROR if out of memory, - XML_STATUS_OK otherwise. -*/ -XMLPARSEAPI(enum XML_Status) -XML_SetBase(XML_Parser parser, const XML_Char *base); - -XMLPARSEAPI(const XML_Char *) -XML_GetBase(XML_Parser parser); - -/* Returns the number of the attribute/value pairs passed in last call - to the XML_StartElementHandler that were specified in the start-tag - rather than defaulted. Each attribute/value pair counts as 2; thus - this correspondds to an index into the atts array passed to the - XML_StartElementHandler. -*/ -XMLPARSEAPI(int) -XML_GetSpecifiedAttributeCount(XML_Parser parser); - -/* Returns the index of the ID attribute passed in the last call to - XML_StartElementHandler, or -1 if there is no ID attribute. Each - attribute/value pair counts as 2; thus this correspondds to an - index into the atts array passed to the XML_StartElementHandler. -*/ -XMLPARSEAPI(int) -XML_GetIdAttributeIndex(XML_Parser parser); - -#ifdef XML_ATTR_INFO -/* Source file byte offsets for the start and end of attribute names and values. - The value indices are exclusive of surrounding quotes; thus in a UTF-8 source - file an attribute value of "blah" will yield: - info->valueEnd - info->valueStart = 4 bytes. -*/ -typedef struct { - XML_Index nameStart; /* Offset to beginning of the attribute name. */ - XML_Index nameEnd; /* Offset after the attribute name's last byte. */ - XML_Index valueStart; /* Offset to beginning of the attribute value. */ - XML_Index valueEnd; /* Offset after the attribute value's last byte. */ -} XML_AttrInfo; - -/* Returns an array of XML_AttrInfo structures for the attribute/value pairs - passed in last call to the XML_StartElementHandler that were specified - in the start-tag rather than defaulted. Each attribute/value pair counts - as 1; thus the number of entries in the array is - XML_GetSpecifiedAttributeCount(parser) / 2. -*/ -XMLPARSEAPI(const XML_AttrInfo *) -XML_GetAttributeInfo(XML_Parser parser); -#endif - -/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is - detected. The last call to XML_Parse must have isFinal true; len - may be zero for this call (or any other). - - Though the return values for these functions has always been - described as a Boolean value, the implementation, at least for the - 1.95.x series, has always returned exactly one of the XML_Status - values. -*/ -XMLPARSEAPI(enum XML_Status) -XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); - -XMLPARSEAPI(void *) -XML_GetBuffer(XML_Parser parser, int len); - -XMLPARSEAPI(enum XML_Status) -XML_ParseBuffer(XML_Parser parser, int len, int isFinal); - -/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. - Must be called from within a call-back handler, except when aborting - (resumable = 0) an already suspended parser. Some call-backs may - still follow because they would otherwise get lost. Examples: - - endElementHandler() for empty elements when stopped in - startElementHandler(), - - endNameSpaceDeclHandler() when stopped in endElementHandler(), - and possibly others. - - Can be called from most handlers, including DTD related call-backs, - except when parsing an external parameter entity and resumable != 0. - Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. - Possible error codes: - - XML_ERROR_SUSPENDED: when suspending an already suspended parser. - - XML_ERROR_FINISHED: when the parser has already finished. - - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. - - When resumable != 0 (true) then parsing is suspended, that is, - XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. - Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() - return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. - - *Note*: - This will be applied to the current parser instance only, that is, if - there is a parent parser then it will continue parsing when the - externalEntityRefHandler() returns. It is up to the implementation of - the externalEntityRefHandler() to call XML_StopParser() on the parent - parser (recursively), if one wants to stop parsing altogether. - - When suspended, parsing can be resumed by calling XML_ResumeParser(). -*/ -XMLPARSEAPI(enum XML_Status) -XML_StopParser(XML_Parser parser, XML_Bool resumable); - -/* Resumes parsing after it has been suspended with XML_StopParser(). - Must not be called from within a handler call-back. Returns same - status codes as XML_Parse() or XML_ParseBuffer(). - Additional error code XML_ERROR_NOT_SUSPENDED possible. - - *Note*: - This must be called on the most deeply nested child parser instance - first, and on its parent parser only after the child parser has finished, - to be applied recursively until the document entity's parser is restarted. - That is, the parent parser will not resume by itself and it is up to the - application to call XML_ResumeParser() on it at the appropriate moment. -*/ -XMLPARSEAPI(enum XML_Status) -XML_ResumeParser(XML_Parser parser); - -enum XML_Parsing { - XML_INITIALIZED, - XML_PARSING, - XML_FINISHED, - XML_SUSPENDED -}; - -typedef struct { - enum XML_Parsing parsing; - XML_Bool finalBuffer; -} XML_ParsingStatus; - -/* Returns status of parser with respect to being initialized, parsing, - finished, or suspended and processing the final buffer. - XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, - XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED -*/ -XMLPARSEAPI(void) -XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); - -/* Creates an XML_Parser object that can parse an external general - entity; context is a '\0'-terminated string specifying the parse - context; encoding is a '\0'-terminated string giving the name of - the externally specified encoding, or NULL if there is no - externally specified encoding. The context string consists of a - sequence of tokens separated by formfeeds (\f); a token consisting - of a name specifies that the general entity of the name is open; a - token of the form prefix=uri specifies the namespace for a - particular prefix; a token of the form =uri specifies the default - namespace. This can be called at any point after the first call to - an ExternalEntityRefHandler so longer as the parser has not yet - been freed. The new parser is completely independent and may - safely be used in a separate thread. The handlers and userData are - initialized from the parser argument. Returns NULL if out of memory. - Otherwise returns a new XML_Parser object. -*/ -XMLPARSEAPI(XML_Parser) -XML_ExternalEntityParserCreate(XML_Parser parser, - const XML_Char *context, - const XML_Char *encoding); - -enum XML_ParamEntityParsing { - XML_PARAM_ENTITY_PARSING_NEVER, - XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, - XML_PARAM_ENTITY_PARSING_ALWAYS -}; - -/* Controls parsing of parameter entities (including the external DTD - subset). If parsing of parameter entities is enabled, then - references to external parameter entities (including the external - DTD subset) will be passed to the handler set with - XML_SetExternalEntityRefHandler. The context passed will be 0. - - Unlike external general entities, external parameter entities can - only be parsed synchronously. If the external parameter entity is - to be parsed, it must be parsed during the call to the external - entity ref handler: the complete sequence of - XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and - XML_ParserFree calls must be made during this call. After - XML_ExternalEntityParserCreate has been called to create the parser - for the external parameter entity (context must be 0 for this - call), it is illegal to make any calls on the old parser until - XML_ParserFree has been called on the newly created parser. - If the library has been compiled without support for parameter - entity parsing (ie without XML_DTD being defined), then - XML_SetParamEntityParsing will return 0 if parsing of parameter - entities is requested; otherwise it will return non-zero. - Note: If XML_SetParamEntityParsing is called after XML_Parse or - XML_ParseBuffer, then it has no effect and will always return 0. -*/ -XMLPARSEAPI(int) -XML_SetParamEntityParsing(XML_Parser parser, - enum XML_ParamEntityParsing parsing); - -/* Sets the hash salt to use for internal hash calculations. - Helps in preventing DoS attacks based on predicting hash - function behavior. This must be called before parsing is started. - Returns 1 if successful, 0 when called after parsing has started. -*/ -XMLPARSEAPI(int) -XML_SetHashSalt(XML_Parser parser, - unsigned long hash_salt); - -/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then - XML_GetErrorCode returns information about the error. -*/ -XMLPARSEAPI(enum XML_Error) -XML_GetErrorCode(XML_Parser parser); - -/* These functions return information about the current parse - location. They may be called from any callback called to report - some parse event; in this case the location is the location of the - first of the sequence of characters that generated the event. When - called from callbacks generated by declarations in the document - prologue, the location identified isn't as neatly defined, but will - be within the relevant markup. When called outside of the callback - functions, the position indicated will be just past the last parse - event (regardless of whether there was an associated callback). - - They may also be called after returning from a call to XML_Parse - or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then - the location is the location of the character at which the error - was detected; otherwise the location is the location of the last - parse event, as described above. -*/ -XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); -XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); -XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); - -/* Return the number of bytes in the current event. - Returns 0 if the event is in an internal entity. -*/ -XMLPARSEAPI(int) -XML_GetCurrentByteCount(XML_Parser parser); - -/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets - the integer pointed to by offset to the offset within this buffer - of the current parse position, and sets the integer pointed to by size - to the size of this buffer (the number of input bytes). Otherwise - returns a NULL pointer. Also returns a NULL pointer if a parse isn't - active. - - NOTE: The character pointer returned should not be used outside - the handler that makes the call. -*/ -XMLPARSEAPI(const char *) -XML_GetInputContext(XML_Parser parser, - int *offset, - int *size); - -/* For backwards compatibility with previous versions. */ -#define XML_GetErrorLineNumber XML_GetCurrentLineNumber -#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber -#define XML_GetErrorByteIndex XML_GetCurrentByteIndex - -/* Frees the content model passed to the element declaration handler */ -XMLPARSEAPI(void) -XML_FreeContentModel(XML_Parser parser, XML_Content *model); - -/* Exposing the memory handling functions used in Expat */ -XMLPARSEAPI(void *) -XML_MemMalloc(XML_Parser parser, size_t size); - -XMLPARSEAPI(void *) -XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); - -XMLPARSEAPI(void) -XML_MemFree(XML_Parser parser, void *ptr); - -/* Frees memory used by the parser. */ -XMLPARSEAPI(void) -XML_ParserFree(XML_Parser parser); - -/* Returns a string describing the error. */ -XMLPARSEAPI(const XML_LChar *) -XML_ErrorString(enum XML_Error code); - -/* Return a string containing the version number of this expat */ -XMLPARSEAPI(const XML_LChar *) -XML_ExpatVersion(void); - -typedef struct { - int major; - int minor; - int micro; -} XML_Expat_Version; - -/* Return an XML_Expat_Version structure containing numeric version - number information for this version of expat. -*/ -XMLPARSEAPI(XML_Expat_Version) -XML_ExpatVersionInfo(void); - -/* Added in Expat 1.95.5. */ -enum XML_FeatureEnum { - XML_FEATURE_END = 0, - XML_FEATURE_UNICODE, - XML_FEATURE_UNICODE_WCHAR_T, - XML_FEATURE_DTD, - XML_FEATURE_CONTEXT_BYTES, - XML_FEATURE_MIN_SIZE, - XML_FEATURE_SIZEOF_XML_CHAR, - XML_FEATURE_SIZEOF_XML_LCHAR, - XML_FEATURE_NS, - XML_FEATURE_LARGE_SIZE, - XML_FEATURE_ATTR_INFO - /* Additional features must be added to the end of this enum. */ -}; - -typedef struct { - enum XML_FeatureEnum feature; - const XML_LChar *name; - long int value; -} XML_Feature; - -XMLPARSEAPI(const XML_Feature *) -XML_GetFeatureList(void); - - -/* Expat follows the GNU/Linux convention of odd number minor version for - beta/development releases and even number minor version for stable - releases. Micro is bumped with each release, and set to 0 with each - change to major or minor version. -*/ -#define XML_MAJOR_VERSION 2 -#define XML_MINOR_VERSION 1 -#define XML_MICRO_VERSION 0 - -#ifdef __cplusplus -} -#endif - -#endif /* not Expat_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/expat_external.h b/apps/nesemu2/src/cartdb/expat/expat_external.h deleted file mode 100644 index a8553ffc..00000000 --- a/apps/nesemu2/src/cartdb/expat/expat_external.h +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#ifndef Expat_External_INCLUDED -#define Expat_External_INCLUDED 1 - -/* External API definitions */ - -#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) -#define XML_USE_MSC_EXTENSIONS 1 -#endif - -/* Expat tries very hard to make the API boundary very specifically - defined. There are two macros defined to control this boundary; - each of these can be defined before including this header to - achieve some different behavior, but doing so it not recommended or - tested frequently. - - XMLCALL - The calling convention to use for all calls across the - "library boundary." This will default to cdecl, and - try really hard to tell the compiler that's what we - want. - - XMLIMPORT - Whatever magic is needed to note that a function is - to be imported from a dynamically loaded library - (.dll, .so, or .sl, depending on your platform). - - The XMLCALL macro was added in Expat 1.95.7. The only one which is - expected to be directly useful in client code is XMLCALL. - - Note that on at least some Unix versions, the Expat library must be - compiled with the cdecl calling convention as the default since - system headers may assume the cdecl convention. -*/ -#ifndef XMLCALL -#if defined(_MSC_VER) -#define XMLCALL __cdecl -#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER) -#define XMLCALL __attribute__((cdecl)) -#else -/* For any platform which uses this definition and supports more than - one calling convention, we need to extend this definition to - declare the convention used on that platform, if it's possible to - do so. - - If this is the case for your platform, please file a bug report - with information on how to identify your platform via the C - pre-processor and how to specify the same calling convention as the - platform's malloc() implementation. -*/ -#define XMLCALL -#endif -#endif /* not defined XMLCALL */ - - -#if !defined(XML_STATIC) && !defined(XMLIMPORT) -#ifndef XML_BUILDING_EXPAT -/* using Expat from an application */ - -#ifdef XML_USE_MSC_EXTENSIONS -#define XMLIMPORT __declspec(dllimport) -#endif - -#endif -#endif /* not defined XML_STATIC */ - - -/* If we didn't define it above, define it away: */ -#ifndef XMLIMPORT -#define XMLIMPORT -#endif - - -#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef XML_UNICODE_WCHAR_T -#define XML_UNICODE -#endif - -#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ -#ifdef XML_UNICODE_WCHAR_T -typedef wchar_t XML_Char; -typedef wchar_t XML_LChar; -#else -typedef unsigned short XML_Char; -typedef char XML_LChar; -#endif /* XML_UNICODE_WCHAR_T */ -#else /* Information is UTF-8 encoded. */ -typedef char XML_Char; -typedef char XML_LChar; -#endif /* XML_UNICODE */ - -#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ -#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 -typedef __int64 XML_Index; -typedef unsigned __int64 XML_Size; -#else -typedef long long XML_Index; -typedef unsigned long long XML_Size; -#endif -#else -typedef long XML_Index; -typedef unsigned long XML_Size; -#endif /* XML_LARGE_SIZE */ - -#ifdef __cplusplus -} -#endif - -#endif /* not Expat_External_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/iasciitab.h b/apps/nesemu2/src/cartdb/expat/iasciitab.h deleted file mode 100644 index 24a1d5cc..00000000 --- a/apps/nesemu2/src/cartdb/expat/iasciitab.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ -/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, -/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, -/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, -/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, -/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, -/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, -/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, -/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, -/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, -/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, -/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, -/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, -/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, -/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/apps/nesemu2/src/cartdb/expat/intconfig.h b/apps/nesemu2/src/cartdb/expat/intconfig.h deleted file mode 100644 index 88092e6a..00000000 --- a/apps/nesemu2/src/cartdb/expat/intconfig.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef INTCONFIG_H -#define INTCONFIG_H - -#include -#include - -#define XML_NS 1 -#define XML_DTD 1 -#define XML_CONTEXT_BYTES 1024 - -#ifdef USE_LSB -#define BYTEORDER 1234 -#else -#define BYTEORDER 4321 -#endif - -#define HAVE_MEMMOVE - -#endif diff --git a/apps/nesemu2/src/cartdb/expat/internal.h b/apps/nesemu2/src/cartdb/expat/internal.h deleted file mode 100644 index dd545483..00000000 --- a/apps/nesemu2/src/cartdb/expat/internal.h +++ /dev/null @@ -1,73 +0,0 @@ -/* internal.h - - Internal definitions used by Expat. This is not needed to compile - client code. - - The following calling convention macros are defined for frequently - called functions: - - FASTCALL - Used for those internal functions that have a simple - body and a low number of arguments and local variables. - - PTRCALL - Used for functions called though function pointers. - - PTRFASTCALL - Like PTRCALL, but for low number of arguments. - - inline - Used for selected internal functions for which inlining - may improve performance on some platforms. - - Note: Use of these macros is based on judgement, not hard rules, - and therefore subject to change. -*/ - -#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__) -/* We'll use this version by default only where we know it helps. - - regparm() generates warnings on Solaris boxes. See SF bug #692878. - - Instability reported with egcs on a RedHat Linux 7.3. - Let's comment out: - #define FASTCALL __attribute__((stdcall, regparm(3))) - and let's try this: -*/ -#define FASTCALL __attribute__((regparm(3))) -#define PTRFASTCALL __attribute__((regparm(3))) -#endif - -/* Using __fastcall seems to have an unexpected negative effect under - MS VC++, especially for function pointers, so we won't use it for - now on that platform. It may be reconsidered for a future release - if it can be made more effective. - Likely reason: __fastcall on Windows is like stdcall, therefore - the compiler cannot perform stack optimizations for call clusters. -*/ - -/* Make sure all of these are defined if they aren't already. */ - -#ifndef FASTCALL -#define FASTCALL -#endif - -#ifndef PTRCALL -#define PTRCALL -#endif - -#ifndef PTRFASTCALL -#define PTRFASTCALL -#endif - -#ifndef XML_MIN_SIZE -#if !defined(__cplusplus) && !defined(inline) -#ifdef __GNUC__ -#define inline __inline -#endif /* __GNUC__ */ -#endif -#endif /* XML_MIN_SIZE */ - -#ifdef __cplusplus -#define inline inline -#else -#ifndef inline -#define inline -#endif -#endif diff --git a/apps/nesemu2/src/cartdb/expat/latin1tab.h b/apps/nesemu2/src/cartdb/expat/latin1tab.h deleted file mode 100644 index 53c25d76..00000000 --- a/apps/nesemu2/src/cartdb/expat/latin1tab.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, -/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, -/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, -/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, -/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, -/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, -/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/apps/nesemu2/src/cartdb/expat/nametab.h b/apps/nesemu2/src/cartdb/expat/nametab.h deleted file mode 100644 index b05e62c7..00000000 --- a/apps/nesemu2/src/cartdb/expat/nametab.h +++ /dev/null @@ -1,150 +0,0 @@ -static const unsigned namingBitmap[] = { -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, -0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, -0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, -0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, -0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, -0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, -0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, -0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, -0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, -0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, -0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, -0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, -0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, -0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, -0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, -0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, -0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, -0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, -0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, -0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, -0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, -0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, -0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, -0x40000000, 0xF580C900, 0x00000007, 0x02010800, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, -0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, -0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, -0x00000000, 0x00004C40, 0x00000000, 0x00000000, -0x00000007, 0x00000000, 0x00000000, 0x00000000, -0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, -0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, -0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, -0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, -0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, -0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, -0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, -0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, -0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, -0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, -0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, -0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, -0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, -0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, -0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, -0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, -0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, -0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, -0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, -0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, -0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, -0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, -0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, -0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, -0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, -0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, -0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, -0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x00000000, 0x00000000, -0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, -0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, -0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, -}; -static const unsigned char nmstrtPages[] = { -0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, -0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, -0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, -0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static const unsigned char namePages[] = { -0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, -0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, -0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, -0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; diff --git a/apps/nesemu2/src/cartdb/expat/utf8tab.h b/apps/nesemu2/src/cartdb/expat/utf8tab.h deleted file mode 100644 index 7bb3e776..00000000 --- a/apps/nesemu2/src/cartdb/expat/utf8tab.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - - -/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, -/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, -/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, -/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, -/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, -/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/apps/nesemu2/src/cartdb/expat/xmlparse.c b/apps/nesemu2/src/cartdb/expat/xmlparse.c deleted file mode 100644 index 7433c35e..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmlparse.c +++ /dev/null @@ -1,6393 +0,0 @@ -/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include -#include /* memset(), memcpy() */ -#include -#include /* UINT_MAX */ -#include /* time() */ - -#define XML_BUILDING_EXPAT 1 - -#include "intconfig.h" - -#include "ascii.h" -#include "expat.h" - -#ifdef XML_UNICODE -#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX -#define XmlConvert XmlUtf16Convert -#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding -#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS -#define XmlEncode XmlUtf16Encode -/* Using pointer subtraction to convert to integer type. */ -#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) -typedef unsigned short ICHAR; -#else -#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX -#define XmlConvert XmlUtf8Convert -#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding -#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS -#define XmlEncode XmlUtf8Encode -#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) -typedef char ICHAR; -#endif - - -#ifndef XML_NS - -#define XmlInitEncodingNS XmlInitEncoding -#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding -#undef XmlGetInternalEncodingNS -#define XmlGetInternalEncodingNS XmlGetInternalEncoding -#define XmlParseXmlDeclNS XmlParseXmlDecl - -#endif - -#ifdef XML_UNICODE - -#ifdef XML_UNICODE_WCHAR_T -#define XML_T(x) (const wchar_t)x -#define XML_L(x) L ## x -#else -#define XML_T(x) (const unsigned short)x -#define XML_L(x) x -#endif - -#else - -#define XML_T(x) x -#define XML_L(x) x - -#endif - -/* Round up n to be a multiple of sz, where sz is a power of 2. */ -#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) - -/* Handle the case where memmove() doesn't exist. */ -#ifndef HAVE_MEMMOVE -#ifdef HAVE_BCOPY -#define memmove(d,s,l) bcopy((s),(d),(l)) -#else -#error memmove does not exist on this platform, nor is a substitute available -#endif /* HAVE_BCOPY */ -#endif /* HAVE_MEMMOVE */ - -#include "internal.h" -#include "xmltok.h" -#include "xmlrole.h" - -typedef const XML_Char *KEY; - -typedef struct { - KEY name; -} NAMED; - -typedef struct { - NAMED **v; - unsigned char power; - size_t size; - size_t used; - const XML_Memory_Handling_Suite *mem; -} HASH_TABLE; - -/* Basic character hash algorithm, taken from Python's string hash: - h = h * 1000003 ^ character, the constant being a prime number. - -*/ -#ifdef XML_UNICODE -#define CHAR_HASH(h, c) \ - (((h) * 0xF4243) ^ (unsigned short)(c)) -#else -#define CHAR_HASH(h, c) \ - (((h) * 0xF4243) ^ (unsigned char)(c)) -#endif - -/* For probing (after a collision) we need a step size relative prime - to the hash table size, which is a power of 2. We use double-hashing, - since we can calculate a second hash value cheaply by taking those bits - of the first hash value that were discarded (masked out) when the table - index was calculated: index = hash & mask, where mask = table->size - 1. - We limit the maximum step size to table->size / 4 (mask >> 2) and make - it odd, since odd numbers are always relative prime to a power of 2. -*/ -#define SECOND_HASH(hash, mask, power) \ - ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) -#define PROBE_STEP(hash, mask, power) \ - ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) - -typedef struct { - NAMED **p; - NAMED **end; -} HASH_TABLE_ITER; - -#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ -#define INIT_DATA_BUF_SIZE 1024 -#define INIT_ATTS_SIZE 16 -#define INIT_ATTS_VERSION 0xFFFFFFFF -#define INIT_BLOCK_SIZE 1024 -#define INIT_BUFFER_SIZE 1024 - -#define EXPAND_SPARE 24 - -typedef struct binding { - struct prefix *prefix; - struct binding *nextTagBinding; - struct binding *prevPrefixBinding; - const struct attribute_id *attId; - XML_Char *uri; - int uriLen; - int uriAlloc; -} BINDING; - -typedef struct prefix { - const XML_Char *name; - BINDING *binding; -} PREFIX; - -typedef struct { - const XML_Char *str; - const XML_Char *localPart; - const XML_Char *prefix; - int strLen; - int uriLen; - int prefixLen; -} TAG_NAME; - -/* TAG represents an open element. - The name of the element is stored in both the document and API - encodings. The memory buffer 'buf' is a separately-allocated - memory area which stores the name. During the XML_Parse()/ - XMLParseBuffer() when the element is open, the memory for the 'raw' - version of the name (in the document encoding) is shared with the - document buffer. If the element is open across calls to - XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to - contain the 'raw' name as well. - - A parser re-uses these structures, maintaining a list of allocated - TAG objects in a free list. -*/ -typedef struct tag { - struct tag *parent; /* parent of this element */ - const char *rawName; /* tagName in the original encoding */ - int rawNameLength; - TAG_NAME name; /* tagName in the API encoding */ - char *buf; /* buffer for name components */ - char *bufEnd; /* end of the buffer */ - BINDING *bindings; -} TAG; - -typedef struct { - const XML_Char *name; - const XML_Char *textPtr; - int textLen; /* length in XML_Chars */ - int processed; /* # of processed bytes - when suspended */ - const XML_Char *systemId; - const XML_Char *base; - const XML_Char *publicId; - const XML_Char *notation; - XML_Bool open; - XML_Bool is_param; - XML_Bool is_internal; /* true if declared in internal subset outside PE */ -} ENTITY; - -typedef struct { - enum XML_Content_Type type; - enum XML_Content_Quant quant; - const XML_Char * name; - int firstchild; - int lastchild; - int childcnt; - int nextsib; -} CONTENT_SCAFFOLD; - -#define INIT_SCAFFOLD_ELEMENTS 32 - -typedef struct block { - struct block *next; - int size; - XML_Char s[1]; -} BLOCK; - -typedef struct { - BLOCK *blocks; - BLOCK *freeBlocks; - const XML_Char *end; - XML_Char *ptr; - XML_Char *start; - const XML_Memory_Handling_Suite *mem; -} STRING_POOL; - -/* The XML_Char before the name is used to determine whether - an attribute has been specified. */ -typedef struct attribute_id { - XML_Char *name; - PREFIX *prefix; - XML_Bool maybeTokenized; - XML_Bool xmlns; -} ATTRIBUTE_ID; - -typedef struct { - const ATTRIBUTE_ID *id; - XML_Bool isCdata; - const XML_Char *value; -} DEFAULT_ATTRIBUTE; - -typedef struct { - unsigned long version; - unsigned long hash; - const XML_Char *uriName; -} NS_ATT; - -typedef struct { - const XML_Char *name; - PREFIX *prefix; - const ATTRIBUTE_ID *idAtt; - int nDefaultAtts; - int allocDefaultAtts; - DEFAULT_ATTRIBUTE *defaultAtts; -} ELEMENT_TYPE; - -typedef struct { - HASH_TABLE generalEntities; - HASH_TABLE elementTypes; - HASH_TABLE attributeIds; - HASH_TABLE prefixes; - STRING_POOL pool; - STRING_POOL entityValuePool; - /* false once a parameter entity reference has been skipped */ - XML_Bool keepProcessing; - /* true once an internal or external PE reference has been encountered; - this includes the reference to an external subset */ - XML_Bool hasParamEntityRefs; - XML_Bool standalone; -#ifdef XML_DTD - /* indicates if external PE has been read */ - XML_Bool paramEntityRead; - HASH_TABLE paramEntities; -#endif /* XML_DTD */ - PREFIX defaultPrefix; - /* === scaffolding for building content model === */ - XML_Bool in_eldecl; - CONTENT_SCAFFOLD *scaffold; - unsigned contentStringLen; - unsigned scaffSize; - unsigned scaffCount; - int scaffLevel; - int *scaffIndex; -} DTD; - -typedef struct open_internal_entity { - const char *internalEventPtr; - const char *internalEventEndPtr; - struct open_internal_entity *next; - ENTITY *entity; - int startTagLevel; - XML_Bool betweenDecl; /* WFC: PE Between Declarations */ -} OPEN_INTERNAL_ENTITY; - -typedef enum XML_Error PTRCALL Processor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr); - -static Processor prologProcessor; -static Processor prologInitProcessor; -static Processor contentProcessor; -static Processor cdataSectionProcessor; -#ifdef XML_DTD -static Processor ignoreSectionProcessor; -static Processor externalParEntProcessor; -static Processor externalParEntInitProcessor; -static Processor entityValueProcessor; -static Processor entityValueInitProcessor; -#endif /* XML_DTD */ -static Processor epilogProcessor; -static Processor errorProcessor; -static Processor externalEntityInitProcessor; -static Processor externalEntityInitProcessor2; -static Processor externalEntityInitProcessor3; -static Processor externalEntityContentProcessor; -static Processor internalEntityProcessor; - -static enum XML_Error -handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); -static enum XML_Error -processXmlDecl(XML_Parser parser, int isGeneralTextEntity, - const char *s, const char *next); -static enum XML_Error -initializeEncoding(XML_Parser parser); -static enum XML_Error -doProlog(XML_Parser parser, const ENCODING *enc, const char *s, - const char *end, int tok, const char *next, const char **nextPtr, - XML_Bool haveMore); -static enum XML_Error -processInternalEntity(XML_Parser parser, ENTITY *entity, - XML_Bool betweenDecl); -static enum XML_Error -doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, - const char *start, const char *end, const char **endPtr, - XML_Bool haveMore); -static enum XML_Error -doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, - const char *end, const char **nextPtr, XML_Bool haveMore); -#ifdef XML_DTD -static enum XML_Error -doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, - const char *end, const char **nextPtr, XML_Bool haveMore); -#endif /* XML_DTD */ - -static enum XML_Error -storeAtts(XML_Parser parser, const ENCODING *, const char *s, - TAG_NAME *tagNamePtr, BINDING **bindingsPtr); -static enum XML_Error -addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, - const XML_Char *uri, BINDING **bindingsPtr); -static int -defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, - XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); -static enum XML_Error -storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, - const char *, const char *, STRING_POOL *); -static enum XML_Error -appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, - const char *, const char *, STRING_POOL *); -static ATTRIBUTE_ID * -getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -static int -setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); -static enum XML_Error -storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -static int -reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end); -static int -reportComment(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); -static void -reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, - const char *end); - -static const XML_Char * getContext(XML_Parser parser); -static XML_Bool -setContext(XML_Parser parser, const XML_Char *context); - -static void FASTCALL normalizePublicId(XML_Char *s); - -static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); -/* do not call if parentParser != NULL */ -static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); -static void -dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); -static int -dtdCopy(XML_Parser oldParser, - DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); -static int -copyEntityTable(XML_Parser oldParser, - HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); -static NAMED * -lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); -static void FASTCALL -hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); -static void FASTCALL hashTableClear(HASH_TABLE *); -static void FASTCALL hashTableDestroy(HASH_TABLE *); -static void FASTCALL -hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); -static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); - -static void FASTCALL -poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); -static void FASTCALL poolClear(STRING_POOL *); -static void FASTCALL poolDestroy(STRING_POOL *); -static XML_Char * -poolAppend(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end); -static XML_Char * -poolStoreString(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end); -static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); -static const XML_Char * FASTCALL -poolCopyString(STRING_POOL *pool, const XML_Char *s); -static const XML_Char * -poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); -static const XML_Char * FASTCALL -poolAppendString(STRING_POOL *pool, const XML_Char *s); - -static int FASTCALL nextScaffoldPart(XML_Parser parser); -static XML_Content * build_model(XML_Parser parser); -static ELEMENT_TYPE * -getElementType(XML_Parser parser, const ENCODING *enc, - const char *ptr, const char *end); - -static unsigned long generate_hash_secret_salt(void); -static XML_Bool startParsing(XML_Parser parser); - -static XML_Parser -parserCreate(const XML_Char *encodingName, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep, - DTD *dtd); - -static void -parserInit(XML_Parser parser, const XML_Char *encodingName); - -#define poolStart(pool) ((pool)->start) -#define poolEnd(pool) ((pool)->ptr) -#define poolLength(pool) ((pool)->ptr - (pool)->start) -#define poolChop(pool) ((void)--(pool->ptr)) -#define poolLastChar(pool) (((pool)->ptr)[-1]) -#define poolDiscard(pool) ((pool)->ptr = (pool)->start) -#define poolFinish(pool) ((pool)->start = (pool)->ptr) -#define poolAppendChar(pool, c) \ - (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ - ? 0 \ - : ((*((pool)->ptr)++ = c), 1)) - -struct XML_ParserStruct { - /* The first member must be userData so that the XML_GetUserData - macro works. */ - void *m_userData; - void *m_handlerArg; - char *m_buffer; - const XML_Memory_Handling_Suite m_mem; - /* first character to be parsed */ - const char *m_bufferPtr; - /* past last character to be parsed */ - char *m_bufferEnd; - /* allocated end of buffer */ - const char *m_bufferLim; - XML_Index m_parseEndByteIndex; - const char *m_parseEndPtr; - XML_Char *m_dataBuf; - XML_Char *m_dataBufEnd; - XML_StartElementHandler m_startElementHandler; - XML_EndElementHandler m_endElementHandler; - XML_CharacterDataHandler m_characterDataHandler; - XML_ProcessingInstructionHandler m_processingInstructionHandler; - XML_CommentHandler m_commentHandler; - XML_StartCdataSectionHandler m_startCdataSectionHandler; - XML_EndCdataSectionHandler m_endCdataSectionHandler; - XML_DefaultHandler m_defaultHandler; - XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; - XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; - XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; - XML_NotationDeclHandler m_notationDeclHandler; - XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; - XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; - XML_NotStandaloneHandler m_notStandaloneHandler; - XML_ExternalEntityRefHandler m_externalEntityRefHandler; - XML_Parser m_externalEntityRefHandlerArg; - XML_SkippedEntityHandler m_skippedEntityHandler; - XML_UnknownEncodingHandler m_unknownEncodingHandler; - XML_ElementDeclHandler m_elementDeclHandler; - XML_AttlistDeclHandler m_attlistDeclHandler; - XML_EntityDeclHandler m_entityDeclHandler; - XML_XmlDeclHandler m_xmlDeclHandler; - const ENCODING *m_encoding; - INIT_ENCODING m_initEncoding; - const ENCODING *m_internalEncoding; - const XML_Char *m_protocolEncodingName; - XML_Bool m_ns; - XML_Bool m_ns_triplets; - void *m_unknownEncodingMem; - void *m_unknownEncodingData; - void *m_unknownEncodingHandlerData; - void (XMLCALL *m_unknownEncodingRelease)(void *); - PROLOG_STATE m_prologState; - Processor *m_processor; - enum XML_Error m_errorCode; - const char *m_eventPtr; - const char *m_eventEndPtr; - const char *m_positionPtr; - OPEN_INTERNAL_ENTITY *m_openInternalEntities; - OPEN_INTERNAL_ENTITY *m_freeInternalEntities; - XML_Bool m_defaultExpandInternalEntities; - int m_tagLevel; - ENTITY *m_declEntity; - const XML_Char *m_doctypeName; - const XML_Char *m_doctypeSysid; - const XML_Char *m_doctypePubid; - const XML_Char *m_declAttributeType; - const XML_Char *m_declNotationName; - const XML_Char *m_declNotationPublicId; - ELEMENT_TYPE *m_declElementType; - ATTRIBUTE_ID *m_declAttributeId; - XML_Bool m_declAttributeIsCdata; - XML_Bool m_declAttributeIsId; - DTD *m_dtd; - const XML_Char *m_curBase; - TAG *m_tagStack; - TAG *m_freeTagList; - BINDING *m_inheritedBindings; - BINDING *m_freeBindingList; - int m_attsSize; - int m_nSpecifiedAtts; - int m_idAttIndex; - ATTRIBUTE *m_atts; - NS_ATT *m_nsAtts; - unsigned long m_nsAttsVersion; - unsigned char m_nsAttsPower; -#ifdef XML_ATTR_INFO - XML_AttrInfo *m_attInfo; -#endif - POSITION m_position; - STRING_POOL m_tempPool; - STRING_POOL m_temp2Pool; - char *m_groupConnector; - unsigned int m_groupSize; - XML_Char m_namespaceSeparator; - XML_Parser m_parentParser; - XML_ParsingStatus m_parsingStatus; -#ifdef XML_DTD - XML_Bool m_isParamEntity; - XML_Bool m_useForeignDTD; - enum XML_ParamEntityParsing m_paramEntityParsing; -#endif - unsigned long m_hash_secret_salt; -}; - -#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) -#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) -#define FREE(p) (parser->m_mem.free_fcn((p))) - -#define userData (parser->m_userData) -#define handlerArg (parser->m_handlerArg) -#define startElementHandler (parser->m_startElementHandler) -#define endElementHandler (parser->m_endElementHandler) -#define characterDataHandler (parser->m_characterDataHandler) -#define processingInstructionHandler \ - (parser->m_processingInstructionHandler) -#define commentHandler (parser->m_commentHandler) -#define startCdataSectionHandler \ - (parser->m_startCdataSectionHandler) -#define endCdataSectionHandler (parser->m_endCdataSectionHandler) -#define defaultHandler (parser->m_defaultHandler) -#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) -#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) -#define unparsedEntityDeclHandler \ - (parser->m_unparsedEntityDeclHandler) -#define notationDeclHandler (parser->m_notationDeclHandler) -#define startNamespaceDeclHandler \ - (parser->m_startNamespaceDeclHandler) -#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) -#define notStandaloneHandler (parser->m_notStandaloneHandler) -#define externalEntityRefHandler \ - (parser->m_externalEntityRefHandler) -#define externalEntityRefHandlerArg \ - (parser->m_externalEntityRefHandlerArg) -#define internalEntityRefHandler \ - (parser->m_internalEntityRefHandler) -#define skippedEntityHandler (parser->m_skippedEntityHandler) -#define unknownEncodingHandler (parser->m_unknownEncodingHandler) -#define elementDeclHandler (parser->m_elementDeclHandler) -#define attlistDeclHandler (parser->m_attlistDeclHandler) -#define entityDeclHandler (parser->m_entityDeclHandler) -#define xmlDeclHandler (parser->m_xmlDeclHandler) -#define encoding (parser->m_encoding) -#define initEncoding (parser->m_initEncoding) -#define internalEncoding (parser->m_internalEncoding) -#define unknownEncodingMem (parser->m_unknownEncodingMem) -#define unknownEncodingData (parser->m_unknownEncodingData) -#define unknownEncodingHandlerData \ - (parser->m_unknownEncodingHandlerData) -#define unknownEncodingRelease (parser->m_unknownEncodingRelease) -#define protocolEncodingName (parser->m_protocolEncodingName) -#define ns (parser->m_ns) -#define ns_triplets (parser->m_ns_triplets) -#define prologState (parser->m_prologState) -#define processor (parser->m_processor) -#define errorCode (parser->m_errorCode) -#define eventPtr (parser->m_eventPtr) -#define eventEndPtr (parser->m_eventEndPtr) -#define positionPtr (parser->m_positionPtr) -#define position (parser->m_position) -#define openInternalEntities (parser->m_openInternalEntities) -#define freeInternalEntities (parser->m_freeInternalEntities) -#define defaultExpandInternalEntities \ - (parser->m_defaultExpandInternalEntities) -#define tagLevel (parser->m_tagLevel) -#define buffer (parser->m_buffer) -#define bufferPtr (parser->m_bufferPtr) -#define bufferEnd (parser->m_bufferEnd) -#define parseEndByteIndex (parser->m_parseEndByteIndex) -#define parseEndPtr (parser->m_parseEndPtr) -#define bufferLim (parser->m_bufferLim) -#define dataBuf (parser->m_dataBuf) -#define dataBufEnd (parser->m_dataBufEnd) -#define _dtd (parser->m_dtd) -#define curBase (parser->m_curBase) -#define declEntity (parser->m_declEntity) -#define doctypeName (parser->m_doctypeName) -#define doctypeSysid (parser->m_doctypeSysid) -#define doctypePubid (parser->m_doctypePubid) -#define declAttributeType (parser->m_declAttributeType) -#define declNotationName (parser->m_declNotationName) -#define declNotationPublicId (parser->m_declNotationPublicId) -#define declElementType (parser->m_declElementType) -#define declAttributeId (parser->m_declAttributeId) -#define declAttributeIsCdata (parser->m_declAttributeIsCdata) -#define declAttributeIsId (parser->m_declAttributeIsId) -#define freeTagList (parser->m_freeTagList) -#define freeBindingList (parser->m_freeBindingList) -#define inheritedBindings (parser->m_inheritedBindings) -#define tagStack (parser->m_tagStack) -#define atts (parser->m_atts) -#define attsSize (parser->m_attsSize) -#define nSpecifiedAtts (parser->m_nSpecifiedAtts) -#define idAttIndex (parser->m_idAttIndex) -#define nsAtts (parser->m_nsAtts) -#define nsAttsVersion (parser->m_nsAttsVersion) -#define nsAttsPower (parser->m_nsAttsPower) -#define attInfo (parser->m_attInfo) -#define tempPool (parser->m_tempPool) -#define temp2Pool (parser->m_temp2Pool) -#define groupConnector (parser->m_groupConnector) -#define groupSize (parser->m_groupSize) -#define namespaceSeparator (parser->m_namespaceSeparator) -#define parentParser (parser->m_parentParser) -#define ps_parsing (parser->m_parsingStatus.parsing) -#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) -#ifdef XML_DTD -#define isParamEntity (parser->m_isParamEntity) -#define useForeignDTD (parser->m_useForeignDTD) -#define paramEntityParsing (parser->m_paramEntityParsing) -#endif /* XML_DTD */ -#define hash_secret_salt (parser->m_hash_secret_salt) - -XML_Parser XMLCALL -XML_ParserCreate(const XML_Char *encodingName) -{ - return XML_ParserCreate_MM(encodingName, NULL, NULL); -} - -XML_Parser XMLCALL -XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) -{ - XML_Char tmp[2]; - *tmp = nsSep; - return XML_ParserCreate_MM(encodingName, NULL, tmp); -} - -static const XML_Char implicitContext[] = { - ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, - ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, - ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, - ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, - ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, - ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' -}; - -static unsigned long -generate_hash_secret_salt(void) -{ - unsigned int seed = (unsigned int)(time(NULL) % UINT_MAX); - srand(seed); - return rand(); -} - -static XML_Bool /* only valid for root parser */ -startParsing(XML_Parser parser) -{ - /* hash functions must be initialized before setContext() is called */ - if (hash_secret_salt == 0) - hash_secret_salt = generate_hash_secret_salt(); - if (ns) { - /* implicit context only set for root parser, since child - parsers (i.e. external entity parsers) will inherit it - */ - return setContext(parser, implicitContext); - } - return XML_TRUE; - } - -XML_Parser XMLCALL -XML_ParserCreate_MM(const XML_Char *encodingName, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep) -{ - return parserCreate(encodingName, memsuite, nameSep, NULL); -} - -static XML_Parser -parserCreate(const XML_Char *encodingName, - const XML_Memory_Handling_Suite *memsuite, - const XML_Char *nameSep, - DTD *dtd) -{ - XML_Parser parser; - - if (memsuite) { - XML_Memory_Handling_Suite *mtemp; - parser = (XML_Parser) - memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); - if (parser != NULL) { - mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); - mtemp->malloc_fcn = memsuite->malloc_fcn; - mtemp->realloc_fcn = memsuite->realloc_fcn; - mtemp->free_fcn = memsuite->free_fcn; - } - } - else { - XML_Memory_Handling_Suite *mtemp; - parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); - if (parser != NULL) { - mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); - mtemp->malloc_fcn = malloc; - mtemp->realloc_fcn = realloc; - mtemp->free_fcn = free; - } - } - - if (!parser) - return parser; - - buffer = NULL; - bufferLim = NULL; - - attsSize = INIT_ATTS_SIZE; - atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); - if (atts == NULL) { - FREE(parser); - return NULL; - } -#ifdef XML_ATTR_INFO - attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo)); - if (attInfo == NULL) { - FREE(atts); - FREE(parser); - return NULL; - } -#endif - dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); - if (dataBuf == NULL) { - FREE(atts); -#ifdef XML_ATTR_INFO - FREE(attInfo); -#endif - FREE(parser); - return NULL; - } - dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; - - if (dtd) - _dtd = dtd; - else { - _dtd = dtdCreate(&parser->m_mem); - if (_dtd == NULL) { - FREE(dataBuf); - FREE(atts); -#ifdef XML_ATTR_INFO - FREE(attInfo); -#endif - FREE(parser); - return NULL; - } - } - - freeBindingList = NULL; - freeTagList = NULL; - freeInternalEntities = NULL; - - groupSize = 0; - groupConnector = NULL; - - unknownEncodingHandler = NULL; - unknownEncodingHandlerData = NULL; - - namespaceSeparator = ASCII_EXCL; - ns = XML_FALSE; - ns_triplets = XML_FALSE; - - nsAtts = NULL; - nsAttsVersion = 0; - nsAttsPower = 0; - - poolInit(&tempPool, &(parser->m_mem)); - poolInit(&temp2Pool, &(parser->m_mem)); - parserInit(parser, encodingName); - - if (encodingName && !protocolEncodingName) { - XML_ParserFree(parser); - return NULL; - } - - if (nameSep) { - ns = XML_TRUE; - internalEncoding = XmlGetInternalEncodingNS(); - namespaceSeparator = *nameSep; - } - else { - internalEncoding = XmlGetInternalEncoding(); - } - - return parser; -} - -static void -parserInit(XML_Parser parser, const XML_Char *encodingName) -{ - processor = prologInitProcessor; - XmlPrologStateInit(&prologState); - protocolEncodingName = (encodingName != NULL - ? poolCopyString(&tempPool, encodingName) - : NULL); - curBase = NULL; - XmlInitEncoding(&initEncoding, &encoding, 0); - userData = NULL; - handlerArg = NULL; - startElementHandler = NULL; - endElementHandler = NULL; - characterDataHandler = NULL; - processingInstructionHandler = NULL; - commentHandler = NULL; - startCdataSectionHandler = NULL; - endCdataSectionHandler = NULL; - defaultHandler = NULL; - startDoctypeDeclHandler = NULL; - endDoctypeDeclHandler = NULL; - unparsedEntityDeclHandler = NULL; - notationDeclHandler = NULL; - startNamespaceDeclHandler = NULL; - endNamespaceDeclHandler = NULL; - notStandaloneHandler = NULL; - externalEntityRefHandler = NULL; - externalEntityRefHandlerArg = parser; - skippedEntityHandler = NULL; - elementDeclHandler = NULL; - attlistDeclHandler = NULL; - entityDeclHandler = NULL; - xmlDeclHandler = NULL; - bufferPtr = buffer; - bufferEnd = buffer; - parseEndByteIndex = 0; - parseEndPtr = NULL; - declElementType = NULL; - declAttributeId = NULL; - declEntity = NULL; - doctypeName = NULL; - doctypeSysid = NULL; - doctypePubid = NULL; - declAttributeType = NULL; - declNotationName = NULL; - declNotationPublicId = NULL; - declAttributeIsCdata = XML_FALSE; - declAttributeIsId = XML_FALSE; - memset(&position, 0, sizeof(POSITION)); - errorCode = XML_ERROR_NONE; - eventPtr = NULL; - eventEndPtr = NULL; - positionPtr = NULL; - openInternalEntities = NULL; - defaultExpandInternalEntities = XML_TRUE; - tagLevel = 0; - tagStack = NULL; - inheritedBindings = NULL; - nSpecifiedAtts = 0; - unknownEncodingMem = NULL; - unknownEncodingRelease = NULL; - unknownEncodingData = NULL; - parentParser = NULL; - ps_parsing = XML_INITIALIZED; -#ifdef XML_DTD - isParamEntity = XML_FALSE; - useForeignDTD = XML_FALSE; - paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; -#endif - hash_secret_salt = 0; -} - -/* moves list of bindings to freeBindingList */ -static void FASTCALL -moveToFreeBindingList(XML_Parser parser, BINDING *bindings) -{ - while (bindings) { - BINDING *b = bindings; - bindings = bindings->nextTagBinding; - b->nextTagBinding = freeBindingList; - freeBindingList = b; - } -} - -XML_Bool XMLCALL -XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) -{ - TAG *tStk; - OPEN_INTERNAL_ENTITY *openEntityList; - if (parentParser) - return XML_FALSE; - /* move tagStack to freeTagList */ - tStk = tagStack; - while (tStk) { - TAG *tag = tStk; - tStk = tStk->parent; - tag->parent = freeTagList; - moveToFreeBindingList(parser, tag->bindings); - tag->bindings = NULL; - freeTagList = tag; - } - /* move openInternalEntities to freeInternalEntities */ - openEntityList = openInternalEntities; - while (openEntityList) { - OPEN_INTERNAL_ENTITY *openEntity = openEntityList; - openEntityList = openEntity->next; - openEntity->next = freeInternalEntities; - freeInternalEntities = openEntity; - } - moveToFreeBindingList(parser, inheritedBindings); - FREE(unknownEncodingMem); - if (unknownEncodingRelease) - unknownEncodingRelease(unknownEncodingData); - poolClear(&tempPool); - poolClear(&temp2Pool); - parserInit(parser, encodingName); - dtdReset(_dtd, &parser->m_mem); - return XML_TRUE; -} - -enum XML_Status XMLCALL -XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) -{ - /* Block after XML_Parse()/XML_ParseBuffer() has been called. - XXX There's no way for the caller to determine which of the - XXX possible error cases caused the XML_STATUS_ERROR return. - */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) - return XML_STATUS_ERROR; - if (encodingName == NULL) - protocolEncodingName = NULL; - else { - protocolEncodingName = poolCopyString(&tempPool, encodingName); - if (!protocolEncodingName) - return XML_STATUS_ERROR; - } - return XML_STATUS_OK; -} - -XML_Parser XMLCALL -XML_ExternalEntityParserCreate(XML_Parser oldParser, - const XML_Char *context, - const XML_Char *encodingName) -{ - XML_Parser parser = oldParser; - DTD *newDtd = NULL; - DTD *oldDtd = _dtd; - XML_StartElementHandler oldStartElementHandler = startElementHandler; - XML_EndElementHandler oldEndElementHandler = endElementHandler; - XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; - XML_ProcessingInstructionHandler oldProcessingInstructionHandler - = processingInstructionHandler; - XML_CommentHandler oldCommentHandler = commentHandler; - XML_StartCdataSectionHandler oldStartCdataSectionHandler - = startCdataSectionHandler; - XML_EndCdataSectionHandler oldEndCdataSectionHandler - = endCdataSectionHandler; - XML_DefaultHandler oldDefaultHandler = defaultHandler; - XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler - = unparsedEntityDeclHandler; - XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; - XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler - = startNamespaceDeclHandler; - XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler - = endNamespaceDeclHandler; - XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; - XML_ExternalEntityRefHandler oldExternalEntityRefHandler - = externalEntityRefHandler; - XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; - XML_UnknownEncodingHandler oldUnknownEncodingHandler - = unknownEncodingHandler; - XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; - XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; - XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; - XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; - ELEMENT_TYPE * oldDeclElementType = declElementType; - - void *oldUserData = userData; - void *oldHandlerArg = handlerArg; - XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; - XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; -#ifdef XML_DTD - enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; - int oldInEntityValue = prologState.inEntityValue; -#endif - XML_Bool oldns_triplets = ns_triplets; - /* Note that the new parser shares the same hash secret as the old - parser, so that dtdCopy and copyEntityTable can lookup values - from hash tables associated with either parser without us having - to worry which hash secrets each table has. - */ - unsigned long oldhash_secret_salt = hash_secret_salt; - -#ifdef XML_DTD - if (!context) - newDtd = oldDtd; -#endif /* XML_DTD */ - - /* Note that the magical uses of the pre-processor to make field - access look more like C++ require that `parser' be overwritten - here. This makes this function more painful to follow than it - would be otherwise. - */ - if (ns) { - XML_Char tmp[2]; - *tmp = namespaceSeparator; - parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); - } - else { - parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); - } - - if (!parser) - return NULL; - - startElementHandler = oldStartElementHandler; - endElementHandler = oldEndElementHandler; - characterDataHandler = oldCharacterDataHandler; - processingInstructionHandler = oldProcessingInstructionHandler; - commentHandler = oldCommentHandler; - startCdataSectionHandler = oldStartCdataSectionHandler; - endCdataSectionHandler = oldEndCdataSectionHandler; - defaultHandler = oldDefaultHandler; - unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; - notationDeclHandler = oldNotationDeclHandler; - startNamespaceDeclHandler = oldStartNamespaceDeclHandler; - endNamespaceDeclHandler = oldEndNamespaceDeclHandler; - notStandaloneHandler = oldNotStandaloneHandler; - externalEntityRefHandler = oldExternalEntityRefHandler; - skippedEntityHandler = oldSkippedEntityHandler; - unknownEncodingHandler = oldUnknownEncodingHandler; - elementDeclHandler = oldElementDeclHandler; - attlistDeclHandler = oldAttlistDeclHandler; - entityDeclHandler = oldEntityDeclHandler; - xmlDeclHandler = oldXmlDeclHandler; - declElementType = oldDeclElementType; - userData = oldUserData; - if (oldUserData == oldHandlerArg) - handlerArg = userData; - else - handlerArg = parser; - if (oldExternalEntityRefHandlerArg != oldParser) - externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; - defaultExpandInternalEntities = oldDefaultExpandInternalEntities; - ns_triplets = oldns_triplets; - hash_secret_salt = oldhash_secret_salt; - parentParser = oldParser; -#ifdef XML_DTD - paramEntityParsing = oldParamEntityParsing; - prologState.inEntityValue = oldInEntityValue; - if (context) { -#endif /* XML_DTD */ - if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem) - || !setContext(parser, context)) { - XML_ParserFree(parser); - return NULL; - } - processor = externalEntityInitProcessor; -#ifdef XML_DTD - } - else { - /* The DTD instance referenced by _dtd is shared between the document's - root parser and external PE parsers, therefore one does not need to - call setContext. In addition, one also *must* not call setContext, - because this would overwrite existing prefix->binding pointers in - _dtd with ones that get destroyed with the external PE parser. - This would leave those prefixes with dangling pointers. - */ - isParamEntity = XML_TRUE; - XmlPrologStateInitExternalEntity(&prologState); - processor = externalParEntInitProcessor; - } -#endif /* XML_DTD */ - return parser; -} - -static void FASTCALL -destroyBindings(BINDING *bindings, XML_Parser parser) -{ - for (;;) { - BINDING *b = bindings; - if (!b) - break; - bindings = b->nextTagBinding; - FREE(b->uri); - FREE(b); - } -} - -void XMLCALL -XML_ParserFree(XML_Parser parser) -{ - TAG *tagList; - OPEN_INTERNAL_ENTITY *entityList; - if (parser == NULL) - return; - /* free tagStack and freeTagList */ - tagList = tagStack; - for (;;) { - TAG *p; - if (tagList == NULL) { - if (freeTagList == NULL) - break; - tagList = freeTagList; - freeTagList = NULL; - } - p = tagList; - tagList = tagList->parent; - FREE(p->buf); - destroyBindings(p->bindings, parser); - FREE(p); - } - /* free openInternalEntities and freeInternalEntities */ - entityList = openInternalEntities; - for (;;) { - OPEN_INTERNAL_ENTITY *openEntity; - if (entityList == NULL) { - if (freeInternalEntities == NULL) - break; - entityList = freeInternalEntities; - freeInternalEntities = NULL; - } - openEntity = entityList; - entityList = entityList->next; - FREE(openEntity); - } - - destroyBindings(freeBindingList, parser); - destroyBindings(inheritedBindings, parser); - poolDestroy(&tempPool); - poolDestroy(&temp2Pool); -#ifdef XML_DTD - /* external parameter entity parsers share the DTD structure - parser->m_dtd with the root parser, so we must not destroy it - */ - if (!isParamEntity && _dtd) -#else - if (_dtd) -#endif /* XML_DTD */ - dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); - FREE((void *)atts); -#ifdef XML_ATTR_INFO - FREE((void *)attInfo); -#endif - FREE(groupConnector); - FREE(buffer); - FREE(dataBuf); - FREE(nsAtts); - FREE(unknownEncodingMem); - if (unknownEncodingRelease) - unknownEncodingRelease(unknownEncodingData); - FREE(parser); -} - -void XMLCALL -XML_UseParserAsHandlerArg(XML_Parser parser) -{ - handlerArg = parser; -} - -enum XML_Error XMLCALL -XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) -{ -#ifdef XML_DTD - /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) - return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; - useForeignDTD = useDTD; - return XML_ERROR_NONE; -#else - return XML_ERROR_FEATURE_REQUIRES_XML_DTD; -#endif -} - -void XMLCALL -XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) -{ - /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) - return; - ns_triplets = do_nst ? XML_TRUE : XML_FALSE; -} - -void XMLCALL -XML_SetUserData(XML_Parser parser, void *p) -{ - if (handlerArg == userData) - handlerArg = userData = p; - else - userData = p; -} - -enum XML_Status XMLCALL -XML_SetBase(XML_Parser parser, const XML_Char *p) -{ - if (p) { - p = poolCopyString(&_dtd->pool, p); - if (!p) - return XML_STATUS_ERROR; - curBase = p; - } - else - curBase = NULL; - return XML_STATUS_OK; -} - -const XML_Char * XMLCALL -XML_GetBase(XML_Parser parser) -{ - return curBase; -} - -int XMLCALL -XML_GetSpecifiedAttributeCount(XML_Parser parser) -{ - return nSpecifiedAtts; -} - -int XMLCALL -XML_GetIdAttributeIndex(XML_Parser parser) -{ - return idAttIndex; -} - -#ifdef XML_ATTR_INFO -const XML_AttrInfo * XMLCALL -XML_GetAttributeInfo(XML_Parser parser) -{ - return attInfo; -} -#endif - -void XMLCALL -XML_SetElementHandler(XML_Parser parser, - XML_StartElementHandler start, - XML_EndElementHandler end) -{ - startElementHandler = start; - endElementHandler = end; -} - -void XMLCALL -XML_SetStartElementHandler(XML_Parser parser, - XML_StartElementHandler start) { - startElementHandler = start; -} - -void XMLCALL -XML_SetEndElementHandler(XML_Parser parser, - XML_EndElementHandler end) { - endElementHandler = end; -} - -void XMLCALL -XML_SetCharacterDataHandler(XML_Parser parser, - XML_CharacterDataHandler handler) -{ - characterDataHandler = handler; -} - -void XMLCALL -XML_SetProcessingInstructionHandler(XML_Parser parser, - XML_ProcessingInstructionHandler handler) -{ - processingInstructionHandler = handler; -} - -void XMLCALL -XML_SetCommentHandler(XML_Parser parser, - XML_CommentHandler handler) -{ - commentHandler = handler; -} - -void XMLCALL -XML_SetCdataSectionHandler(XML_Parser parser, - XML_StartCdataSectionHandler start, - XML_EndCdataSectionHandler end) -{ - startCdataSectionHandler = start; - endCdataSectionHandler = end; -} - -void XMLCALL -XML_SetStartCdataSectionHandler(XML_Parser parser, - XML_StartCdataSectionHandler start) { - startCdataSectionHandler = start; -} - -void XMLCALL -XML_SetEndCdataSectionHandler(XML_Parser parser, - XML_EndCdataSectionHandler end) { - endCdataSectionHandler = end; -} - -void XMLCALL -XML_SetDefaultHandler(XML_Parser parser, - XML_DefaultHandler handler) -{ - defaultHandler = handler; - defaultExpandInternalEntities = XML_FALSE; -} - -void XMLCALL -XML_SetDefaultHandlerExpand(XML_Parser parser, - XML_DefaultHandler handler) -{ - defaultHandler = handler; - defaultExpandInternalEntities = XML_TRUE; -} - -void XMLCALL -XML_SetDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start, - XML_EndDoctypeDeclHandler end) -{ - startDoctypeDeclHandler = start; - endDoctypeDeclHandler = end; -} - -void XMLCALL -XML_SetStartDoctypeDeclHandler(XML_Parser parser, - XML_StartDoctypeDeclHandler start) { - startDoctypeDeclHandler = start; -} - -void XMLCALL -XML_SetEndDoctypeDeclHandler(XML_Parser parser, - XML_EndDoctypeDeclHandler end) { - endDoctypeDeclHandler = end; -} - -void XMLCALL -XML_SetUnparsedEntityDeclHandler(XML_Parser parser, - XML_UnparsedEntityDeclHandler handler) -{ - unparsedEntityDeclHandler = handler; -} - -void XMLCALL -XML_SetNotationDeclHandler(XML_Parser parser, - XML_NotationDeclHandler handler) -{ - notationDeclHandler = handler; -} - -void XMLCALL -XML_SetNamespaceDeclHandler(XML_Parser parser, - XML_StartNamespaceDeclHandler start, - XML_EndNamespaceDeclHandler end) -{ - startNamespaceDeclHandler = start; - endNamespaceDeclHandler = end; -} - -void XMLCALL -XML_SetStartNamespaceDeclHandler(XML_Parser parser, - XML_StartNamespaceDeclHandler start) { - startNamespaceDeclHandler = start; -} - -void XMLCALL -XML_SetEndNamespaceDeclHandler(XML_Parser parser, - XML_EndNamespaceDeclHandler end) { - endNamespaceDeclHandler = end; -} - -void XMLCALL -XML_SetNotStandaloneHandler(XML_Parser parser, - XML_NotStandaloneHandler handler) -{ - notStandaloneHandler = handler; -} - -void XMLCALL -XML_SetExternalEntityRefHandler(XML_Parser parser, - XML_ExternalEntityRefHandler handler) -{ - externalEntityRefHandler = handler; -} - -void XMLCALL -XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) -{ - if (arg) - externalEntityRefHandlerArg = (XML_Parser)arg; - else - externalEntityRefHandlerArg = parser; -} - -void XMLCALL -XML_SetSkippedEntityHandler(XML_Parser parser, - XML_SkippedEntityHandler handler) -{ - skippedEntityHandler = handler; -} - -void XMLCALL -XML_SetUnknownEncodingHandler(XML_Parser parser, - XML_UnknownEncodingHandler handler, - void *data) -{ - unknownEncodingHandler = handler; - unknownEncodingHandlerData = data; -} - -void XMLCALL -XML_SetElementDeclHandler(XML_Parser parser, - XML_ElementDeclHandler eldecl) -{ - elementDeclHandler = eldecl; -} - -void XMLCALL -XML_SetAttlistDeclHandler(XML_Parser parser, - XML_AttlistDeclHandler attdecl) -{ - attlistDeclHandler = attdecl; -} - -void XMLCALL -XML_SetEntityDeclHandler(XML_Parser parser, - XML_EntityDeclHandler handler) -{ - entityDeclHandler = handler; -} - -void XMLCALL -XML_SetXmlDeclHandler(XML_Parser parser, - XML_XmlDeclHandler handler) { - xmlDeclHandler = handler; -} - -int XMLCALL -XML_SetParamEntityParsing(XML_Parser parser, - enum XML_ParamEntityParsing peParsing) -{ - /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) - return 0; -#ifdef XML_DTD - paramEntityParsing = peParsing; - return 1; -#else - return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; -#endif -} - -int XMLCALL -XML_SetHashSalt(XML_Parser parser, - unsigned long hash_salt) -{ - /* block after XML_Parse()/XML_ParseBuffer() has been called */ - if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) - return 0; - hash_secret_salt = hash_salt; - return 1; -} - -enum XML_Status XMLCALL -XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) -{ - switch (ps_parsing) { - case XML_SUSPENDED: - errorCode = XML_ERROR_SUSPENDED; - return XML_STATUS_ERROR; - case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; - return XML_STATUS_ERROR; - case XML_INITIALIZED: - if (parentParser == NULL && !startParsing(parser)) { - errorCode = XML_ERROR_NO_MEMORY; - return XML_STATUS_ERROR; - } - default: - ps_parsing = XML_PARSING; - } - - if (len == 0) { - ps_finalBuffer = (XML_Bool)isFinal; - if (!isFinal) - return XML_STATUS_OK; - positionPtr = bufferPtr; - parseEndPtr = bufferEnd; - - /* If data are left over from last buffer, and we now know that these - data are the final chunk of input, then we have to check them again - to detect errors based on that fact. - */ - errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); - - if (errorCode == XML_ERROR_NONE) { - switch (ps_parsing) { - case XML_SUSPENDED: - XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); - positionPtr = bufferPtr; - return XML_STATUS_SUSPENDED; - case XML_INITIALIZED: - case XML_PARSING: - ps_parsing = XML_FINISHED; - /* fall through */ - default: - return XML_STATUS_OK; - } - } - eventEndPtr = eventPtr; - processor = errorProcessor; - return XML_STATUS_ERROR; - } -#ifndef XML_CONTEXT_BYTES - else if (bufferPtr == bufferEnd) { - const char *end; - int nLeftOver; - enum XML_Error result; - parseEndByteIndex += len; - positionPtr = s; - ps_finalBuffer = (XML_Bool)isFinal; - - errorCode = processor(parser, s, parseEndPtr = s + len, &end); - - if (errorCode != XML_ERROR_NONE) { - eventEndPtr = eventPtr; - processor = errorProcessor; - return XML_STATUS_ERROR; - } - else { - switch (ps_parsing) { - case XML_SUSPENDED: - result = XML_STATUS_SUSPENDED; - break; - case XML_INITIALIZED: - case XML_PARSING: - if (isFinal) { - ps_parsing = XML_FINISHED; - return XML_STATUS_OK; - } - /* fall through */ - default: - result = XML_STATUS_OK; - } - } - - XmlUpdatePosition(encoding, positionPtr, end, &position); - nLeftOver = s + len - end; - if (nLeftOver) { - if (buffer == NULL || nLeftOver > bufferLim - buffer) { - /* FIXME avoid integer overflow */ - char *temp; - temp = (buffer == NULL - ? (char *)MALLOC(len * 2) - : (char *)REALLOC(buffer, len * 2)); - if (temp == NULL) { - errorCode = XML_ERROR_NO_MEMORY; - eventPtr = eventEndPtr = NULL; - processor = errorProcessor; - return XML_STATUS_ERROR; - } - buffer = temp; - bufferLim = buffer + len * 2; - } - memcpy(buffer, end, nLeftOver); - } - bufferPtr = buffer; - bufferEnd = buffer + nLeftOver; - positionPtr = bufferPtr; - parseEndPtr = bufferEnd; - eventPtr = bufferPtr; - eventEndPtr = bufferPtr; - return result; - } -#endif /* not defined XML_CONTEXT_BYTES */ - else { - void *buff = XML_GetBuffer(parser, len); - if (buff == NULL) - return XML_STATUS_ERROR; - else { - memcpy(buff, s, len); - return XML_ParseBuffer(parser, len, isFinal); - } - } -} - -enum XML_Status XMLCALL -XML_ParseBuffer(XML_Parser parser, int len, int isFinal) -{ - const char *start; - enum XML_Status result = XML_STATUS_OK; - - switch (ps_parsing) { - case XML_SUSPENDED: - errorCode = XML_ERROR_SUSPENDED; - return XML_STATUS_ERROR; - case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; - return XML_STATUS_ERROR; - case XML_INITIALIZED: - if (parentParser == NULL && !startParsing(parser)) { - errorCode = XML_ERROR_NO_MEMORY; - return XML_STATUS_ERROR; - } - default: - ps_parsing = XML_PARSING; - } - - start = bufferPtr; - positionPtr = start; - bufferEnd += len; - parseEndPtr = bufferEnd; - parseEndByteIndex += len; - ps_finalBuffer = (XML_Bool)isFinal; - - errorCode = processor(parser, start, parseEndPtr, &bufferPtr); - - if (errorCode != XML_ERROR_NONE) { - eventEndPtr = eventPtr; - processor = errorProcessor; - return XML_STATUS_ERROR; - } - else { - switch (ps_parsing) { - case XML_SUSPENDED: - result = XML_STATUS_SUSPENDED; - break; - case XML_INITIALIZED: - case XML_PARSING: - if (isFinal) { - ps_parsing = XML_FINISHED; - return result; - } - default: ; /* should not happen */ - } - } - - XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); - positionPtr = bufferPtr; - return result; -} - -void * XMLCALL -XML_GetBuffer(XML_Parser parser, int len) -{ - switch (ps_parsing) { - case XML_SUSPENDED: - errorCode = XML_ERROR_SUSPENDED; - return NULL; - case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; - return NULL; - default: ; - } - - if (len > bufferLim - bufferEnd) { - /* FIXME avoid integer overflow */ - int neededSize = len + (int)(bufferEnd - bufferPtr); -#ifdef XML_CONTEXT_BYTES - int keep = (int)(bufferPtr - buffer); - - if (keep > XML_CONTEXT_BYTES) - keep = XML_CONTEXT_BYTES; - neededSize += keep; -#endif /* defined XML_CONTEXT_BYTES */ - if (neededSize <= bufferLim - buffer) { -#ifdef XML_CONTEXT_BYTES - if (keep < bufferPtr - buffer) { - int offset = (int)(bufferPtr - buffer) - keep; - memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); - bufferEnd -= offset; - bufferPtr -= offset; - } -#else - memmove(buffer, bufferPtr, bufferEnd - bufferPtr); - bufferEnd = buffer + (bufferEnd - bufferPtr); - bufferPtr = buffer; -#endif /* not defined XML_CONTEXT_BYTES */ - } - else { - char *newBuf; - int bufferSize = (int)(bufferLim - bufferPtr); - if (bufferSize == 0) - bufferSize = INIT_BUFFER_SIZE; - do { - bufferSize *= 2; - } while (bufferSize < neededSize); - newBuf = (char *)MALLOC(bufferSize); - if (newBuf == 0) { - errorCode = XML_ERROR_NO_MEMORY; - return NULL; - } - bufferLim = newBuf + bufferSize; -#ifdef XML_CONTEXT_BYTES - if (bufferPtr) { - int keep = (int)(bufferPtr - buffer); - if (keep > XML_CONTEXT_BYTES) - keep = XML_CONTEXT_BYTES; - memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); - FREE(buffer); - buffer = newBuf; - bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; - bufferPtr = buffer + keep; - } - else { - bufferEnd = newBuf + (bufferEnd - bufferPtr); - bufferPtr = buffer = newBuf; - } -#else - if (bufferPtr) { - memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); - FREE(buffer); - } - bufferEnd = newBuf + (bufferEnd - bufferPtr); - bufferPtr = buffer = newBuf; -#endif /* not defined XML_CONTEXT_BYTES */ - } - eventPtr = eventEndPtr = NULL; - positionPtr = NULL; - } - return bufferEnd; -} - -enum XML_Status XMLCALL -XML_StopParser(XML_Parser parser, XML_Bool resumable) -{ - switch (ps_parsing) { - case XML_SUSPENDED: - if (resumable) { - errorCode = XML_ERROR_SUSPENDED; - return XML_STATUS_ERROR; - } - ps_parsing = XML_FINISHED; - break; - case XML_FINISHED: - errorCode = XML_ERROR_FINISHED; - return XML_STATUS_ERROR; - default: - if (resumable) { -#ifdef XML_DTD - if (isParamEntity) { - errorCode = XML_ERROR_SUSPEND_PE; - return XML_STATUS_ERROR; - } -#endif - ps_parsing = XML_SUSPENDED; - } - else - ps_parsing = XML_FINISHED; - } - return XML_STATUS_OK; -} - -enum XML_Status XMLCALL -XML_ResumeParser(XML_Parser parser) -{ - enum XML_Status result = XML_STATUS_OK; - - if (ps_parsing != XML_SUSPENDED) { - errorCode = XML_ERROR_NOT_SUSPENDED; - return XML_STATUS_ERROR; - } - ps_parsing = XML_PARSING; - - errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); - - if (errorCode != XML_ERROR_NONE) { - eventEndPtr = eventPtr; - processor = errorProcessor; - return XML_STATUS_ERROR; - } - else { - switch (ps_parsing) { - case XML_SUSPENDED: - result = XML_STATUS_SUSPENDED; - break; - case XML_INITIALIZED: - case XML_PARSING: - if (ps_finalBuffer) { - ps_parsing = XML_FINISHED; - return result; - } - default: ; - } - } - - XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); - positionPtr = bufferPtr; - return result; -} - -void XMLCALL -XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) -{ - assert(status != NULL); - *status = parser->m_parsingStatus; -} - -enum XML_Error XMLCALL -XML_GetErrorCode(XML_Parser parser) -{ - return errorCode; -} - -XML_Index XMLCALL -XML_GetCurrentByteIndex(XML_Parser parser) -{ - if (eventPtr) - return parseEndByteIndex - (parseEndPtr - eventPtr); - return -1; -} - -int XMLCALL -XML_GetCurrentByteCount(XML_Parser parser) -{ - if (eventEndPtr && eventPtr) - return (int)(eventEndPtr - eventPtr); - return 0; -} - -const char * XMLCALL -XML_GetInputContext(XML_Parser parser, int *offset, int *size) -{ -#ifdef XML_CONTEXT_BYTES - if (eventPtr && buffer) { - *offset = (int)(eventPtr - buffer); - *size = (int)(bufferEnd - buffer); - return buffer; - } -#endif /* defined XML_CONTEXT_BYTES */ - return (char *) 0; -} - -XML_Size XMLCALL -XML_GetCurrentLineNumber(XML_Parser parser) -{ - if (eventPtr && eventPtr >= positionPtr) { - XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); - positionPtr = eventPtr; - } - return position.lineNumber + 1; -} - -XML_Size XMLCALL -XML_GetCurrentColumnNumber(XML_Parser parser) -{ - if (eventPtr && eventPtr >= positionPtr) { - XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); - positionPtr = eventPtr; - } - return position.columnNumber; -} - -void XMLCALL -XML_FreeContentModel(XML_Parser parser, XML_Content *model) -{ - FREE(model); -} - -void * XMLCALL -XML_MemMalloc(XML_Parser parser, size_t size) -{ - return MALLOC(size); -} - -void * XMLCALL -XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) -{ - return REALLOC(ptr, size); -} - -void XMLCALL -XML_MemFree(XML_Parser parser, void *ptr) -{ - FREE(ptr); -} - -void XMLCALL -XML_DefaultCurrent(XML_Parser parser) -{ - if (defaultHandler) { - if (openInternalEntities) - reportDefault(parser, - internalEncoding, - openInternalEntities->internalEventPtr, - openInternalEntities->internalEventEndPtr); - else - reportDefault(parser, encoding, eventPtr, eventEndPtr); - } -} - -const XML_LChar * XMLCALL -XML_ErrorString(enum XML_Error code) -{ - static const XML_LChar* const message[] = { - 0, - XML_L("out of memory"), - XML_L("syntax error"), - XML_L("no element found"), - XML_L("not well-formed (invalid token)"), - XML_L("unclosed token"), - XML_L("partial character"), - XML_L("mismatched tag"), - XML_L("duplicate attribute"), - XML_L("junk after document element"), - XML_L("illegal parameter entity reference"), - XML_L("undefined entity"), - XML_L("recursive entity reference"), - XML_L("asynchronous entity"), - XML_L("reference to invalid character number"), - XML_L("reference to binary entity"), - XML_L("reference to external entity in attribute"), - XML_L("XML or text declaration not at start of entity"), - XML_L("unknown encoding"), - XML_L("encoding specified in XML declaration is incorrect"), - XML_L("unclosed CDATA section"), - XML_L("error in processing external entity reference"), - XML_L("document is not standalone"), - XML_L("unexpected parser state - please send a bug report"), - XML_L("entity declared in parameter entity"), - XML_L("requested feature requires XML_DTD support in Expat"), - XML_L("cannot change setting once parsing has begun"), - XML_L("unbound prefix"), - XML_L("must not undeclare prefix"), - XML_L("incomplete markup in parameter entity"), - XML_L("XML declaration not well-formed"), - XML_L("text declaration not well-formed"), - XML_L("illegal character(s) in public id"), - XML_L("parser suspended"), - XML_L("parser not suspended"), - XML_L("parsing aborted"), - XML_L("parsing finished"), - XML_L("cannot suspend in external parameter entity"), - XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), - XML_L("reserved prefix (xmlns) must not be declared or undeclared"), - XML_L("prefix must not be bound to one of the reserved namespace names") - }; - if (code > 0 && code < sizeof(message)/sizeof(message[0])) - return message[code]; - return NULL; -} - -const XML_LChar * XMLCALL -XML_ExpatVersion(void) { - - /* V1 is used to string-ize the version number. However, it would - string-ize the actual version macro *names* unless we get them - substituted before being passed to V1. CPP is defined to expand - a macro, then rescan for more expansions. Thus, we use V2 to expand - the version macros, then CPP will expand the resulting V1() macro - with the correct numerals. */ - /* ### I'm assuming cpp is portable in this respect... */ - -#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) -#define V2(a,b,c) XML_L("expat_")V1(a,b,c) - - return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); - -#undef V1 -#undef V2 -} - -XML_Expat_Version XMLCALL -XML_ExpatVersionInfo(void) -{ - XML_Expat_Version version; - - version.major = XML_MAJOR_VERSION; - version.minor = XML_MINOR_VERSION; - version.micro = XML_MICRO_VERSION; - - return version; -} - -const XML_Feature * XMLCALL -XML_GetFeatureList(void) -{ - static const XML_Feature features[] = { - {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), - sizeof(XML_Char)}, - {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), - sizeof(XML_LChar)}, -#ifdef XML_UNICODE - {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, -#endif -#ifdef XML_UNICODE_WCHAR_T - {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, -#endif -#ifdef XML_DTD - {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, -#endif -#ifdef XML_CONTEXT_BYTES - {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), - XML_CONTEXT_BYTES}, -#endif -#ifdef XML_MIN_SIZE - {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, -#endif -#ifdef XML_NS - {XML_FEATURE_NS, XML_L("XML_NS"), 0}, -#endif -#ifdef XML_LARGE_SIZE - {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, -#endif -#ifdef XML_ATTR_INFO - {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, -#endif - {XML_FEATURE_END, NULL, 0} - }; - - return features; -} - -/* Initially tag->rawName always points into the parse buffer; - for those TAG instances opened while the current parse buffer was - processed, and not yet closed, we need to store tag->rawName in a more - permanent location, since the parse buffer is about to be discarded. -*/ -static XML_Bool -storeRawNames(XML_Parser parser) -{ - TAG *tag = tagStack; - while (tag) { - int bufSize; - int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); - char *rawNameBuf = tag->buf + nameLen; - /* Stop if already stored. Since tagStack is a stack, we can stop - at the first entry that has already been copied; everything - below it in the stack is already been accounted for in a - previous call to this function. - */ - if (tag->rawName == rawNameBuf) - break; - /* For re-use purposes we need to ensure that the - size of tag->buf is a multiple of sizeof(XML_Char). - */ - bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); - if (bufSize > tag->bufEnd - tag->buf) { - char *temp = (char *)REALLOC(tag->buf, bufSize); - if (temp == NULL) - return XML_FALSE; - /* if tag->name.str points to tag->buf (only when namespace - processing is off) then we have to update it - */ - if (tag->name.str == (XML_Char *)tag->buf) - tag->name.str = (XML_Char *)temp; - /* if tag->name.localPart is set (when namespace processing is on) - then update it as well, since it will always point into tag->buf - */ - if (tag->name.localPart) - tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - - (XML_Char *)tag->buf); - tag->buf = temp; - tag->bufEnd = temp + bufSize; - rawNameBuf = temp + nameLen; - } - memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); - tag->rawName = rawNameBuf; - tag = tag->parent; - } - return XML_TRUE; -} - -static enum XML_Error PTRCALL -contentProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doContent(parser, 0, encoding, start, end, - endPtr, (XML_Bool)!ps_finalBuffer); - if (result == XML_ERROR_NONE) { - if (!storeRawNames(parser)) - return XML_ERROR_NO_MEMORY; - } - return result; -} - -static enum XML_Error PTRCALL -externalEntityInitProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = initializeEncoding(parser); - if (result != XML_ERROR_NONE) - return result; - processor = externalEntityInitProcessor2; - return externalEntityInitProcessor2(parser, start, end, endPtr); -} - -static enum XML_Error PTRCALL -externalEntityInitProcessor2(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - const char *next = start; /* XmlContentTok doesn't always set the last arg */ - int tok = XmlContentTok(encoding, start, end, &next); - switch (tok) { - case XML_TOK_BOM: - /* If we are at the end of the buffer, this would cause the next stage, - i.e. externalEntityInitProcessor3, to pass control directly to - doContent (by detecting XML_TOK_NONE) without processing any xml text - declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. - */ - if (next == end && !ps_finalBuffer) { - *endPtr = next; - return XML_ERROR_NONE; - } - start = next; - break; - case XML_TOK_PARTIAL: - if (!ps_finalBuffer) { - *endPtr = start; - return XML_ERROR_NONE; - } - eventPtr = start; - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - if (!ps_finalBuffer) { - *endPtr = start; - return XML_ERROR_NONE; - } - eventPtr = start; - return XML_ERROR_PARTIAL_CHAR; - } - processor = externalEntityInitProcessor3; - return externalEntityInitProcessor3(parser, start, end, endPtr); -} - -static enum XML_Error PTRCALL -externalEntityInitProcessor3(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - int tok; - const char *next = start; /* XmlContentTok doesn't always set the last arg */ - eventPtr = start; - tok = XmlContentTok(encoding, start, end, &next); - eventEndPtr = next; - - switch (tok) { - case XML_TOK_XML_DECL: - { - enum XML_Error result; - result = processXmlDecl(parser, 1, start, next); - if (result != XML_ERROR_NONE) - return result; - switch (ps_parsing) { - case XML_SUSPENDED: - *endPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: - start = next; - } - } - break; - case XML_TOK_PARTIAL: - if (!ps_finalBuffer) { - *endPtr = start; - return XML_ERROR_NONE; - } - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - if (!ps_finalBuffer) { - *endPtr = start; - return XML_ERROR_NONE; - } - return XML_ERROR_PARTIAL_CHAR; - } - processor = externalEntityContentProcessor; - tagLevel = 1; - return externalEntityContentProcessor(parser, start, end, endPtr); -} - -static enum XML_Error PTRCALL -externalEntityContentProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doContent(parser, 1, encoding, start, end, - endPtr, (XML_Bool)!ps_finalBuffer); - if (result == XML_ERROR_NONE) { - if (!storeRawNames(parser)) - return XML_ERROR_NO_MEMORY; - } - return result; -} - -static enum XML_Error -doContent(XML_Parser parser, - int startTagLevel, - const ENCODING *enc, - const char *s, - const char *end, - const char **nextPtr, - XML_Bool haveMore) -{ - /* save one level of indirection */ - DTD * const dtd = _dtd; - - const char **eventPP; - const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); - } - *eventPP = s; - - for (;;) { - const char *next = s; /* XmlContentTok doesn't always set the last arg */ - int tok = XmlContentTok(enc, s, end, &next); - *eventEndPP = next; - switch (tok) { - case XML_TOK_TRAILING_CR: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - *eventEndPP = end; - if (characterDataHandler) { - XML_Char c = 0xA; - characterDataHandler(handlerArg, &c, 1); - } - else if (defaultHandler) - reportDefault(parser, enc, s, end); - /* We are at the end of the final buffer, should we check for - XML_SUSPENDED, XML_FINISHED? - */ - if (startTagLevel == 0) - return XML_ERROR_NO_ELEMENTS; - if (tagLevel != startTagLevel) - return XML_ERROR_ASYNC_ENTITY; - *nextPtr = end; - return XML_ERROR_NONE; - case XML_TOK_NONE: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - if (startTagLevel > 0) { - if (tagLevel != startTagLevel) - return XML_ERROR_ASYNC_ENTITY; - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_NO_ELEMENTS; - case XML_TOK_INVALID: - *eventPP = next; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_ENTITY_REF: - { - const XML_Char *name; - ENTITY *entity; - XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (ch) { - if (characterDataHandler) - characterDataHandler(handlerArg, &ch, 1); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - name = poolStoreString(&dtd->pool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) - return XML_ERROR_NO_MEMORY; - entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); - poolDiscard(&dtd->pool); - /* First, determine if a check for an existing declaration is needed; - if yes, check that the entity exists, and that it is internal, - otherwise call the skipped entity or default handler. - */ - if (!dtd->hasParamEntityRefs || dtd->standalone) { - if (!entity) - return XML_ERROR_UNDEFINED_ENTITY; - else if (!entity->is_internal) - return XML_ERROR_ENTITY_DECLARED_IN_PE; - } - else if (!entity) { - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, name, 0); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - if (entity->open) - return XML_ERROR_RECURSIVE_ENTITY_REF; - if (entity->notation) - return XML_ERROR_BINARY_ENTITY_REF; - if (entity->textPtr) { - enum XML_Error result; - if (!defaultExpandInternalEntities) { - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, entity->name, 0); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - result = processInternalEntity(parser, entity, XML_FALSE); - if (result != XML_ERROR_NONE) - return result; - } - else if (externalEntityRefHandler) { - const XML_Char *context; - entity->open = XML_TRUE; - context = getContext(parser); - entity->open = XML_FALSE; - if (!context) - return XML_ERROR_NO_MEMORY; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - context, - entity->base, - entity->systemId, - entity->publicId)) - return XML_ERROR_EXTERNAL_ENTITY_HANDLING; - poolDiscard(&tempPool); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - case XML_TOK_START_TAG_NO_ATTS: - /* fall through */ - case XML_TOK_START_TAG_WITH_ATTS: - { - TAG *tag; - enum XML_Error result; - XML_Char *toPtr; - if (freeTagList) { - tag = freeTagList; - freeTagList = freeTagList->parent; - } - else { - tag = (TAG *)MALLOC(sizeof(TAG)); - if (!tag) - return XML_ERROR_NO_MEMORY; - tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); - if (!tag->buf) { - FREE(tag); - return XML_ERROR_NO_MEMORY; - } - tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; - } - tag->bindings = NULL; - tag->parent = tagStack; - tagStack = tag; - tag->name.localPart = NULL; - tag->name.prefix = NULL; - tag->rawName = s + enc->minBytesPerChar; - tag->rawNameLength = XmlNameLength(enc, tag->rawName); - ++tagLevel; - { - const char *rawNameEnd = tag->rawName + tag->rawNameLength; - const char *fromPtr = tag->rawName; - toPtr = (XML_Char *)tag->buf; - for (;;) { - int bufSize; - int convLen; - XmlConvert(enc, - &fromPtr, rawNameEnd, - (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); - convLen = (int)(toPtr - (XML_Char *)tag->buf); - if (fromPtr == rawNameEnd) { - tag->name.strLen = convLen; - break; - } - bufSize = (int)(tag->bufEnd - tag->buf) << 1; - { - char *temp = (char *)REALLOC(tag->buf, bufSize); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - tag->buf = temp; - tag->bufEnd = temp + bufSize; - toPtr = (XML_Char *)temp + convLen; - } - } - } - tag->name.str = (XML_Char *)tag->buf; - *toPtr = XML_T('\0'); - result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); - if (result) - return result; - if (startElementHandler) - startElementHandler(handlerArg, tag->name.str, - (const XML_Char **)atts); - else if (defaultHandler) - reportDefault(parser, enc, s, next); - poolClear(&tempPool); - break; - } - case XML_TOK_EMPTY_ELEMENT_NO_ATTS: - /* fall through */ - case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: - { - const char *rawName = s + enc->minBytesPerChar; - enum XML_Error result; - BINDING *bindings = NULL; - XML_Bool noElmHandlers = XML_TRUE; - TAG_NAME name; - name.str = poolStoreString(&tempPool, enc, rawName, - rawName + XmlNameLength(enc, rawName)); - if (!name.str) - return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - result = storeAtts(parser, enc, s, &name, &bindings); - if (result) - return result; - poolFinish(&tempPool); - if (startElementHandler) { - startElementHandler(handlerArg, name.str, (const XML_Char **)atts); - noElmHandlers = XML_FALSE; - } - if (endElementHandler) { - if (startElementHandler) - *eventPP = *eventEndPP; - endElementHandler(handlerArg, name.str); - noElmHandlers = XML_FALSE; - } - if (noElmHandlers && defaultHandler) - reportDefault(parser, enc, s, next); - poolClear(&tempPool); - while (bindings) { - BINDING *b = bindings; - if (endNamespaceDeclHandler) - endNamespaceDeclHandler(handlerArg, b->prefix->name); - bindings = bindings->nextTagBinding; - b->nextTagBinding = freeBindingList; - freeBindingList = b; - b->prefix->binding = b->prevPrefixBinding; - } - } - if (tagLevel == 0) - return epilogProcessor(parser, next, end, nextPtr); - break; - case XML_TOK_END_TAG: - if (tagLevel == startTagLevel) - return XML_ERROR_ASYNC_ENTITY; - else { - int len; - const char *rawName; - TAG *tag = tagStack; - tagStack = tag->parent; - tag->parent = freeTagList; - freeTagList = tag; - rawName = s + enc->minBytesPerChar*2; - len = XmlNameLength(enc, rawName); - if (len != tag->rawNameLength - || memcmp(tag->rawName, rawName, len) != 0) { - *eventPP = rawName; - return XML_ERROR_TAG_MISMATCH; - } - --tagLevel; - if (endElementHandler) { - const XML_Char *localPart; - const XML_Char *prefix; - XML_Char *uri; - localPart = tag->name.localPart; - if (ns && localPart) { - /* localPart and prefix may have been overwritten in - tag->name.str, since this points to the binding->uri - buffer which gets re-used; so we have to add them again - */ - uri = (XML_Char *)tag->name.str + tag->name.uriLen; - /* don't need to check for space - already done in storeAtts() */ - while (*localPart) *uri++ = *localPart++; - prefix = (XML_Char *)tag->name.prefix; - if (ns_triplets && prefix) { - *uri++ = namespaceSeparator; - while (*prefix) *uri++ = *prefix++; - } - *uri = XML_T('\0'); - } - endElementHandler(handlerArg, tag->name.str); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - while (tag->bindings) { - BINDING *b = tag->bindings; - if (endNamespaceDeclHandler) - endNamespaceDeclHandler(handlerArg, b->prefix->name); - tag->bindings = tag->bindings->nextTagBinding; - b->nextTagBinding = freeBindingList; - freeBindingList = b; - b->prefix->binding = b->prevPrefixBinding; - } - if (tagLevel == 0) - return epilogProcessor(parser, next, end, nextPtr); - } - break; - case XML_TOK_CHAR_REF: - { - int n = XmlCharRefNumber(enc, s); - if (n < 0) - return XML_ERROR_BAD_CHAR_REF; - if (characterDataHandler) { - XML_Char buf[XML_ENCODE_MAX]; - characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - } - break; - case XML_TOK_XML_DECL: - return XML_ERROR_MISPLACED_XML_PI; - case XML_TOK_DATA_NEWLINE: - if (characterDataHandler) { - XML_Char c = 0xA; - characterDataHandler(handlerArg, &c, 1); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - case XML_TOK_CDATA_SECT_OPEN: - { - enum XML_Error result; - if (startCdataSectionHandler) - startCdataSectionHandler(handlerArg); -#if 0 - /* Suppose you doing a transformation on a document that involves - changing only the character data. You set up a defaultHandler - and a characterDataHandler. The defaultHandler simply copies - characters through. The characterDataHandler does the - transformation and writes the characters out escaping them as - necessary. This case will fail to work if we leave out the - following two lines (because & and < inside CDATA sections will - be incorrectly escaped). - - However, now we have a start/endCdataSectionHandler, so it seems - easier to let the user deal with this. - */ - else if (characterDataHandler) - characterDataHandler(handlerArg, dataBuf, 0); -#endif - else if (defaultHandler) - reportDefault(parser, enc, s, next); - result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); - if (result != XML_ERROR_NONE) - return result; - else if (!next) { - processor = cdataSectionProcessor; - return result; - } - } - break; - case XML_TOK_TRAILING_RSQB: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - if (characterDataHandler) { - if (MUST_CONVERT(enc, s)) { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); - characterDataHandler(handlerArg, dataBuf, - (int)(dataPtr - (ICHAR *)dataBuf)); - } - else - characterDataHandler(handlerArg, - (XML_Char *)s, - (int)((XML_Char *)end - (XML_Char *)s)); - } - else if (defaultHandler) - reportDefault(parser, enc, s, end); - /* We are at the end of the final buffer, should we check for - XML_SUSPENDED, XML_FINISHED? - */ - if (startTagLevel == 0) { - *eventPP = end; - return XML_ERROR_NO_ELEMENTS; - } - if (tagLevel != startTagLevel) { - *eventPP = end; - return XML_ERROR_ASYNC_ENTITY; - } - *nextPtr = end; - return XML_ERROR_NONE; - case XML_TOK_DATA_CHARS: - { - XML_CharacterDataHandler charDataHandler = characterDataHandler; - if (charDataHandler) { - if (MUST_CONVERT(enc, s)) { - for (;;) { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); - *eventEndPP = s; - charDataHandler(handlerArg, dataBuf, - (int)(dataPtr - (ICHAR *)dataBuf)); - if (s == next) - break; - *eventPP = s; - } - } - else - charDataHandler(handlerArg, - (XML_Char *)s, - (int)((XML_Char *)next - (XML_Char *)s)); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - } - break; - case XML_TOK_PI: - if (!reportProcessingInstruction(parser, enc, s, next)) - return XML_ERROR_NO_MEMORY; - break; - case XML_TOK_COMMENT: - if (!reportComment(parser, enc, s, next)) - return XML_ERROR_NO_MEMORY; - break; - default: - if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - } - *eventPP = s = next; - switch (ps_parsing) { - case XML_SUSPENDED: - *nextPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: ; - } - } - /* not reached */ -} - -/* Precondition: all arguments must be non-NULL; - Purpose: - - normalize attributes - - check attributes for well-formedness - - generate namespace aware attribute names (URI, prefix) - - build list of attributes for startElementHandler - - default attributes - - process namespace declarations (check and report them) - - generate namespace aware element name (URI, prefix) -*/ -static enum XML_Error -storeAtts(XML_Parser parser, const ENCODING *enc, - const char *attStr, TAG_NAME *tagNamePtr, - BINDING **bindingsPtr) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - ELEMENT_TYPE *elementType; - int nDefaultAtts; - const XML_Char **appAtts; /* the attribute list for the application */ - int attIndex = 0; - int prefixLen; - int i; - int n; - XML_Char *uri; - int nPrefixes = 0; - BINDING *binding; - const XML_Char *localPart; - - /* lookup the element type name */ - elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0); - if (!elementType) { - const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); - if (!name) - return XML_ERROR_NO_MEMORY; - elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, - sizeof(ELEMENT_TYPE)); - if (!elementType) - return XML_ERROR_NO_MEMORY; - if (ns && !setElementTypePrefix(parser, elementType)) - return XML_ERROR_NO_MEMORY; - } - nDefaultAtts = elementType->nDefaultAtts; - - /* get the attributes from the tokenizer */ - n = XmlGetAttributes(enc, attStr, attsSize, atts); - if (n + nDefaultAtts > attsSize) { - int oldAttsSize = attsSize; - ATTRIBUTE *temp; -#ifdef XML_ATTR_INFO - XML_AttrInfo *temp2; -#endif - attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; - temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - atts = temp; -#ifdef XML_ATTR_INFO - temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); - if (temp2 == NULL) - return XML_ERROR_NO_MEMORY; - attInfo = temp2; -#endif - if (n > oldAttsSize) - XmlGetAttributes(enc, attStr, n, atts); - } - - appAtts = (const XML_Char **)atts; - for (i = 0; i < n; i++) { - ATTRIBUTE *currAtt = &atts[i]; -#ifdef XML_ATTR_INFO - XML_AttrInfo *currAttInfo = &attInfo[i]; -#endif - /* add the name and value to the attribute list */ - ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, - currAtt->name - + XmlNameLength(enc, currAtt->name)); - if (!attId) - return XML_ERROR_NO_MEMORY; -#ifdef XML_ATTR_INFO - currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name); - currAttInfo->nameEnd = currAttInfo->nameStart + - XmlNameLength(enc, currAtt->name); - currAttInfo->valueStart = parseEndByteIndex - - (parseEndPtr - currAtt->valuePtr); - currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd); -#endif - /* Detect duplicate attributes by their QNames. This does not work when - namespace processing is turned on and different prefixes for the same - namespace are used. For this case we have a check further down. - */ - if ((attId->name)[-1]) { - if (enc == encoding) - eventPtr = atts[i].name; - return XML_ERROR_DUPLICATE_ATTRIBUTE; - } - (attId->name)[-1] = 1; - appAtts[attIndex++] = attId->name; - if (!atts[i].normalized) { - enum XML_Error result; - XML_Bool isCdata = XML_TRUE; - - /* figure out whether declared as other than CDATA */ - if (attId->maybeTokenized) { - int j; - for (j = 0; j < nDefaultAtts; j++) { - if (attId == elementType->defaultAtts[j].id) { - isCdata = elementType->defaultAtts[j].isCdata; - break; - } - } - } - - /* normalize the attribute value */ - result = storeAttributeValue(parser, enc, isCdata, - atts[i].valuePtr, atts[i].valueEnd, - &tempPool); - if (result) - return result; - appAtts[attIndex] = poolStart(&tempPool); - poolFinish(&tempPool); - } - else { - /* the value did not need normalizing */ - appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, - atts[i].valueEnd); - if (appAtts[attIndex] == 0) - return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - } - /* handle prefixed attribute names */ - if (attId->prefix) { - if (attId->xmlns) { - /* deal with namespace declarations here */ - enum XML_Error result = addBinding(parser, attId->prefix, attId, - appAtts[attIndex], bindingsPtr); - if (result) - return result; - --attIndex; - } - else { - /* deal with other prefixed names later */ - attIndex++; - nPrefixes++; - (attId->name)[-1] = 2; - } - } - else - attIndex++; - } - - /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ - nSpecifiedAtts = attIndex; - if (elementType->idAtt && (elementType->idAtt->name)[-1]) { - for (i = 0; i < attIndex; i += 2) - if (appAtts[i] == elementType->idAtt->name) { - idAttIndex = i; - break; - } - } - else - idAttIndex = -1; - - /* do attribute defaulting */ - for (i = 0; i < nDefaultAtts; i++) { - const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; - if (!(da->id->name)[-1] && da->value) { - if (da->id->prefix) { - if (da->id->xmlns) { - enum XML_Error result = addBinding(parser, da->id->prefix, da->id, - da->value, bindingsPtr); - if (result) - return result; - } - else { - (da->id->name)[-1] = 2; - nPrefixes++; - appAtts[attIndex++] = da->id->name; - appAtts[attIndex++] = da->value; - } - } - else { - (da->id->name)[-1] = 1; - appAtts[attIndex++] = da->id->name; - appAtts[attIndex++] = da->value; - } - } - } - appAtts[attIndex] = 0; - - /* expand prefixed attribute names, check for duplicates, - and clear flags that say whether attributes were specified */ - i = 0; - if (nPrefixes) { - int j; /* hash table index */ - unsigned long version = nsAttsVersion; - int nsAttsSize = (int)1 << nsAttsPower; - /* size of hash table must be at least 2 * (# of prefixed attributes) */ - if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ - NS_ATT *temp; - /* hash table size must also be a power of 2 and >= 8 */ - while (nPrefixes >> nsAttsPower++); - if (nsAttsPower < 3) - nsAttsPower = 3; - nsAttsSize = (int)1 << nsAttsPower; - temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); - if (!temp) - return XML_ERROR_NO_MEMORY; - nsAtts = temp; - version = 0; /* force re-initialization of nsAtts hash table */ - } - /* using a version flag saves us from initializing nsAtts every time */ - if (!version) { /* initialize version flags when version wraps around */ - version = INIT_ATTS_VERSION; - for (j = nsAttsSize; j != 0; ) - nsAtts[--j].version = version; - } - nsAttsVersion = --version; - - /* expand prefixed names and check for duplicates */ - for (; i < attIndex; i += 2) { - const XML_Char *s = appAtts[i]; - if (s[-1] == 2) { /* prefixed */ - ATTRIBUTE_ID *id; - const BINDING *b; - unsigned long uriHash = hash_secret_salt; - ((XML_Char *)s)[-1] = 0; /* clear flag */ - id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); - b = id->prefix->binding; - if (!b) - return XML_ERROR_UNBOUND_PREFIX; - - /* as we expand the name we also calculate its hash value */ - for (j = 0; j < b->uriLen; j++) { - const XML_Char c = b->uri[j]; - if (!poolAppendChar(&tempPool, c)) - return XML_ERROR_NO_MEMORY; - uriHash = CHAR_HASH(uriHash, c); - } - while (*s++ != XML_T(ASCII_COLON)) - ; - do { /* copies null terminator */ - const XML_Char c = *s; - if (!poolAppendChar(&tempPool, *s)) - return XML_ERROR_NO_MEMORY; - uriHash = CHAR_HASH(uriHash, c); - } while (*s++); - - { /* Check hash table for duplicate of expanded name (uriName). - Derived from code in lookup(parser, HASH_TABLE *table, ...). - */ - unsigned char step = 0; - unsigned long mask = nsAttsSize - 1; - j = uriHash & mask; /* index into hash table */ - while (nsAtts[j].version == version) { - /* for speed we compare stored hash values first */ - if (uriHash == nsAtts[j].hash) { - const XML_Char *s1 = poolStart(&tempPool); - const XML_Char *s2 = nsAtts[j].uriName; - /* s1 is null terminated, but not s2 */ - for (; *s1 == *s2 && *s1 != 0; s1++, s2++); - if (*s1 == 0) - return XML_ERROR_DUPLICATE_ATTRIBUTE; - } - if (!step) - step = PROBE_STEP(uriHash, mask, nsAttsPower); - j < step ? (j += nsAttsSize - step) : (j -= step); - } - } - - if (ns_triplets) { /* append namespace separator and prefix */ - tempPool.ptr[-1] = namespaceSeparator; - s = b->prefix->name; - do { - if (!poolAppendChar(&tempPool, *s)) - return XML_ERROR_NO_MEMORY; - } while (*s++); - } - - /* store expanded name in attribute list */ - s = poolStart(&tempPool); - poolFinish(&tempPool); - appAtts[i] = s; - - /* fill empty slot with new version, uriName and hash value */ - nsAtts[j].version = version; - nsAtts[j].hash = uriHash; - nsAtts[j].uriName = s; - - if (!--nPrefixes) { - i += 2; - break; - } - } - else /* not prefixed */ - ((XML_Char *)s)[-1] = 0; /* clear flag */ - } - } - /* clear flags for the remaining attributes */ - for (; i < attIndex; i += 2) - ((XML_Char *)(appAtts[i]))[-1] = 0; - for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) - binding->attId->name[-1] = 0; - - if (!ns) - return XML_ERROR_NONE; - - /* expand the element type name */ - if (elementType->prefix) { - binding = elementType->prefix->binding; - if (!binding) - return XML_ERROR_UNBOUND_PREFIX; - localPart = tagNamePtr->str; - while (*localPart++ != XML_T(ASCII_COLON)) - ; - } - else if (dtd->defaultPrefix.binding) { - binding = dtd->defaultPrefix.binding; - localPart = tagNamePtr->str; - } - else - return XML_ERROR_NONE; - prefixLen = 0; - if (ns_triplets && binding->prefix->name) { - for (; binding->prefix->name[prefixLen++];) - ; /* prefixLen includes null terminator */ - } - tagNamePtr->localPart = localPart; - tagNamePtr->uriLen = binding->uriLen; - tagNamePtr->prefix = binding->prefix->name; - tagNamePtr->prefixLen = prefixLen; - for (i = 0; localPart[i++];) - ; /* i includes null terminator */ - n = i + binding->uriLen + prefixLen; - if (n > binding->uriAlloc) { - TAG *p; - uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); - if (!uri) - return XML_ERROR_NO_MEMORY; - binding->uriAlloc = n + EXPAND_SPARE; - memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); - for (p = tagStack; p; p = p->parent) - if (p->name.str == binding->uri) - p->name.str = uri; - FREE(binding->uri); - binding->uri = uri; - } - /* if namespaceSeparator != '\0' then uri includes it already */ - uri = binding->uri + binding->uriLen; - memcpy(uri, localPart, i * sizeof(XML_Char)); - /* we always have a namespace separator between localPart and prefix */ - if (prefixLen) { - uri += i - 1; - *uri = namespaceSeparator; /* replace null terminator */ - memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); - } - tagNamePtr->str = binding->uri; - return XML_ERROR_NONE; -} - -/* addBinding() overwrites the value of prefix->binding without checking. - Therefore one must keep track of the old value outside of addBinding(). -*/ -static enum XML_Error -addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, - const XML_Char *uri, BINDING **bindingsPtr) -{ - static const XML_Char xmlNamespace[] = { - ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, - ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, - ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, - ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, - ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, - ASCII_e, '\0' - }; - static const int xmlLen = - (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; - static const XML_Char xmlnsNamespace[] = { - ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, - ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, - ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, - ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, - ASCII_SLASH, '\0' - }; - static const int xmlnsLen = - (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; - - XML_Bool mustBeXML = XML_FALSE; - XML_Bool isXML = XML_TRUE; - XML_Bool isXMLNS = XML_TRUE; - - BINDING *b; - int len; - - /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ - if (*uri == XML_T('\0') && prefix->name) - return XML_ERROR_UNDECLARING_PREFIX; - - if (prefix->name - && prefix->name[0] == XML_T(ASCII_x) - && prefix->name[1] == XML_T(ASCII_m) - && prefix->name[2] == XML_T(ASCII_l)) { - - /* Not allowed to bind xmlns */ - if (prefix->name[3] == XML_T(ASCII_n) - && prefix->name[4] == XML_T(ASCII_s) - && prefix->name[5] == XML_T('\0')) - return XML_ERROR_RESERVED_PREFIX_XMLNS; - - if (prefix->name[3] == XML_T('\0')) - mustBeXML = XML_TRUE; - } - - for (len = 0; uri[len]; len++) { - if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) - isXML = XML_FALSE; - - if (!mustBeXML && isXMLNS - && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) - isXMLNS = XML_FALSE; - } - isXML = isXML && len == xmlLen; - isXMLNS = isXMLNS && len == xmlnsLen; - - if (mustBeXML != isXML) - return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML - : XML_ERROR_RESERVED_NAMESPACE_URI; - - if (isXMLNS) - return XML_ERROR_RESERVED_NAMESPACE_URI; - - if (namespaceSeparator) - len++; - if (freeBindingList) { - b = freeBindingList; - if (len > b->uriAlloc) { - XML_Char *temp = (XML_Char *)REALLOC(b->uri, - sizeof(XML_Char) * (len + EXPAND_SPARE)); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - b->uri = temp; - b->uriAlloc = len + EXPAND_SPARE; - } - freeBindingList = b->nextTagBinding; - } - else { - b = (BINDING *)MALLOC(sizeof(BINDING)); - if (!b) - return XML_ERROR_NO_MEMORY; - b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); - if (!b->uri) { - FREE(b); - return XML_ERROR_NO_MEMORY; - } - b->uriAlloc = len + EXPAND_SPARE; - } - b->uriLen = len; - memcpy(b->uri, uri, len * sizeof(XML_Char)); - if (namespaceSeparator) - b->uri[len - 1] = namespaceSeparator; - b->prefix = prefix; - b->attId = attId; - b->prevPrefixBinding = prefix->binding; - /* NULL binding when default namespace undeclared */ - if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) - prefix->binding = NULL; - else - prefix->binding = b; - b->nextTagBinding = *bindingsPtr; - *bindingsPtr = b; - /* if attId == NULL then we are not starting a namespace scope */ - if (attId && startNamespaceDeclHandler) - startNamespaceDeclHandler(handlerArg, prefix->name, - prefix->binding ? uri : 0); - return XML_ERROR_NONE; -} - -/* The idea here is to avoid using stack for each CDATA section when - the whole file is parsed with one call. -*/ -static enum XML_Error PTRCALL -cdataSectionProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doCdataSection(parser, encoding, &start, end, - endPtr, (XML_Bool)!ps_finalBuffer); - if (result != XML_ERROR_NONE) - return result; - if (start) { - if (parentParser) { /* we are parsing an external entity */ - processor = externalEntityContentProcessor; - return externalEntityContentProcessor(parser, start, end, endPtr); - } - else { - processor = contentProcessor; - return contentProcessor(parser, start, end, endPtr); - } - } - return result; -} - -/* startPtr gets set to non-null if the section is closed, and to null if - the section is not yet closed. -*/ -static enum XML_Error -doCdataSection(XML_Parser parser, - const ENCODING *enc, - const char **startPtr, - const char *end, - const char **nextPtr, - XML_Bool haveMore) -{ - const char *s = *startPtr; - const char **eventPP; - const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; - *eventPP = s; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); - } - *eventPP = s; - *startPtr = NULL; - - for (;;) { - const char *next; - int tok = XmlCdataSectionTok(enc, s, end, &next); - *eventEndPP = next; - switch (tok) { - case XML_TOK_CDATA_SECT_CLOSE: - if (endCdataSectionHandler) - endCdataSectionHandler(handlerArg); -#if 0 - /* see comment under XML_TOK_CDATA_SECT_OPEN */ - else if (characterDataHandler) - characterDataHandler(handlerArg, dataBuf, 0); -#endif - else if (defaultHandler) - reportDefault(parser, enc, s, next); - *startPtr = next; - *nextPtr = next; - if (ps_parsing == XML_FINISHED) - return XML_ERROR_ABORTED; - else - return XML_ERROR_NONE; - case XML_TOK_DATA_NEWLINE: - if (characterDataHandler) { - XML_Char c = 0xA; - characterDataHandler(handlerArg, &c, 1); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - break; - case XML_TOK_DATA_CHARS: - { - XML_CharacterDataHandler charDataHandler = characterDataHandler; - if (charDataHandler) { - if (MUST_CONVERT(enc, s)) { - for (;;) { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); - *eventEndPP = next; - charDataHandler(handlerArg, dataBuf, - (int)(dataPtr - (ICHAR *)dataBuf)); - if (s == next) - break; - *eventPP = s; - } - } - else - charDataHandler(handlerArg, - (XML_Char *)s, - (int)((XML_Char *)next - (XML_Char *)s)); - } - else if (defaultHandler) - reportDefault(parser, enc, s, next); - } - break; - case XML_TOK_INVALID: - *eventPP = next; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL_CHAR: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_PARTIAL: - case XML_TOK_NONE: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_UNCLOSED_CDATA_SECTION; - default: - *eventPP = next; - return XML_ERROR_UNEXPECTED_STATE; - } - - *eventPP = s = next; - switch (ps_parsing) { - case XML_SUSPENDED: - *nextPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: ; - } - } - /* not reached */ -} - -#ifdef XML_DTD - -/* The idea here is to avoid using stack for each IGNORE section when - the whole file is parsed with one call. -*/ -static enum XML_Error PTRCALL -ignoreSectionProcessor(XML_Parser parser, - const char *start, - const char *end, - const char **endPtr) -{ - enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, - endPtr, (XML_Bool)!ps_finalBuffer); - if (result != XML_ERROR_NONE) - return result; - if (start) { - processor = prologProcessor; - return prologProcessor(parser, start, end, endPtr); - } - return result; -} - -/* startPtr gets set to non-null is the section is closed, and to null - if the section is not yet closed. -*/ -static enum XML_Error -doIgnoreSection(XML_Parser parser, - const ENCODING *enc, - const char **startPtr, - const char *end, - const char **nextPtr, - XML_Bool haveMore) -{ - const char *next; - int tok; - const char *s = *startPtr; - const char **eventPP; - const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; - *eventPP = s; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); - } - *eventPP = s; - *startPtr = NULL; - tok = XmlIgnoreSectionTok(enc, s, end, &next); - *eventEndPP = next; - switch (tok) { - case XML_TOK_IGNORE_SECT: - if (defaultHandler) - reportDefault(parser, enc, s, next); - *startPtr = next; - *nextPtr = next; - if (ps_parsing == XML_FINISHED) - return XML_ERROR_ABORTED; - else - return XML_ERROR_NONE; - case XML_TOK_INVALID: - *eventPP = next; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL_CHAR: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_PARTIAL: - case XML_TOK_NONE: - if (haveMore) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ - default: - *eventPP = next; - return XML_ERROR_UNEXPECTED_STATE; - } - /* not reached */ -} - -#endif /* XML_DTD */ - -static enum XML_Error -initializeEncoding(XML_Parser parser) -{ - const char *s; -#ifdef XML_UNICODE - char encodingBuf[128]; - if (!protocolEncodingName) - s = NULL; - else { - int i; - for (i = 0; protocolEncodingName[i]; i++) { - if (i == sizeof(encodingBuf) - 1 - || (protocolEncodingName[i] & ~0x7f) != 0) { - encodingBuf[0] = '\0'; - break; - } - encodingBuf[i] = (char)protocolEncodingName[i]; - } - encodingBuf[i] = '\0'; - s = encodingBuf; - } -#else - s = protocolEncodingName; -#endif - if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) - return XML_ERROR_NONE; - return handleUnknownEncoding(parser, protocolEncodingName); -} - -static enum XML_Error -processXmlDecl(XML_Parser parser, int isGeneralTextEntity, - const char *s, const char *next) -{ - const char *encodingName = NULL; - const XML_Char *storedEncName = NULL; - const ENCODING *newEncoding = NULL; - const char *version = NULL; - const char *versionend; - const XML_Char *storedversion = NULL; - int standalone = -1; - if (!(ns - ? XmlParseXmlDeclNS - : XmlParseXmlDecl)(isGeneralTextEntity, - encoding, - s, - next, - &eventPtr, - &version, - &versionend, - &encodingName, - &newEncoding, - &standalone)) { - if (isGeneralTextEntity) - return XML_ERROR_TEXT_DECL; - else - return XML_ERROR_XML_DECL; - } - if (!isGeneralTextEntity && standalone == 1) { - _dtd->standalone = XML_TRUE; -#ifdef XML_DTD - if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) - paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; -#endif /* XML_DTD */ - } - if (xmlDeclHandler) { - if (encodingName != NULL) { - storedEncName = poolStoreString(&temp2Pool, - encoding, - encodingName, - encodingName - + XmlNameLength(encoding, encodingName)); - if (!storedEncName) - return XML_ERROR_NO_MEMORY; - poolFinish(&temp2Pool); - } - if (version) { - storedversion = poolStoreString(&temp2Pool, - encoding, - version, - versionend - encoding->minBytesPerChar); - if (!storedversion) - return XML_ERROR_NO_MEMORY; - } - xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); - } - else if (defaultHandler) - reportDefault(parser, encoding, s, next); - if (protocolEncodingName == NULL) { - if (newEncoding) { - if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { - eventPtr = encodingName; - return XML_ERROR_INCORRECT_ENCODING; - } - encoding = newEncoding; - } - else if (encodingName) { - enum XML_Error result; - if (!storedEncName) { - storedEncName = poolStoreString( - &temp2Pool, encoding, encodingName, - encodingName + XmlNameLength(encoding, encodingName)); - if (!storedEncName) - return XML_ERROR_NO_MEMORY; - } - result = handleUnknownEncoding(parser, storedEncName); - poolClear(&temp2Pool); - if (result == XML_ERROR_UNKNOWN_ENCODING) - eventPtr = encodingName; - return result; - } - } - - if (storedEncName || storedversion) - poolClear(&temp2Pool); - - return XML_ERROR_NONE; -} - -static enum XML_Error -handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) -{ - if (unknownEncodingHandler) { - XML_Encoding info; - int i; - for (i = 0; i < 256; i++) - info.map[i] = -1; - info.convert = NULL; - info.data = NULL; - info.release = NULL; - if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, - &info)) { - ENCODING *enc; - unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); - if (!unknownEncodingMem) { - if (info.release) - info.release(info.data); - return XML_ERROR_NO_MEMORY; - } - enc = (ns - ? XmlInitUnknownEncodingNS - : XmlInitUnknownEncoding)(unknownEncodingMem, - info.map, - info.convert, - info.data); - if (enc) { - unknownEncodingData = info.data; - unknownEncodingRelease = info.release; - encoding = enc; - return XML_ERROR_NONE; - } - } - if (info.release != NULL) - info.release(info.data); - } - return XML_ERROR_UNKNOWN_ENCODING; -} - -static enum XML_Error PTRCALL -prologInitProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - enum XML_Error result = initializeEncoding(parser); - if (result != XML_ERROR_NONE) - return result; - processor = prologProcessor; - return prologProcessor(parser, s, end, nextPtr); -} - -#ifdef XML_DTD - -static enum XML_Error PTRCALL -externalParEntInitProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - enum XML_Error result = initializeEncoding(parser); - if (result != XML_ERROR_NONE) - return result; - - /* we know now that XML_Parse(Buffer) has been called, - so we consider the external parameter entity read */ - _dtd->paramEntityRead = XML_TRUE; - - if (prologState.inEntityValue) { - processor = entityValueInitProcessor; - return entityValueInitProcessor(parser, s, end, nextPtr); - } - else { - processor = externalParEntProcessor; - return externalParEntProcessor(parser, s, end, nextPtr); - } -} - -static enum XML_Error PTRCALL -entityValueInitProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - int tok; - const char *start = s; - const char *next = start; - eventPtr = start; - - for (;;) { - tok = XmlPrologTok(encoding, start, end, &next); - eventEndPtr = next; - if (tok <= 0) { - if (!ps_finalBuffer && tok != XML_TOK_INVALID) { - *nextPtr = s; - return XML_ERROR_NONE; - } - switch (tok) { - case XML_TOK_INVALID: - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_NONE: /* start == end */ - default: - break; - } - /* found end of entity value - can store it now */ - return storeEntityValue(parser, encoding, s, end); - } - else if (tok == XML_TOK_XML_DECL) { - enum XML_Error result; - result = processXmlDecl(parser, 0, start, next); - if (result != XML_ERROR_NONE) - return result; - switch (ps_parsing) { - case XML_SUSPENDED: - *nextPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: - *nextPtr = next; - } - /* stop scanning for text declaration - we found one */ - processor = entityValueProcessor; - return entityValueProcessor(parser, next, end, nextPtr); - } - /* If we are at the end of the buffer, this would cause XmlPrologTok to - return XML_TOK_NONE on the next call, which would then cause the - function to exit with *nextPtr set to s - that is what we want for other - tokens, but not for the BOM - we would rather like to skip it; - then, when this routine is entered the next time, XmlPrologTok will - return XML_TOK_INVALID, since the BOM is still in the buffer - */ - else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { - *nextPtr = next; - return XML_ERROR_NONE; - } - start = next; - eventPtr = start; - } -} - -static enum XML_Error PTRCALL -externalParEntProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - const char *next = s; - int tok; - - tok = XmlPrologTok(encoding, s, end, &next); - if (tok <= 0) { - if (!ps_finalBuffer && tok != XML_TOK_INVALID) { - *nextPtr = s; - return XML_ERROR_NONE; - } - switch (tok) { - case XML_TOK_INVALID: - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_NONE: /* start == end */ - default: - break; - } - } - /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. - However, when parsing an external subset, doProlog will not accept a BOM - as valid, and report a syntax error, so we have to skip the BOM - */ - else if (tok == XML_TOK_BOM) { - s = next; - tok = XmlPrologTok(encoding, s, end, &next); - } - - processor = prologProcessor; - return doProlog(parser, encoding, s, end, tok, next, - nextPtr, (XML_Bool)!ps_finalBuffer); -} - -static enum XML_Error PTRCALL -entityValueProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - const char *start = s; - const char *next = s; - const ENCODING *enc = encoding; - int tok; - - for (;;) { - tok = XmlPrologTok(enc, start, end, &next); - if (tok <= 0) { - if (!ps_finalBuffer && tok != XML_TOK_INVALID) { - *nextPtr = s; - return XML_ERROR_NONE; - } - switch (tok) { - case XML_TOK_INVALID: - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - return XML_ERROR_PARTIAL_CHAR; - case XML_TOK_NONE: /* start == end */ - default: - break; - } - /* found end of entity value - can store it now */ - return storeEntityValue(parser, enc, s, end); - } - start = next; - } -} - -#endif /* XML_DTD */ - -static enum XML_Error PTRCALL -prologProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - const char *next = s; - int tok = XmlPrologTok(encoding, s, end, &next); - return doProlog(parser, encoding, s, end, tok, next, - nextPtr, (XML_Bool)!ps_finalBuffer); -} - -static enum XML_Error -doProlog(XML_Parser parser, - const ENCODING *enc, - const char *s, - const char *end, - int tok, - const char *next, - const char **nextPtr, - XML_Bool haveMore) -{ -#ifdef XML_DTD - static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; -#endif /* XML_DTD */ - static const XML_Char atypeCDATA[] = - { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; - static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; - static const XML_Char atypeIDREF[] = - { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; - static const XML_Char atypeIDREFS[] = - { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; - static const XML_Char atypeENTITY[] = - { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; - static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, - ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; - static const XML_Char atypeNMTOKEN[] = { - ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; - static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, - ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; - static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, - ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; - static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; - static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; - - /* save one level of indirection */ - DTD * const dtd = _dtd; - - const char **eventPP; - const char **eventEndPP; - enum XML_Content_Quant quant; - - if (enc == encoding) { - eventPP = &eventPtr; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); - } - - for (;;) { - int role; - XML_Bool handleDefault = XML_TRUE; - *eventPP = s; - *eventEndPP = next; - if (tok <= 0) { - if (haveMore && tok != XML_TOK_INVALID) { - *nextPtr = s; - return XML_ERROR_NONE; - } - switch (tok) { - case XML_TOK_INVALID: - *eventPP = next; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - return XML_ERROR_PARTIAL_CHAR; - case -XML_TOK_PROLOG_S: - tok = -tok; - break; - case XML_TOK_NONE: -#ifdef XML_DTD - /* for internal PE NOT referenced between declarations */ - if (enc != encoding && !openInternalEntities->betweenDecl) { - *nextPtr = s; - return XML_ERROR_NONE; - } - /* WFC: PE Between Declarations - must check that PE contains - complete markup, not only for external PEs, but also for - internal PEs if the reference occurs between declarations. - */ - if (isParamEntity || enc != encoding) { - if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) - == XML_ROLE_ERROR) - return XML_ERROR_INCOMPLETE_PE; - *nextPtr = s; - return XML_ERROR_NONE; - } -#endif /* XML_DTD */ - return XML_ERROR_NO_ELEMENTS; - default: - tok = -tok; - next = end; - break; - } - } - role = XmlTokenRole(&prologState, tok, s, next, enc); - switch (role) { - case XML_ROLE_XML_DECL: - { - enum XML_Error result = processXmlDecl(parser, 0, s, next); - if (result != XML_ERROR_NONE) - return result; - enc = encoding; - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_DOCTYPE_NAME: - if (startDoctypeDeclHandler) { - doctypeName = poolStoreString(&tempPool, enc, s, next); - if (!doctypeName) - return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - doctypePubid = NULL; - handleDefault = XML_FALSE; - } - doctypeSysid = NULL; /* always initialize to NULL */ - break; - case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: - if (startDoctypeDeclHandler) { - startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, - doctypePubid, 1); - doctypeName = NULL; - poolClear(&tempPool); - handleDefault = XML_FALSE; - } - break; -#ifdef XML_DTD - case XML_ROLE_TEXT_DECL: - { - enum XML_Error result = processXmlDecl(parser, 1, s, next); - if (result != XML_ERROR_NONE) - return result; - enc = encoding; - handleDefault = XML_FALSE; - } - break; -#endif /* XML_DTD */ - case XML_ROLE_DOCTYPE_PUBLIC_ID: -#ifdef XML_DTD - useForeignDTD = XML_FALSE; - declEntity = (ENTITY *)lookup(parser, - &dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!declEntity) - return XML_ERROR_NO_MEMORY; -#endif /* XML_DTD */ - dtd->hasParamEntityRefs = XML_TRUE; - if (startDoctypeDeclHandler) { - XML_Char *pubId; - if (!XmlIsPublicId(enc, s, next, eventPP)) - return XML_ERROR_PUBLICID; - pubId = poolStoreString(&tempPool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!pubId) - return XML_ERROR_NO_MEMORY; - normalizePublicId(pubId); - poolFinish(&tempPool); - doctypePubid = pubId; - handleDefault = XML_FALSE; - goto alreadyChecked; - } - /* fall through */ - case XML_ROLE_ENTITY_PUBLIC_ID: - if (!XmlIsPublicId(enc, s, next, eventPP)) - return XML_ERROR_PUBLICID; - alreadyChecked: - if (dtd->keepProcessing && declEntity) { - XML_Char *tem = poolStoreString(&dtd->pool, - enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!tem) - return XML_ERROR_NO_MEMORY; - normalizePublicId(tem); - declEntity->publicId = tem; - poolFinish(&dtd->pool); - if (entityDeclHandler) - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_DOCTYPE_CLOSE: - if (doctypeName) { - startDoctypeDeclHandler(handlerArg, doctypeName, - doctypeSysid, doctypePubid, 0); - poolClear(&tempPool); - handleDefault = XML_FALSE; - } - /* doctypeSysid will be non-NULL in the case of a previous - XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler - was not set, indicating an external subset - */ -#ifdef XML_DTD - if (doctypeSysid || useForeignDTD) { - XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; - dtd->hasParamEntityRefs = XML_TRUE; - if (paramEntityParsing && externalEntityRefHandler) { - ENTITY *entity = (ENTITY *)lookup(parser, - &dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!entity) - return XML_ERROR_NO_MEMORY; - if (useForeignDTD) - entity->base = curBase; - dtd->paramEntityRead = XML_FALSE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) - return XML_ERROR_EXTERNAL_ENTITY_HANDLING; - if (dtd->paramEntityRead) { - if (!dtd->standalone && - notStandaloneHandler && - !notStandaloneHandler(handlerArg)) - return XML_ERROR_NOT_STANDALONE; - } - /* if we didn't read the foreign DTD then this means that there - is no external subset and we must reset dtd->hasParamEntityRefs - */ - else if (!doctypeSysid) - dtd->hasParamEntityRefs = hadParamEntityRefs; - /* end of DTD - no need to update dtd->keepProcessing */ - } - useForeignDTD = XML_FALSE; - } -#endif /* XML_DTD */ - if (endDoctypeDeclHandler) { - endDoctypeDeclHandler(handlerArg); - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_INSTANCE_START: -#ifdef XML_DTD - /* if there is no DOCTYPE declaration then now is the - last chance to read the foreign DTD - */ - if (useForeignDTD) { - XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; - dtd->hasParamEntityRefs = XML_TRUE; - if (paramEntityParsing && externalEntityRefHandler) { - ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!entity) - return XML_ERROR_NO_MEMORY; - entity->base = curBase; - dtd->paramEntityRead = XML_FALSE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) - return XML_ERROR_EXTERNAL_ENTITY_HANDLING; - if (dtd->paramEntityRead) { - if (!dtd->standalone && - notStandaloneHandler && - !notStandaloneHandler(handlerArg)) - return XML_ERROR_NOT_STANDALONE; - } - /* if we didn't read the foreign DTD then this means that there - is no external subset and we must reset dtd->hasParamEntityRefs - */ - else - dtd->hasParamEntityRefs = hadParamEntityRefs; - /* end of DTD - no need to update dtd->keepProcessing */ - } - } -#endif /* XML_DTD */ - processor = contentProcessor; - return contentProcessor(parser, s, end, nextPtr); - case XML_ROLE_ATTLIST_ELEMENT_NAME: - declElementType = getElementType(parser, enc, s, next); - if (!declElementType) - return XML_ERROR_NO_MEMORY; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_NAME: - declAttributeId = getAttributeId(parser, enc, s, next); - if (!declAttributeId) - return XML_ERROR_NO_MEMORY; - declAttributeIsCdata = XML_FALSE; - declAttributeType = NULL; - declAttributeIsId = XML_FALSE; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_CDATA: - declAttributeIsCdata = XML_TRUE; - declAttributeType = atypeCDATA; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_ID: - declAttributeIsId = XML_TRUE; - declAttributeType = atypeID; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_IDREF: - declAttributeType = atypeIDREF; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: - declAttributeType = atypeIDREFS; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: - declAttributeType = atypeENTITY; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: - declAttributeType = atypeENTITIES; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: - declAttributeType = atypeNMTOKEN; - goto checkAttListDeclHandler; - case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: - declAttributeType = atypeNMTOKENS; - checkAttListDeclHandler: - if (dtd->keepProcessing && attlistDeclHandler) - handleDefault = XML_FALSE; - break; - case XML_ROLE_ATTRIBUTE_ENUM_VALUE: - case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: - if (dtd->keepProcessing && attlistDeclHandler) { - const XML_Char *prefix; - if (declAttributeType) { - prefix = enumValueSep; - } - else { - prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE - ? notationPrefix - : enumValueStart); - } - if (!poolAppendString(&tempPool, prefix)) - return XML_ERROR_NO_MEMORY; - if (!poolAppend(&tempPool, enc, s, next)) - return XML_ERROR_NO_MEMORY; - declAttributeType = tempPool.start; - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: - case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: - if (dtd->keepProcessing) { - if (!defineAttribute(declElementType, declAttributeId, - declAttributeIsCdata, declAttributeIsId, - 0, parser)) - return XML_ERROR_NO_MEMORY; - if (attlistDeclHandler && declAttributeType) { - if (*declAttributeType == XML_T(ASCII_LPAREN) - || (*declAttributeType == XML_T(ASCII_N) - && declAttributeType[1] == XML_T(ASCII_O))) { - /* Enumerated or Notation type */ - if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) - || !poolAppendChar(&tempPool, XML_T('\0'))) - return XML_ERROR_NO_MEMORY; - declAttributeType = tempPool.start; - poolFinish(&tempPool); - } - *eventEndPP = s; - attlistDeclHandler(handlerArg, declElementType->name, - declAttributeId->name, declAttributeType, - 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); - poolClear(&tempPool); - handleDefault = XML_FALSE; - } - } - break; - case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: - case XML_ROLE_FIXED_ATTRIBUTE_VALUE: - if (dtd->keepProcessing) { - const XML_Char *attVal; - enum XML_Error result = - storeAttributeValue(parser, enc, declAttributeIsCdata, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar, - &dtd->pool); - if (result) - return result; - attVal = poolStart(&dtd->pool); - poolFinish(&dtd->pool); - /* ID attributes aren't allowed to have a default */ - if (!defineAttribute(declElementType, declAttributeId, - declAttributeIsCdata, XML_FALSE, attVal, parser)) - return XML_ERROR_NO_MEMORY; - if (attlistDeclHandler && declAttributeType) { - if (*declAttributeType == XML_T(ASCII_LPAREN) - || (*declAttributeType == XML_T(ASCII_N) - && declAttributeType[1] == XML_T(ASCII_O))) { - /* Enumerated or Notation type */ - if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) - || !poolAppendChar(&tempPool, XML_T('\0'))) - return XML_ERROR_NO_MEMORY; - declAttributeType = tempPool.start; - poolFinish(&tempPool); - } - *eventEndPP = s; - attlistDeclHandler(handlerArg, declElementType->name, - declAttributeId->name, declAttributeType, - attVal, - role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); - poolClear(&tempPool); - handleDefault = XML_FALSE; - } - } - break; - case XML_ROLE_ENTITY_VALUE: - if (dtd->keepProcessing) { - enum XML_Error result = storeEntityValue(parser, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (declEntity) { - declEntity->textPtr = poolStart(&dtd->entityValuePool); - declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); - poolFinish(&dtd->entityValuePool); - if (entityDeclHandler) { - *eventEndPP = s; - entityDeclHandler(handlerArg, - declEntity->name, - declEntity->is_param, - declEntity->textPtr, - declEntity->textLen, - curBase, 0, 0, 0); - handleDefault = XML_FALSE; - } - } - else - poolDiscard(&dtd->entityValuePool); - if (result != XML_ERROR_NONE) - return result; - } - break; - case XML_ROLE_DOCTYPE_SYSTEM_ID: -#ifdef XML_DTD - useForeignDTD = XML_FALSE; -#endif /* XML_DTD */ - dtd->hasParamEntityRefs = XML_TRUE; - if (startDoctypeDeclHandler) { - doctypeSysid = poolStoreString(&tempPool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (doctypeSysid == NULL) - return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - handleDefault = XML_FALSE; - } -#ifdef XML_DTD - else - /* use externalSubsetName to make doctypeSysid non-NULL - for the case where no startDoctypeDeclHandler is set */ - doctypeSysid = externalSubsetName; -#endif /* XML_DTD */ - if (!dtd->standalone -#ifdef XML_DTD - && !paramEntityParsing -#endif /* XML_DTD */ - && notStandaloneHandler - && !notStandaloneHandler(handlerArg)) - return XML_ERROR_NOT_STANDALONE; -#ifndef XML_DTD - break; -#else /* XML_DTD */ - if (!declEntity) { - declEntity = (ENTITY *)lookup(parser, - &dtd->paramEntities, - externalSubsetName, - sizeof(ENTITY)); - if (!declEntity) - return XML_ERROR_NO_MEMORY; - declEntity->publicId = NULL; - } - /* fall through */ -#endif /* XML_DTD */ - case XML_ROLE_ENTITY_SYSTEM_ID: - if (dtd->keepProcessing && declEntity) { - declEntity->systemId = poolStoreString(&dtd->pool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!declEntity->systemId) - return XML_ERROR_NO_MEMORY; - declEntity->base = curBase; - poolFinish(&dtd->pool); - if (entityDeclHandler) - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_ENTITY_COMPLETE: - if (dtd->keepProcessing && declEntity && entityDeclHandler) { - *eventEndPP = s; - entityDeclHandler(handlerArg, - declEntity->name, - declEntity->is_param, - 0,0, - declEntity->base, - declEntity->systemId, - declEntity->publicId, - 0); - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_ENTITY_NOTATION_NAME: - if (dtd->keepProcessing && declEntity) { - declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); - if (!declEntity->notation) - return XML_ERROR_NO_MEMORY; - poolFinish(&dtd->pool); - if (unparsedEntityDeclHandler) { - *eventEndPP = s; - unparsedEntityDeclHandler(handlerArg, - declEntity->name, - declEntity->base, - declEntity->systemId, - declEntity->publicId, - declEntity->notation); - handleDefault = XML_FALSE; - } - else if (entityDeclHandler) { - *eventEndPP = s; - entityDeclHandler(handlerArg, - declEntity->name, - 0,0,0, - declEntity->base, - declEntity->systemId, - declEntity->publicId, - declEntity->notation); - handleDefault = XML_FALSE; - } - } - break; - case XML_ROLE_GENERAL_ENTITY_NAME: - { - if (XmlPredefinedEntityName(enc, s, next)) { - declEntity = NULL; - break; - } - if (dtd->keepProcessing) { - const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); - if (!name) - return XML_ERROR_NO_MEMORY; - declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, - sizeof(ENTITY)); - if (!declEntity) - return XML_ERROR_NO_MEMORY; - if (declEntity->name != name) { - poolDiscard(&dtd->pool); - declEntity = NULL; - } - else { - poolFinish(&dtd->pool); - declEntity->publicId = NULL; - declEntity->is_param = XML_FALSE; - /* if we have a parent parser or are reading an internal parameter - entity, then the entity declaration is not considered "internal" - */ - declEntity->is_internal = !(parentParser || openInternalEntities); - if (entityDeclHandler) - handleDefault = XML_FALSE; - } - } - else { - poolDiscard(&dtd->pool); - declEntity = NULL; - } - } - break; - case XML_ROLE_PARAM_ENTITY_NAME: -#ifdef XML_DTD - if (dtd->keepProcessing) { - const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); - if (!name) - return XML_ERROR_NO_MEMORY; - declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, - name, sizeof(ENTITY)); - if (!declEntity) - return XML_ERROR_NO_MEMORY; - if (declEntity->name != name) { - poolDiscard(&dtd->pool); - declEntity = NULL; - } - else { - poolFinish(&dtd->pool); - declEntity->publicId = NULL; - declEntity->is_param = XML_TRUE; - /* if we have a parent parser or are reading an internal parameter - entity, then the entity declaration is not considered "internal" - */ - declEntity->is_internal = !(parentParser || openInternalEntities); - if (entityDeclHandler) - handleDefault = XML_FALSE; - } - } - else { - poolDiscard(&dtd->pool); - declEntity = NULL; - } -#else /* not XML_DTD */ - declEntity = NULL; -#endif /* XML_DTD */ - break; - case XML_ROLE_NOTATION_NAME: - declNotationPublicId = NULL; - declNotationName = NULL; - if (notationDeclHandler) { - declNotationName = poolStoreString(&tempPool, enc, s, next); - if (!declNotationName) - return XML_ERROR_NO_MEMORY; - poolFinish(&tempPool); - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_NOTATION_PUBLIC_ID: - if (!XmlIsPublicId(enc, s, next, eventPP)) - return XML_ERROR_PUBLICID; - if (declNotationName) { /* means notationDeclHandler != NULL */ - XML_Char *tem = poolStoreString(&tempPool, - enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!tem) - return XML_ERROR_NO_MEMORY; - normalizePublicId(tem); - declNotationPublicId = tem; - poolFinish(&tempPool); - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_NOTATION_SYSTEM_ID: - if (declNotationName && notationDeclHandler) { - const XML_Char *systemId - = poolStoreString(&tempPool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!systemId) - return XML_ERROR_NO_MEMORY; - *eventEndPP = s; - notationDeclHandler(handlerArg, - declNotationName, - curBase, - systemId, - declNotationPublicId); - handleDefault = XML_FALSE; - } - poolClear(&tempPool); - break; - case XML_ROLE_NOTATION_NO_SYSTEM_ID: - if (declNotationPublicId && notationDeclHandler) { - *eventEndPP = s; - notationDeclHandler(handlerArg, - declNotationName, - curBase, - 0, - declNotationPublicId); - handleDefault = XML_FALSE; - } - poolClear(&tempPool); - break; - case XML_ROLE_ERROR: - switch (tok) { - case XML_TOK_PARAM_ENTITY_REF: - /* PE references in internal subset are - not allowed within declarations. */ - return XML_ERROR_PARAM_ENTITY_REF; - case XML_TOK_XML_DECL: - return XML_ERROR_MISPLACED_XML_PI; - default: - return XML_ERROR_SYNTAX; - } -#ifdef XML_DTD - case XML_ROLE_IGNORE_SECT: - { - enum XML_Error result; - if (defaultHandler) - reportDefault(parser, enc, s, next); - handleDefault = XML_FALSE; - result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); - if (result != XML_ERROR_NONE) - return result; - else if (!next) { - processor = ignoreSectionProcessor; - return result; - } - } - break; -#endif /* XML_DTD */ - case XML_ROLE_GROUP_OPEN: - if (prologState.level >= groupSize) { - if (groupSize) { - char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - groupConnector = temp; - if (dtd->scaffIndex) { - int *temp = (int *)REALLOC(dtd->scaffIndex, - groupSize * sizeof(int)); - if (temp == NULL) - return XML_ERROR_NO_MEMORY; - dtd->scaffIndex = temp; - } - } - else { - groupConnector = (char *)MALLOC(groupSize = 32); - if (!groupConnector) - return XML_ERROR_NO_MEMORY; - } - } - groupConnector[prologState.level] = 0; - if (dtd->in_eldecl) { - int myindex = nextScaffoldPart(parser); - if (myindex < 0) - return XML_ERROR_NO_MEMORY; - dtd->scaffIndex[dtd->scaffLevel] = myindex; - dtd->scaffLevel++; - dtd->scaffold[myindex].type = XML_CTYPE_SEQ; - if (elementDeclHandler) - handleDefault = XML_FALSE; - } - break; - case XML_ROLE_GROUP_SEQUENCE: - if (groupConnector[prologState.level] == ASCII_PIPE) - return XML_ERROR_SYNTAX; - groupConnector[prologState.level] = ASCII_COMMA; - if (dtd->in_eldecl && elementDeclHandler) - handleDefault = XML_FALSE; - break; - case XML_ROLE_GROUP_CHOICE: - if (groupConnector[prologState.level] == ASCII_COMMA) - return XML_ERROR_SYNTAX; - if (dtd->in_eldecl - && !groupConnector[prologState.level] - && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type - != XML_CTYPE_MIXED) - ) { - dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type - = XML_CTYPE_CHOICE; - if (elementDeclHandler) - handleDefault = XML_FALSE; - } - groupConnector[prologState.level] = ASCII_PIPE; - break; - case XML_ROLE_PARAM_ENTITY_REF: -#ifdef XML_DTD - case XML_ROLE_INNER_PARAM_ENTITY_REF: - dtd->hasParamEntityRefs = XML_TRUE; - if (!paramEntityParsing) - dtd->keepProcessing = dtd->standalone; - else { - const XML_Char *name; - ENTITY *entity; - name = poolStoreString(&dtd->pool, enc, - s + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) - return XML_ERROR_NO_MEMORY; - entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); - poolDiscard(&dtd->pool); - /* first, determine if a check for an existing declaration is needed; - if yes, check that the entity exists, and that it is internal, - otherwise call the skipped entity handler - */ - if (prologState.documentEntity && - (dtd->standalone - ? !openInternalEntities - : !dtd->hasParamEntityRefs)) { - if (!entity) - return XML_ERROR_UNDEFINED_ENTITY; - else if (!entity->is_internal) - return XML_ERROR_ENTITY_DECLARED_IN_PE; - } - else if (!entity) { - dtd->keepProcessing = dtd->standalone; - /* cannot report skipped entities in declarations */ - if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { - skippedEntityHandler(handlerArg, name, 1); - handleDefault = XML_FALSE; - } - break; - } - if (entity->open) - return XML_ERROR_RECURSIVE_ENTITY_REF; - if (entity->textPtr) { - enum XML_Error result; - XML_Bool betweenDecl = - (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); - result = processInternalEntity(parser, entity, betweenDecl); - if (result != XML_ERROR_NONE) - return result; - handleDefault = XML_FALSE; - break; - } - if (externalEntityRefHandler) { - dtd->paramEntityRead = XML_FALSE; - entity->open = XML_TRUE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) { - entity->open = XML_FALSE; - return XML_ERROR_EXTERNAL_ENTITY_HANDLING; - } - entity->open = XML_FALSE; - handleDefault = XML_FALSE; - if (!dtd->paramEntityRead) { - dtd->keepProcessing = dtd->standalone; - break; - } - } - else { - dtd->keepProcessing = dtd->standalone; - break; - } - } -#endif /* XML_DTD */ - if (!dtd->standalone && - notStandaloneHandler && - !notStandaloneHandler(handlerArg)) - return XML_ERROR_NOT_STANDALONE; - break; - - /* Element declaration stuff */ - - case XML_ROLE_ELEMENT_NAME: - if (elementDeclHandler) { - declElementType = getElementType(parser, enc, s, next); - if (!declElementType) - return XML_ERROR_NO_MEMORY; - dtd->scaffLevel = 0; - dtd->scaffCount = 0; - dtd->in_eldecl = XML_TRUE; - handleDefault = XML_FALSE; - } - break; - - case XML_ROLE_CONTENT_ANY: - case XML_ROLE_CONTENT_EMPTY: - if (dtd->in_eldecl) { - if (elementDeclHandler) { - XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); - if (!content) - return XML_ERROR_NO_MEMORY; - content->quant = XML_CQUANT_NONE; - content->name = NULL; - content->numchildren = 0; - content->children = NULL; - content->type = ((role == XML_ROLE_CONTENT_ANY) ? - XML_CTYPE_ANY : - XML_CTYPE_EMPTY); - *eventEndPP = s; - elementDeclHandler(handlerArg, declElementType->name, content); - handleDefault = XML_FALSE; - } - dtd->in_eldecl = XML_FALSE; - } - break; - - case XML_ROLE_CONTENT_PCDATA: - if (dtd->in_eldecl) { - dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type - = XML_CTYPE_MIXED; - if (elementDeclHandler) - handleDefault = XML_FALSE; - } - break; - - case XML_ROLE_CONTENT_ELEMENT: - quant = XML_CQUANT_NONE; - goto elementContent; - case XML_ROLE_CONTENT_ELEMENT_OPT: - quant = XML_CQUANT_OPT; - goto elementContent; - case XML_ROLE_CONTENT_ELEMENT_REP: - quant = XML_CQUANT_REP; - goto elementContent; - case XML_ROLE_CONTENT_ELEMENT_PLUS: - quant = XML_CQUANT_PLUS; - elementContent: - if (dtd->in_eldecl) { - ELEMENT_TYPE *el; - const XML_Char *name; - int nameLen; - const char *nxt = (quant == XML_CQUANT_NONE - ? next - : next - enc->minBytesPerChar); - int myindex = nextScaffoldPart(parser); - if (myindex < 0) - return XML_ERROR_NO_MEMORY; - dtd->scaffold[myindex].type = XML_CTYPE_NAME; - dtd->scaffold[myindex].quant = quant; - el = getElementType(parser, enc, s, nxt); - if (!el) - return XML_ERROR_NO_MEMORY; - name = el->name; - dtd->scaffold[myindex].name = name; - nameLen = 0; - for (; name[nameLen++]; ); - dtd->contentStringLen += nameLen; - if (elementDeclHandler) - handleDefault = XML_FALSE; - } - break; - - case XML_ROLE_GROUP_CLOSE: - quant = XML_CQUANT_NONE; - goto closeGroup; - case XML_ROLE_GROUP_CLOSE_OPT: - quant = XML_CQUANT_OPT; - goto closeGroup; - case XML_ROLE_GROUP_CLOSE_REP: - quant = XML_CQUANT_REP; - goto closeGroup; - case XML_ROLE_GROUP_CLOSE_PLUS: - quant = XML_CQUANT_PLUS; - closeGroup: - if (dtd->in_eldecl) { - if (elementDeclHandler) - handleDefault = XML_FALSE; - dtd->scaffLevel--; - dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; - if (dtd->scaffLevel == 0) { - if (!handleDefault) { - XML_Content *model = build_model(parser); - if (!model) - return XML_ERROR_NO_MEMORY; - *eventEndPP = s; - elementDeclHandler(handlerArg, declElementType->name, model); - } - dtd->in_eldecl = XML_FALSE; - dtd->contentStringLen = 0; - } - } - break; - /* End element declaration stuff */ - - case XML_ROLE_PI: - if (!reportProcessingInstruction(parser, enc, s, next)) - return XML_ERROR_NO_MEMORY; - handleDefault = XML_FALSE; - break; - case XML_ROLE_COMMENT: - if (!reportComment(parser, enc, s, next)) - return XML_ERROR_NO_MEMORY; - handleDefault = XML_FALSE; - break; - case XML_ROLE_NONE: - switch (tok) { - case XML_TOK_BOM: - handleDefault = XML_FALSE; - break; - } - break; - case XML_ROLE_DOCTYPE_NONE: - if (startDoctypeDeclHandler) - handleDefault = XML_FALSE; - break; - case XML_ROLE_ENTITY_NONE: - if (dtd->keepProcessing && entityDeclHandler) - handleDefault = XML_FALSE; - break; - case XML_ROLE_NOTATION_NONE: - if (notationDeclHandler) - handleDefault = XML_FALSE; - break; - case XML_ROLE_ATTLIST_NONE: - if (dtd->keepProcessing && attlistDeclHandler) - handleDefault = XML_FALSE; - break; - case XML_ROLE_ELEMENT_NONE: - if (elementDeclHandler) - handleDefault = XML_FALSE; - break; - } /* end of big switch */ - - if (handleDefault && defaultHandler) - reportDefault(parser, enc, s, next); - - switch (ps_parsing) { - case XML_SUSPENDED: - *nextPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: - s = next; - tok = XmlPrologTok(enc, s, end, &next); - } - } - /* not reached */ -} - -static enum XML_Error PTRCALL -epilogProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - processor = epilogProcessor; - eventPtr = s; - for (;;) { - const char *next = NULL; - int tok = XmlPrologTok(encoding, s, end, &next); - eventEndPtr = next; - switch (tok) { - /* report partial linebreak - it might be the last token */ - case -XML_TOK_PROLOG_S: - if (defaultHandler) { - reportDefault(parser, encoding, s, next); - if (ps_parsing == XML_FINISHED) - return XML_ERROR_ABORTED; - } - *nextPtr = next; - return XML_ERROR_NONE; - case XML_TOK_NONE: - *nextPtr = s; - return XML_ERROR_NONE; - case XML_TOK_PROLOG_S: - if (defaultHandler) - reportDefault(parser, encoding, s, next); - break; - case XML_TOK_PI: - if (!reportProcessingInstruction(parser, encoding, s, next)) - return XML_ERROR_NO_MEMORY; - break; - case XML_TOK_COMMENT: - if (!reportComment(parser, encoding, s, next)) - return XML_ERROR_NO_MEMORY; - break; - case XML_TOK_INVALID: - eventPtr = next; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - if (!ps_finalBuffer) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_UNCLOSED_TOKEN; - case XML_TOK_PARTIAL_CHAR: - if (!ps_finalBuffer) { - *nextPtr = s; - return XML_ERROR_NONE; - } - return XML_ERROR_PARTIAL_CHAR; - default: - return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; - } - eventPtr = s = next; - switch (ps_parsing) { - case XML_SUSPENDED: - *nextPtr = next; - return XML_ERROR_NONE; - case XML_FINISHED: - return XML_ERROR_ABORTED; - default: ; - } - } -} - -static enum XML_Error -processInternalEntity(XML_Parser parser, ENTITY *entity, - XML_Bool betweenDecl) -{ - const char *textStart, *textEnd; - const char *next; - enum XML_Error result; - OPEN_INTERNAL_ENTITY *openEntity; - - if (freeInternalEntities) { - openEntity = freeInternalEntities; - freeInternalEntities = openEntity->next; - } - else { - openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); - if (!openEntity) - return XML_ERROR_NO_MEMORY; - } - entity->open = XML_TRUE; - entity->processed = 0; - openEntity->next = openInternalEntities; - openInternalEntities = openEntity; - openEntity->entity = entity; - openEntity->startTagLevel = tagLevel; - openEntity->betweenDecl = betweenDecl; - openEntity->internalEventPtr = NULL; - openEntity->internalEventEndPtr = NULL; - textStart = (char *)entity->textPtr; - textEnd = (char *)(entity->textPtr + entity->textLen); - -#ifdef XML_DTD - if (entity->is_param) { - int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); - result = doProlog(parser, internalEncoding, textStart, textEnd, tok, - next, &next, XML_FALSE); - } - else -#endif /* XML_DTD */ - result = doContent(parser, tagLevel, internalEncoding, textStart, - textEnd, &next, XML_FALSE); - - if (result == XML_ERROR_NONE) { - if (textEnd != next && ps_parsing == XML_SUSPENDED) { - entity->processed = (int)(next - textStart); - processor = internalEntityProcessor; - } - else { - entity->open = XML_FALSE; - openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ - openEntity->next = freeInternalEntities; - freeInternalEntities = openEntity; - } - } - return result; -} - -static enum XML_Error PTRCALL -internalEntityProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - ENTITY *entity; - const char *textStart, *textEnd; - const char *next; - enum XML_Error result; - OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; - if (!openEntity) - return XML_ERROR_UNEXPECTED_STATE; - - entity = openEntity->entity; - textStart = ((char *)entity->textPtr) + entity->processed; - textEnd = (char *)(entity->textPtr + entity->textLen); - -#ifdef XML_DTD - if (entity->is_param) { - int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); - result = doProlog(parser, internalEncoding, textStart, textEnd, tok, - next, &next, XML_FALSE); - } - else -#endif /* XML_DTD */ - result = doContent(parser, openEntity->startTagLevel, internalEncoding, - textStart, textEnd, &next, XML_FALSE); - - if (result != XML_ERROR_NONE) - return result; - else if (textEnd != next && ps_parsing == XML_SUSPENDED) { - entity->processed = (int)(next - (char *)entity->textPtr); - return result; - } - else { - entity->open = XML_FALSE; - openInternalEntities = openEntity->next; - /* put openEntity back in list of free instances */ - openEntity->next = freeInternalEntities; - freeInternalEntities = openEntity; - } - -#ifdef XML_DTD - if (entity->is_param) { - int tok; - processor = prologProcessor; - tok = XmlPrologTok(encoding, s, end, &next); - return doProlog(parser, encoding, s, end, tok, next, nextPtr, - (XML_Bool)!ps_finalBuffer); - } - else -#endif /* XML_DTD */ - { - processor = contentProcessor; - /* see externalEntityContentProcessor vs contentProcessor */ - return doContent(parser, parentParser ? 1 : 0, encoding, s, end, - nextPtr, (XML_Bool)!ps_finalBuffer); - } -} - -static enum XML_Error PTRCALL -errorProcessor(XML_Parser parser, - const char *s, - const char *end, - const char **nextPtr) -{ - return errorCode; -} - -static enum XML_Error -storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, - STRING_POOL *pool) -{ - enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, - end, pool); - if (result) - return result; - if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) - poolChop(pool); - if (!poolAppendChar(pool, XML_T('\0'))) - return XML_ERROR_NO_MEMORY; - return XML_ERROR_NONE; -} - -static enum XML_Error -appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, - const char *ptr, const char *end, - STRING_POOL *pool) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - for (;;) { - const char *next; - int tok = XmlAttributeValueTok(enc, ptr, end, &next); - switch (tok) { - case XML_TOK_NONE: - return XML_ERROR_NONE; - case XML_TOK_INVALID: - if (enc == encoding) - eventPtr = next; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_PARTIAL: - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_INVALID_TOKEN; - case XML_TOK_CHAR_REF: - { - XML_Char buf[XML_ENCODE_MAX]; - int i; - int n = XmlCharRefNumber(enc, ptr); - if (n < 0) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_BAD_CHAR_REF; - } - if (!isCdata - && n == 0x20 /* space */ - && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) - break; - n = XmlEncode(n, (ICHAR *)buf); - if (!n) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_BAD_CHAR_REF; - } - for (i = 0; i < n; i++) { - if (!poolAppendChar(pool, buf[i])) - return XML_ERROR_NO_MEMORY; - } - } - break; - case XML_TOK_DATA_CHARS: - if (!poolAppend(pool, enc, ptr, next)) - return XML_ERROR_NO_MEMORY; - break; - case XML_TOK_TRAILING_CR: - next = ptr + enc->minBytesPerChar; - /* fall through */ - case XML_TOK_ATTRIBUTE_VALUE_S: - case XML_TOK_DATA_NEWLINE: - if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) - break; - if (!poolAppendChar(pool, 0x20)) - return XML_ERROR_NO_MEMORY; - break; - case XML_TOK_ENTITY_REF: - { - const XML_Char *name; - ENTITY *entity; - char checkEntityDecl; - XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, - ptr + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (ch) { - if (!poolAppendChar(pool, ch)) - return XML_ERROR_NO_MEMORY; - break; - } - name = poolStoreString(&temp2Pool, enc, - ptr + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) - return XML_ERROR_NO_MEMORY; - entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); - poolDiscard(&temp2Pool); - /* First, determine if a check for an existing declaration is needed; - if yes, check that the entity exists, and that it is internal. - */ - if (pool == &dtd->pool) /* are we called from prolog? */ - checkEntityDecl = -#ifdef XML_DTD - prologState.documentEntity && -#endif /* XML_DTD */ - (dtd->standalone - ? !openInternalEntities - : !dtd->hasParamEntityRefs); - else /* if (pool == &tempPool): we are called from content */ - checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; - if (checkEntityDecl) { - if (!entity) - return XML_ERROR_UNDEFINED_ENTITY; - else if (!entity->is_internal) - return XML_ERROR_ENTITY_DECLARED_IN_PE; - } - else if (!entity) { - /* Cannot report skipped entity here - see comments on - skippedEntityHandler. - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, name, 0); - */ - /* Cannot call the default handler because this would be - out of sync with the call to the startElementHandler. - if ((pool == &tempPool) && defaultHandler) - reportDefault(parser, enc, ptr, next); - */ - break; - } - if (entity->open) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_RECURSIVE_ENTITY_REF; - } - if (entity->notation) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_BINARY_ENTITY_REF; - } - if (!entity->textPtr) { - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; - } - else { - enum XML_Error result; - const XML_Char *textEnd = entity->textPtr + entity->textLen; - entity->open = XML_TRUE; - result = appendAttributeValue(parser, internalEncoding, isCdata, - (char *)entity->textPtr, - (char *)textEnd, pool); - entity->open = XML_FALSE; - if (result) - return result; - } - } - break; - default: - if (enc == encoding) - eventPtr = ptr; - return XML_ERROR_UNEXPECTED_STATE; - } - ptr = next; - } - /* not reached */ -} - -static enum XML_Error -storeEntityValue(XML_Parser parser, - const ENCODING *enc, - const char *entityTextPtr, - const char *entityTextEnd) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - STRING_POOL *pool = &(dtd->entityValuePool); - enum XML_Error result = XML_ERROR_NONE; -#ifdef XML_DTD - int oldInEntityValue = prologState.inEntityValue; - prologState.inEntityValue = 1; -#endif /* XML_DTD */ - /* never return Null for the value argument in EntityDeclHandler, - since this would indicate an external entity; therefore we - have to make sure that entityValuePool.start is not null */ - if (!pool->blocks) { - if (!poolGrow(pool)) - return XML_ERROR_NO_MEMORY; - } - - for (;;) { - const char *next; - int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); - switch (tok) { - case XML_TOK_PARAM_ENTITY_REF: -#ifdef XML_DTD - if (isParamEntity || enc != encoding) { - const XML_Char *name; - ENTITY *entity; - name = poolStoreString(&tempPool, enc, - entityTextPtr + enc->minBytesPerChar, - next - enc->minBytesPerChar); - if (!name) { - result = XML_ERROR_NO_MEMORY; - goto endEntityValue; - } - entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); - poolDiscard(&tempPool); - if (!entity) { - /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ - /* cannot report skipped entity here - see comments on - skippedEntityHandler - if (skippedEntityHandler) - skippedEntityHandler(handlerArg, name, 0); - */ - dtd->keepProcessing = dtd->standalone; - goto endEntityValue; - } - if (entity->open) { - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_RECURSIVE_ENTITY_REF; - goto endEntityValue; - } - if (entity->systemId) { - if (externalEntityRefHandler) { - dtd->paramEntityRead = XML_FALSE; - entity->open = XML_TRUE; - if (!externalEntityRefHandler(externalEntityRefHandlerArg, - 0, - entity->base, - entity->systemId, - entity->publicId)) { - entity->open = XML_FALSE; - result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; - goto endEntityValue; - } - entity->open = XML_FALSE; - if (!dtd->paramEntityRead) - dtd->keepProcessing = dtd->standalone; - } - else - dtd->keepProcessing = dtd->standalone; - } - else { - entity->open = XML_TRUE; - result = storeEntityValue(parser, - internalEncoding, - (char *)entity->textPtr, - (char *)(entity->textPtr - + entity->textLen)); - entity->open = XML_FALSE; - if (result) - goto endEntityValue; - } - break; - } -#endif /* XML_DTD */ - /* In the internal subset, PE references are not legal - within markup declarations, e.g entity values in this case. */ - eventPtr = entityTextPtr; - result = XML_ERROR_PARAM_ENTITY_REF; - goto endEntityValue; - case XML_TOK_NONE: - result = XML_ERROR_NONE; - goto endEntityValue; - case XML_TOK_ENTITY_REF: - case XML_TOK_DATA_CHARS: - if (!poolAppend(pool, enc, entityTextPtr, next)) { - result = XML_ERROR_NO_MEMORY; - goto endEntityValue; - } - break; - case XML_TOK_TRAILING_CR: - next = entityTextPtr + enc->minBytesPerChar; - /* fall through */ - case XML_TOK_DATA_NEWLINE: - if (pool->end == pool->ptr && !poolGrow(pool)) { - result = XML_ERROR_NO_MEMORY; - goto endEntityValue; - } - *(pool->ptr)++ = 0xA; - break; - case XML_TOK_CHAR_REF: - { - XML_Char buf[XML_ENCODE_MAX]; - int i; - int n = XmlCharRefNumber(enc, entityTextPtr); - if (n < 0) { - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_BAD_CHAR_REF; - goto endEntityValue; - } - n = XmlEncode(n, (ICHAR *)buf); - if (!n) { - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_BAD_CHAR_REF; - goto endEntityValue; - } - for (i = 0; i < n; i++) { - if (pool->end == pool->ptr && !poolGrow(pool)) { - result = XML_ERROR_NO_MEMORY; - goto endEntityValue; - } - *(pool->ptr)++ = buf[i]; - } - } - break; - case XML_TOK_PARTIAL: - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_INVALID_TOKEN; - goto endEntityValue; - case XML_TOK_INVALID: - if (enc == encoding) - eventPtr = next; - result = XML_ERROR_INVALID_TOKEN; - goto endEntityValue; - default: - if (enc == encoding) - eventPtr = entityTextPtr; - result = XML_ERROR_UNEXPECTED_STATE; - goto endEntityValue; - } - entityTextPtr = next; - } -endEntityValue: -#ifdef XML_DTD - prologState.inEntityValue = oldInEntityValue; -#endif /* XML_DTD */ - return result; -} - -static void FASTCALL -normalizeLines(XML_Char *s) -{ - XML_Char *p; - for (;; s++) { - if (*s == XML_T('\0')) - return; - if (*s == 0xD) - break; - } - p = s; - do { - if (*s == 0xD) { - *p++ = 0xA; - if (*++s == 0xA) - s++; - } - else - *p++ = *s++; - } while (*s); - *p = XML_T('\0'); -} - -static int -reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end) -{ - const XML_Char *target; - XML_Char *data; - const char *tem; - if (!processingInstructionHandler) { - if (defaultHandler) - reportDefault(parser, enc, start, end); - return 1; - } - start += enc->minBytesPerChar * 2; - tem = start + XmlNameLength(enc, start); - target = poolStoreString(&tempPool, enc, start, tem); - if (!target) - return 0; - poolFinish(&tempPool); - data = poolStoreString(&tempPool, enc, - XmlSkipS(enc, tem), - end - enc->minBytesPerChar*2); - if (!data) - return 0; - normalizeLines(data); - processingInstructionHandler(handlerArg, target, data); - poolClear(&tempPool); - return 1; -} - -static int -reportComment(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end) -{ - XML_Char *data; - if (!commentHandler) { - if (defaultHandler) - reportDefault(parser, enc, start, end); - return 1; - } - data = poolStoreString(&tempPool, - enc, - start + enc->minBytesPerChar * 4, - end - enc->minBytesPerChar * 3); - if (!data) - return 0; - normalizeLines(data); - commentHandler(handlerArg, data); - poolClear(&tempPool); - return 1; -} - -static void -reportDefault(XML_Parser parser, const ENCODING *enc, - const char *s, const char *end) -{ - if (MUST_CONVERT(enc, s)) { - const char **eventPP; - const char **eventEndPP; - if (enc == encoding) { - eventPP = &eventPtr; - eventEndPP = &eventEndPtr; - } - else { - eventPP = &(openInternalEntities->internalEventPtr); - eventEndPP = &(openInternalEntities->internalEventEndPtr); - } - do { - ICHAR *dataPtr = (ICHAR *)dataBuf; - XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); - *eventEndPP = s; - defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); - *eventPP = s; - } while (s != end); - } - else - defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); -} - - -static int -defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, - XML_Bool isId, const XML_Char *value, XML_Parser parser) -{ - DEFAULT_ATTRIBUTE *att; - if (value || isId) { - /* The handling of default attributes gets messed up if we have - a default which duplicates a non-default. */ - int i; - for (i = 0; i < type->nDefaultAtts; i++) - if (attId == type->defaultAtts[i].id) - return 1; - if (isId && !type->idAtt && !attId->xmlns) - type->idAtt = attId; - } - if (type->nDefaultAtts == type->allocDefaultAtts) { - if (type->allocDefaultAtts == 0) { - type->allocDefaultAtts = 8; - type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts - * sizeof(DEFAULT_ATTRIBUTE)); - if (!type->defaultAtts) - return 0; - } - else { - DEFAULT_ATTRIBUTE *temp; - int count = type->allocDefaultAtts * 2; - temp = (DEFAULT_ATTRIBUTE *) - REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); - if (temp == NULL) - return 0; - type->allocDefaultAtts = count; - type->defaultAtts = temp; - } - } - att = type->defaultAtts + type->nDefaultAtts; - att->id = attId; - att->value = value; - att->isCdata = isCdata; - if (!isCdata) - attId->maybeTokenized = XML_TRUE; - type->nDefaultAtts += 1; - return 1; -} - -static int -setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - const XML_Char *name; - for (name = elementType->name; *name; name++) { - if (*name == XML_T(ASCII_COLON)) { - PREFIX *prefix; - const XML_Char *s; - for (s = elementType->name; s != name; s++) { - if (!poolAppendChar(&dtd->pool, *s)) - return 0; - } - if (!poolAppendChar(&dtd->pool, XML_T('\0'))) - return 0; - prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), - sizeof(PREFIX)); - if (!prefix) - return 0; - if (prefix->name == poolStart(&dtd->pool)) - poolFinish(&dtd->pool); - else - poolDiscard(&dtd->pool); - elementType->prefix = prefix; - - } - } - return 1; -} - -static ATTRIBUTE_ID * -getAttributeId(XML_Parser parser, const ENCODING *enc, - const char *start, const char *end) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - ATTRIBUTE_ID *id; - const XML_Char *name; - if (!poolAppendChar(&dtd->pool, XML_T('\0'))) - return NULL; - name = poolStoreString(&dtd->pool, enc, start, end); - if (!name) - return NULL; - /* skip quotation mark - its storage will be re-used (like in name[-1]) */ - ++name; - id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); - if (!id) - return NULL; - if (id->name != name) - poolDiscard(&dtd->pool); - else { - poolFinish(&dtd->pool); - if (!ns) - ; - else if (name[0] == XML_T(ASCII_x) - && name[1] == XML_T(ASCII_m) - && name[2] == XML_T(ASCII_l) - && name[3] == XML_T(ASCII_n) - && name[4] == XML_T(ASCII_s) - && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { - if (name[5] == XML_T('\0')) - id->prefix = &dtd->defaultPrefix; - else - id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); - id->xmlns = XML_TRUE; - } - else { - int i; - for (i = 0; name[i]; i++) { - /* attributes without prefix are *not* in the default namespace */ - if (name[i] == XML_T(ASCII_COLON)) { - int j; - for (j = 0; j < i; j++) { - if (!poolAppendChar(&dtd->pool, name[j])) - return NULL; - } - if (!poolAppendChar(&dtd->pool, XML_T('\0'))) - return NULL; - id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), - sizeof(PREFIX)); - if (id->prefix->name == poolStart(&dtd->pool)) - poolFinish(&dtd->pool); - else - poolDiscard(&dtd->pool); - break; - } - } - } - } - return id; -} - -#define CONTEXT_SEP XML_T(ASCII_FF) - -static const XML_Char * -getContext(XML_Parser parser) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - HASH_TABLE_ITER iter; - XML_Bool needSep = XML_FALSE; - - if (dtd->defaultPrefix.binding) { - int i; - int len; - if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) - return NULL; - len = dtd->defaultPrefix.binding->uriLen; - if (namespaceSeparator) - len--; - for (i = 0; i < len; i++) - if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) - return NULL; - needSep = XML_TRUE; - } - - hashTableIterInit(&iter, &(dtd->prefixes)); - for (;;) { - int i; - int len; - const XML_Char *s; - PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); - if (!prefix) - break; - if (!prefix->binding) - continue; - if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) - return NULL; - for (s = prefix->name; *s; s++) - if (!poolAppendChar(&tempPool, *s)) - return NULL; - if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) - return NULL; - len = prefix->binding->uriLen; - if (namespaceSeparator) - len--; - for (i = 0; i < len; i++) - if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) - return NULL; - needSep = XML_TRUE; - } - - - hashTableIterInit(&iter, &(dtd->generalEntities)); - for (;;) { - const XML_Char *s; - ENTITY *e = (ENTITY *)hashTableIterNext(&iter); - if (!e) - break; - if (!e->open) - continue; - if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) - return NULL; - for (s = e->name; *s; s++) - if (!poolAppendChar(&tempPool, *s)) - return 0; - needSep = XML_TRUE; - } - - if (!poolAppendChar(&tempPool, XML_T('\0'))) - return NULL; - return tempPool.start; -} - -static XML_Bool -setContext(XML_Parser parser, const XML_Char *context) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - const XML_Char *s = context; - - while (*context != XML_T('\0')) { - if (*s == CONTEXT_SEP || *s == XML_T('\0')) { - ENTITY *e; - if (!poolAppendChar(&tempPool, XML_T('\0'))) - return XML_FALSE; - e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0); - if (e) - e->open = XML_TRUE; - if (*s != XML_T('\0')) - s++; - context = s; - poolDiscard(&tempPool); - } - else if (*s == XML_T(ASCII_EQUALS)) { - PREFIX *prefix; - if (poolLength(&tempPool) == 0) - prefix = &dtd->defaultPrefix; - else { - if (!poolAppendChar(&tempPool, XML_T('\0'))) - return XML_FALSE; - prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool), - sizeof(PREFIX)); - if (!prefix) - return XML_FALSE; - if (prefix->name == poolStart(&tempPool)) { - prefix->name = poolCopyString(&dtd->pool, prefix->name); - if (!prefix->name) - return XML_FALSE; - } - poolDiscard(&tempPool); - } - for (context = s + 1; - *context != CONTEXT_SEP && *context != XML_T('\0'); - context++) - if (!poolAppendChar(&tempPool, *context)) - return XML_FALSE; - if (!poolAppendChar(&tempPool, XML_T('\0'))) - return XML_FALSE; - if (addBinding(parser, prefix, NULL, poolStart(&tempPool), - &inheritedBindings) != XML_ERROR_NONE) - return XML_FALSE; - poolDiscard(&tempPool); - if (*context != XML_T('\0')) - ++context; - s = context; - } - else { - if (!poolAppendChar(&tempPool, *s)) - return XML_FALSE; - s++; - } - } - return XML_TRUE; -} - -static void FASTCALL -normalizePublicId(XML_Char *publicId) -{ - XML_Char *p = publicId; - XML_Char *s; - for (s = publicId; *s; s++) { - switch (*s) { - case 0x20: - case 0xD: - case 0xA: - if (p != publicId && p[-1] != 0x20) - *p++ = 0x20; - break; - default: - *p++ = *s; - } - } - if (p != publicId && p[-1] == 0x20) - --p; - *p = XML_T('\0'); -} - -static DTD * -dtdCreate(const XML_Memory_Handling_Suite *ms) -{ - DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); - if (p == NULL) - return p; - poolInit(&(p->pool), ms); - poolInit(&(p->entityValuePool), ms); - hashTableInit(&(p->generalEntities), ms); - hashTableInit(&(p->elementTypes), ms); - hashTableInit(&(p->attributeIds), ms); - hashTableInit(&(p->prefixes), ms); -#ifdef XML_DTD - p->paramEntityRead = XML_FALSE; - hashTableInit(&(p->paramEntities), ms); -#endif /* XML_DTD */ - p->defaultPrefix.name = NULL; - p->defaultPrefix.binding = NULL; - - p->in_eldecl = XML_FALSE; - p->scaffIndex = NULL; - p->scaffold = NULL; - p->scaffLevel = 0; - p->scaffSize = 0; - p->scaffCount = 0; - p->contentStringLen = 0; - - p->keepProcessing = XML_TRUE; - p->hasParamEntityRefs = XML_FALSE; - p->standalone = XML_FALSE; - return p; -} - -static void -dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) -{ - HASH_TABLE_ITER iter; - hashTableIterInit(&iter, &(p->elementTypes)); - for (;;) { - ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); - if (!e) - break; - if (e->allocDefaultAtts != 0) - ms->free_fcn(e->defaultAtts); - } - hashTableClear(&(p->generalEntities)); -#ifdef XML_DTD - p->paramEntityRead = XML_FALSE; - hashTableClear(&(p->paramEntities)); -#endif /* XML_DTD */ - hashTableClear(&(p->elementTypes)); - hashTableClear(&(p->attributeIds)); - hashTableClear(&(p->prefixes)); - poolClear(&(p->pool)); - poolClear(&(p->entityValuePool)); - p->defaultPrefix.name = NULL; - p->defaultPrefix.binding = NULL; - - p->in_eldecl = XML_FALSE; - - ms->free_fcn(p->scaffIndex); - p->scaffIndex = NULL; - ms->free_fcn(p->scaffold); - p->scaffold = NULL; - - p->scaffLevel = 0; - p->scaffSize = 0; - p->scaffCount = 0; - p->contentStringLen = 0; - - p->keepProcessing = XML_TRUE; - p->hasParamEntityRefs = XML_FALSE; - p->standalone = XML_FALSE; -} - -static void -dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) -{ - HASH_TABLE_ITER iter; - hashTableIterInit(&iter, &(p->elementTypes)); - for (;;) { - ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); - if (!e) - break; - if (e->allocDefaultAtts != 0) - ms->free_fcn(e->defaultAtts); - } - hashTableDestroy(&(p->generalEntities)); -#ifdef XML_DTD - hashTableDestroy(&(p->paramEntities)); -#endif /* XML_DTD */ - hashTableDestroy(&(p->elementTypes)); - hashTableDestroy(&(p->attributeIds)); - hashTableDestroy(&(p->prefixes)); - poolDestroy(&(p->pool)); - poolDestroy(&(p->entityValuePool)); - if (isDocEntity) { - ms->free_fcn(p->scaffIndex); - ms->free_fcn(p->scaffold); - } - ms->free_fcn(p); -} - -/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. - The new DTD has already been initialized. -*/ -static int -dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) -{ - HASH_TABLE_ITER iter; - - /* Copy the prefix table. */ - - hashTableIterInit(&iter, &(oldDtd->prefixes)); - for (;;) { - const XML_Char *name; - const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); - if (!oldP) - break; - name = poolCopyString(&(newDtd->pool), oldP->name); - if (!name) - return 0; - if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) - return 0; - } - - hashTableIterInit(&iter, &(oldDtd->attributeIds)); - - /* Copy the attribute id table. */ - - for (;;) { - ATTRIBUTE_ID *newA; - const XML_Char *name; - const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); - - if (!oldA) - break; - /* Remember to allocate the scratch byte before the name. */ - if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) - return 0; - name = poolCopyString(&(newDtd->pool), oldA->name); - if (!name) - return 0; - ++name; - newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, - sizeof(ATTRIBUTE_ID)); - if (!newA) - return 0; - newA->maybeTokenized = oldA->maybeTokenized; - if (oldA->prefix) { - newA->xmlns = oldA->xmlns; - if (oldA->prefix == &oldDtd->defaultPrefix) - newA->prefix = &newDtd->defaultPrefix; - else - newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), - oldA->prefix->name, 0); - } - } - - /* Copy the element type table. */ - - hashTableIterInit(&iter, &(oldDtd->elementTypes)); - - for (;;) { - int i; - ELEMENT_TYPE *newE; - const XML_Char *name; - const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); - if (!oldE) - break; - name = poolCopyString(&(newDtd->pool), oldE->name); - if (!name) - return 0; - newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, - sizeof(ELEMENT_TYPE)); - if (!newE) - return 0; - if (oldE->nDefaultAtts) { - newE->defaultAtts = (DEFAULT_ATTRIBUTE *) - ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); - if (!newE->defaultAtts) { - ms->free_fcn(newE); - return 0; - } - } - if (oldE->idAtt) - newE->idAtt = (ATTRIBUTE_ID *) - lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0); - newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; - if (oldE->prefix) - newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), - oldE->prefix->name, 0); - for (i = 0; i < newE->nDefaultAtts; i++) { - newE->defaultAtts[i].id = (ATTRIBUTE_ID *) - lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); - newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; - if (oldE->defaultAtts[i].value) { - newE->defaultAtts[i].value - = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); - if (!newE->defaultAtts[i].value) - return 0; - } - else - newE->defaultAtts[i].value = NULL; - } - } - - /* Copy the entity tables. */ - if (!copyEntityTable(oldParser, - &(newDtd->generalEntities), - &(newDtd->pool), - &(oldDtd->generalEntities))) - return 0; - -#ifdef XML_DTD - if (!copyEntityTable(oldParser, - &(newDtd->paramEntities), - &(newDtd->pool), - &(oldDtd->paramEntities))) - return 0; - newDtd->paramEntityRead = oldDtd->paramEntityRead; -#endif /* XML_DTD */ - - newDtd->keepProcessing = oldDtd->keepProcessing; - newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; - newDtd->standalone = oldDtd->standalone; - - /* Don't want deep copying for scaffolding */ - newDtd->in_eldecl = oldDtd->in_eldecl; - newDtd->scaffold = oldDtd->scaffold; - newDtd->contentStringLen = oldDtd->contentStringLen; - newDtd->scaffSize = oldDtd->scaffSize; - newDtd->scaffLevel = oldDtd->scaffLevel; - newDtd->scaffIndex = oldDtd->scaffIndex; - - return 1; -} /* End dtdCopy */ - -static int -copyEntityTable(XML_Parser oldParser, - HASH_TABLE *newTable, - STRING_POOL *newPool, - const HASH_TABLE *oldTable) -{ - HASH_TABLE_ITER iter; - const XML_Char *cachedOldBase = NULL; - const XML_Char *cachedNewBase = NULL; - - hashTableIterInit(&iter, oldTable); - - for (;;) { - ENTITY *newE; - const XML_Char *name; - const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); - if (!oldE) - break; - name = poolCopyString(newPool, oldE->name); - if (!name) - return 0; - newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); - if (!newE) - return 0; - if (oldE->systemId) { - const XML_Char *tem = poolCopyString(newPool, oldE->systemId); - if (!tem) - return 0; - newE->systemId = tem; - if (oldE->base) { - if (oldE->base == cachedOldBase) - newE->base = cachedNewBase; - else { - cachedOldBase = oldE->base; - tem = poolCopyString(newPool, cachedOldBase); - if (!tem) - return 0; - cachedNewBase = newE->base = tem; - } - } - if (oldE->publicId) { - tem = poolCopyString(newPool, oldE->publicId); - if (!tem) - return 0; - newE->publicId = tem; - } - } - else { - const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, - oldE->textLen); - if (!tem) - return 0; - newE->textPtr = tem; - newE->textLen = oldE->textLen; - } - if (oldE->notation) { - const XML_Char *tem = poolCopyString(newPool, oldE->notation); - if (!tem) - return 0; - newE->notation = tem; - } - newE->is_param = oldE->is_param; - newE->is_internal = oldE->is_internal; - } - return 1; -} - -#define INIT_POWER 6 - -static XML_Bool FASTCALL -keyeq(KEY s1, KEY s2) -{ - for (; *s1 == *s2; s1++, s2++) - if (*s1 == 0) - return XML_TRUE; - return XML_FALSE; -} - -static unsigned long FASTCALL -hash(XML_Parser parser, KEY s) -{ - unsigned long h = hash_secret_salt; - while (*s) - h = CHAR_HASH(h, *s++); - return h; -} - -static NAMED * -lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) -{ - size_t i; - if (table->size == 0) { - size_t tsize; - if (!createSize) - return NULL; - table->power = INIT_POWER; - /* table->size is a power of 2 */ - table->size = (size_t)1 << INIT_POWER; - tsize = table->size * sizeof(NAMED *); - table->v = (NAMED **)table->mem->malloc_fcn(tsize); - if (!table->v) { - table->size = 0; - return NULL; - } - memset(table->v, 0, tsize); - i = hash(parser, name) & ((unsigned long)table->size - 1); - } - else { - unsigned long h = hash(parser, name); - unsigned long mask = (unsigned long)table->size - 1; - unsigned char step = 0; - i = h & mask; - while (table->v[i]) { - if (keyeq(name, table->v[i]->name)) - return table->v[i]; - if (!step) - step = PROBE_STEP(h, mask, table->power); - i < step ? (i += table->size - step) : (i -= step); - } - if (!createSize) - return NULL; - - /* check for overflow (table is half full) */ - if (table->used >> (table->power - 1)) { - unsigned char newPower = table->power + 1; - size_t newSize = (size_t)1 << newPower; - unsigned long newMask = (unsigned long)newSize - 1; - size_t tsize = newSize * sizeof(NAMED *); - NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); - if (!newV) - return NULL; - memset(newV, 0, tsize); - for (i = 0; i < table->size; i++) - if (table->v[i]) { - unsigned long newHash = hash(parser, table->v[i]->name); - size_t j = newHash & newMask; - step = 0; - while (newV[j]) { - if (!step) - step = PROBE_STEP(newHash, newMask, newPower); - j < step ? (j += newSize - step) : (j -= step); - } - newV[j] = table->v[i]; - } - table->mem->free_fcn(table->v); - table->v = newV; - table->power = newPower; - table->size = newSize; - i = h & newMask; - step = 0; - while (table->v[i]) { - if (!step) - step = PROBE_STEP(h, newMask, newPower); - i < step ? (i += newSize - step) : (i -= step); - } - } - } - table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); - if (!table->v[i]) - return NULL; - memset(table->v[i], 0, createSize); - table->v[i]->name = name; - (table->used)++; - return table->v[i]; -} - -static void FASTCALL -hashTableClear(HASH_TABLE *table) -{ - size_t i; - for (i = 0; i < table->size; i++) { - table->mem->free_fcn(table->v[i]); - table->v[i] = NULL; - } - table->used = 0; -} - -static void FASTCALL -hashTableDestroy(HASH_TABLE *table) -{ - size_t i; - for (i = 0; i < table->size; i++) - table->mem->free_fcn(table->v[i]); - table->mem->free_fcn(table->v); -} - -static void FASTCALL -hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) -{ - p->power = 0; - p->size = 0; - p->used = 0; - p->v = NULL; - p->mem = ms; -} - -static void FASTCALL -hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) -{ - iter->p = table->v; - iter->end = iter->p + table->size; -} - -static NAMED * FASTCALL -hashTableIterNext(HASH_TABLE_ITER *iter) -{ - while (iter->p != iter->end) { - NAMED *tem = *(iter->p)++; - if (tem) - return tem; - } - return NULL; -} - -static void FASTCALL -poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) -{ - pool->blocks = NULL; - pool->freeBlocks = NULL; - pool->start = NULL; - pool->ptr = NULL; - pool->end = NULL; - pool->mem = ms; -} - -static void FASTCALL -poolClear(STRING_POOL *pool) -{ - if (!pool->freeBlocks) - pool->freeBlocks = pool->blocks; - else { - BLOCK *p = pool->blocks; - while (p) { - BLOCK *tem = p->next; - p->next = pool->freeBlocks; - pool->freeBlocks = p; - p = tem; - } - } - pool->blocks = NULL; - pool->start = NULL; - pool->ptr = NULL; - pool->end = NULL; -} - -static void FASTCALL -poolDestroy(STRING_POOL *pool) -{ - BLOCK *p = pool->blocks; - while (p) { - BLOCK *tem = p->next; - pool->mem->free_fcn(p); - p = tem; - } - p = pool->freeBlocks; - while (p) { - BLOCK *tem = p->next; - pool->mem->free_fcn(p); - p = tem; - } -} - -static XML_Char * -poolAppend(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end) -{ - if (!pool->ptr && !poolGrow(pool)) - return NULL; - for (;;) { - XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); - if (ptr == end) - break; - if (!poolGrow(pool)) - return NULL; - } - return pool->start; -} - -static const XML_Char * FASTCALL -poolCopyString(STRING_POOL *pool, const XML_Char *s) -{ - do { - if (!poolAppendChar(pool, *s)) - return NULL; - } while (*s++); - s = pool->start; - poolFinish(pool); - return s; -} - -static const XML_Char * -poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) -{ - if (!pool->ptr && !poolGrow(pool)) - return NULL; - for (; n > 0; --n, s++) { - if (!poolAppendChar(pool, *s)) - return NULL; - } - s = pool->start; - poolFinish(pool); - return s; -} - -static const XML_Char * FASTCALL -poolAppendString(STRING_POOL *pool, const XML_Char *s) -{ - while (*s) { - if (!poolAppendChar(pool, *s)) - return NULL; - s++; - } - return pool->start; -} - -static XML_Char * -poolStoreString(STRING_POOL *pool, const ENCODING *enc, - const char *ptr, const char *end) -{ - if (!poolAppend(pool, enc, ptr, end)) - return NULL; - if (pool->ptr == pool->end && !poolGrow(pool)) - return NULL; - *(pool->ptr)++ = 0; - return pool->start; -} - -static XML_Bool FASTCALL -poolGrow(STRING_POOL *pool) -{ - if (pool->freeBlocks) { - if (pool->start == 0) { - pool->blocks = pool->freeBlocks; - pool->freeBlocks = pool->freeBlocks->next; - pool->blocks->next = NULL; - pool->start = pool->blocks->s; - pool->end = pool->start + pool->blocks->size; - pool->ptr = pool->start; - return XML_TRUE; - } - if (pool->end - pool->start < pool->freeBlocks->size) { - BLOCK *tem = pool->freeBlocks->next; - pool->freeBlocks->next = pool->blocks; - pool->blocks = pool->freeBlocks; - pool->freeBlocks = tem; - memcpy(pool->blocks->s, pool->start, - (pool->end - pool->start) * sizeof(XML_Char)); - pool->ptr = pool->blocks->s + (pool->ptr - pool->start); - pool->start = pool->blocks->s; - pool->end = pool->start + pool->blocks->size; - return XML_TRUE; - } - } - if (pool->blocks && pool->start == pool->blocks->s) { - int blockSize = (int)(pool->end - pool->start)*2; - BLOCK *temp = (BLOCK *) - pool->mem->realloc_fcn(pool->blocks, - (offsetof(BLOCK, s) - + blockSize * sizeof(XML_Char))); - if (temp == NULL) - return XML_FALSE; - pool->blocks = temp; - pool->blocks->size = blockSize; - pool->ptr = pool->blocks->s + (pool->ptr - pool->start); - pool->start = pool->blocks->s; - pool->end = pool->start + blockSize; - } - else { - BLOCK *tem; - int blockSize = (int)(pool->end - pool->start); - if (blockSize < INIT_BLOCK_SIZE) - blockSize = INIT_BLOCK_SIZE; - else - blockSize *= 2; - tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) - + blockSize * sizeof(XML_Char)); - if (!tem) - return XML_FALSE; - tem->size = blockSize; - tem->next = pool->blocks; - pool->blocks = tem; - if (pool->ptr != pool->start) - memcpy(tem->s, pool->start, - (pool->ptr - pool->start) * sizeof(XML_Char)); - pool->ptr = tem->s + (pool->ptr - pool->start); - pool->start = tem->s; - pool->end = tem->s + blockSize; - } - return XML_TRUE; -} - -static int FASTCALL -nextScaffoldPart(XML_Parser parser) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - CONTENT_SCAFFOLD * me; - int next; - - if (!dtd->scaffIndex) { - dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); - if (!dtd->scaffIndex) - return -1; - dtd->scaffIndex[0] = 0; - } - - if (dtd->scaffCount >= dtd->scaffSize) { - CONTENT_SCAFFOLD *temp; - if (dtd->scaffold) { - temp = (CONTENT_SCAFFOLD *) - REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); - if (temp == NULL) - return -1; - dtd->scaffSize *= 2; - } - else { - temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS - * sizeof(CONTENT_SCAFFOLD)); - if (temp == NULL) - return -1; - dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; - } - dtd->scaffold = temp; - } - next = dtd->scaffCount++; - me = &dtd->scaffold[next]; - if (dtd->scaffLevel) { - CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; - if (parent->lastchild) { - dtd->scaffold[parent->lastchild].nextsib = next; - } - if (!parent->childcnt) - parent->firstchild = next; - parent->lastchild = next; - parent->childcnt++; - } - me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; - return next; -} - -static void -build_node(XML_Parser parser, - int src_node, - XML_Content *dest, - XML_Content **contpos, - XML_Char **strpos) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - dest->type = dtd->scaffold[src_node].type; - dest->quant = dtd->scaffold[src_node].quant; - if (dest->type == XML_CTYPE_NAME) { - const XML_Char *src; - dest->name = *strpos; - src = dtd->scaffold[src_node].name; - for (;;) { - *(*strpos)++ = *src; - if (!*src) - break; - src++; - } - dest->numchildren = 0; - dest->children = NULL; - } - else { - unsigned int i; - int cn; - dest->numchildren = dtd->scaffold[src_node].childcnt; - dest->children = *contpos; - *contpos += dest->numchildren; - for (i = 0, cn = dtd->scaffold[src_node].firstchild; - i < dest->numchildren; - i++, cn = dtd->scaffold[cn].nextsib) { - build_node(parser, cn, &(dest->children[i]), contpos, strpos); - } - dest->name = NULL; - } -} - -static XML_Content * -build_model (XML_Parser parser) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - XML_Content *ret; - XML_Content *cpos; - XML_Char * str; - int allocsize = (dtd->scaffCount * sizeof(XML_Content) - + (dtd->contentStringLen * sizeof(XML_Char))); - - ret = (XML_Content *)MALLOC(allocsize); - if (!ret) - return NULL; - - str = (XML_Char *) (&ret[dtd->scaffCount]); - cpos = &ret[1]; - - build_node(parser, 0, ret, &cpos, &str); - return ret; -} - -static ELEMENT_TYPE * -getElementType(XML_Parser parser, - const ENCODING *enc, - const char *ptr, - const char *end) -{ - DTD * const dtd = _dtd; /* save one level of indirection */ - const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); - ELEMENT_TYPE *ret; - - if (!name) - return NULL; - ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); - if (!ret) - return NULL; - if (ret->name != name) - poolDiscard(&dtd->pool); - else { - poolFinish(&dtd->pool); - if (!setElementTypePrefix(parser, ret)) - return NULL; - } - return ret; -} diff --git a/apps/nesemu2/src/cartdb/expat/xmlrole.c b/apps/nesemu2/src/cartdb/expat/xmlrole.c deleted file mode 100644 index d854f949..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmlrole.c +++ /dev/null @@ -1,1324 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include - -#include "intconfig.h" - -#include "expat_external.h" -#include "internal.h" -#include "xmlrole.h" -#include "ascii.h" - -/* Doesn't check: - - that ,| are not mixed in a model group - content of literals - -*/ - -static const char KW_ANY[] = { - ASCII_A, ASCII_N, ASCII_Y, '\0' }; -static const char KW_ATTLIST[] = { - ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; -static const char KW_CDATA[] = { - ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; -static const char KW_DOCTYPE[] = { - ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; -static const char KW_ELEMENT[] = { - ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; -static const char KW_EMPTY[] = { - ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; -static const char KW_ENTITIES[] = { - ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, - '\0' }; -static const char KW_ENTITY[] = { - ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; -static const char KW_FIXED[] = { - ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; -static const char KW_ID[] = { - ASCII_I, ASCII_D, '\0' }; -static const char KW_IDREF[] = { - ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; -static const char KW_IDREFS[] = { - ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; -#ifdef XML_DTD -static const char KW_IGNORE[] = { - ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; -#endif -static const char KW_IMPLIED[] = { - ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; -#ifdef XML_DTD -static const char KW_INCLUDE[] = { - ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; -#endif -static const char KW_NDATA[] = { - ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; -static const char KW_NMTOKEN[] = { - ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; -static const char KW_NMTOKENS[] = { - ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, - '\0' }; -static const char KW_NOTATION[] = - { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, - '\0' }; -static const char KW_PCDATA[] = { - ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; -static const char KW_PUBLIC[] = { - ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; -static const char KW_REQUIRED[] = { - ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, - '\0' }; -static const char KW_SYSTEM[] = { - ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; - -#ifndef MIN_BYTES_PER_CHAR -#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) -#endif - -#ifdef XML_DTD -#define setTopLevel(state) \ - ((state)->handler = ((state)->documentEntity \ - ? internalSubset \ - : externalSubset1)) -#else /* not XML_DTD */ -#define setTopLevel(state) ((state)->handler = internalSubset) -#endif /* not XML_DTD */ - -typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc); - -static PROLOG_HANDLER - prolog0, prolog1, prolog2, - doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, - internalSubset, - entity0, entity1, entity2, entity3, entity4, entity5, entity6, - entity7, entity8, entity9, entity10, - notation0, notation1, notation2, notation3, notation4, - attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, - attlist7, attlist8, attlist9, - element0, element1, element2, element3, element4, element5, element6, - element7, -#ifdef XML_DTD - externalSubset0, externalSubset1, - condSect0, condSect1, condSect2, -#endif /* XML_DTD */ - declClose, - error; - -static int FASTCALL common(PROLOG_STATE *state, int tok); - -static int PTRCALL -prolog0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - state->handler = prolog1; - return XML_ROLE_NONE; - case XML_TOK_XML_DECL: - state->handler = prolog1; - return XML_ROLE_XML_DECL; - case XML_TOK_PI: - state->handler = prolog1; - return XML_ROLE_PI; - case XML_TOK_COMMENT: - state->handler = prolog1; - return XML_ROLE_COMMENT; - case XML_TOK_BOM: - return XML_ROLE_NONE; - case XML_TOK_DECL_OPEN: - if (!XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_DOCTYPE)) - break; - state->handler = doctype0; - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_INSTANCE_START: - state->handler = error; - return XML_ROLE_INSTANCE_START; - } - return common(state, tok); -} - -static int PTRCALL -prolog1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_PI: - return XML_ROLE_PI; - case XML_TOK_COMMENT: - return XML_ROLE_COMMENT; - case XML_TOK_BOM: - return XML_ROLE_NONE; - case XML_TOK_DECL_OPEN: - if (!XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_DOCTYPE)) - break; - state->handler = doctype0; - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_INSTANCE_START: - state->handler = error; - return XML_ROLE_INSTANCE_START; - } - return common(state, tok); -} - -static int PTRCALL -prolog2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_PI: - return XML_ROLE_PI; - case XML_TOK_COMMENT: - return XML_ROLE_COMMENT; - case XML_TOK_INSTANCE_START: - state->handler = error; - return XML_ROLE_INSTANCE_START; - } - return common(state, tok); -} - -static int PTRCALL -doctype0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = doctype1; - return XML_ROLE_DOCTYPE_NAME; - } - return common(state, tok); -} - -static int PTRCALL -doctype1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_OPEN_BRACKET: - state->handler = internalSubset; - return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; - case XML_TOK_DECL_CLOSE: - state->handler = prolog2; - return XML_ROLE_DOCTYPE_CLOSE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { - state->handler = doctype3; - return XML_ROLE_DOCTYPE_NONE; - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { - state->handler = doctype2; - return XML_ROLE_DOCTYPE_NONE; - } - break; - } - return common(state, tok); -} - -static int PTRCALL -doctype2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_LITERAL: - state->handler = doctype3; - return XML_ROLE_DOCTYPE_PUBLIC_ID; - } - return common(state, tok); -} - -static int PTRCALL -doctype3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_LITERAL: - state->handler = doctype4; - return XML_ROLE_DOCTYPE_SYSTEM_ID; - } - return common(state, tok); -} - -static int PTRCALL -doctype4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_OPEN_BRACKET: - state->handler = internalSubset; - return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; - case XML_TOK_DECL_CLOSE: - state->handler = prolog2; - return XML_ROLE_DOCTYPE_CLOSE; - } - return common(state, tok); -} - -static int PTRCALL -doctype5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_DECL_CLOSE: - state->handler = prolog2; - return XML_ROLE_DOCTYPE_CLOSE; - } - return common(state, tok); -} - -static int PTRCALL -internalSubset(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_DECL_OPEN: - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_ENTITY)) { - state->handler = entity0; - return XML_ROLE_ENTITY_NONE; - } - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_ATTLIST)) { - state->handler = attlist0; - return XML_ROLE_ATTLIST_NONE; - } - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_ELEMENT)) { - state->handler = element0; - return XML_ROLE_ELEMENT_NONE; - } - if (XmlNameMatchesAscii(enc, - ptr + 2 * MIN_BYTES_PER_CHAR(enc), - end, - KW_NOTATION)) { - state->handler = notation0; - return XML_ROLE_NOTATION_NONE; - } - break; - case XML_TOK_PI: - return XML_ROLE_PI; - case XML_TOK_COMMENT: - return XML_ROLE_COMMENT; - case XML_TOK_PARAM_ENTITY_REF: - return XML_ROLE_PARAM_ENTITY_REF; - case XML_TOK_CLOSE_BRACKET: - state->handler = doctype5; - return XML_ROLE_DOCTYPE_NONE; - case XML_TOK_NONE: - return XML_ROLE_NONE; - } - return common(state, tok); -} - -#ifdef XML_DTD - -static int PTRCALL -externalSubset0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - state->handler = externalSubset1; - if (tok == XML_TOK_XML_DECL) - return XML_ROLE_TEXT_DECL; - return externalSubset1(state, tok, ptr, end, enc); -} - -static int PTRCALL -externalSubset1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_COND_SECT_OPEN: - state->handler = condSect0; - return XML_ROLE_NONE; - case XML_TOK_COND_SECT_CLOSE: - if (state->includeLevel == 0) - break; - state->includeLevel -= 1; - return XML_ROLE_NONE; - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_CLOSE_BRACKET: - break; - case XML_TOK_NONE: - if (state->includeLevel) - break; - return XML_ROLE_NONE; - default: - return internalSubset(state, tok, ptr, end, enc); - } - return common(state, tok); -} - -#endif /* XML_DTD */ - -static int PTRCALL -entity0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_PERCENT: - state->handler = entity1; - return XML_ROLE_ENTITY_NONE; - case XML_TOK_NAME: - state->handler = entity2; - return XML_ROLE_GENERAL_ENTITY_NAME; - } - return common(state, tok); -} - -static int PTRCALL -entity1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_NAME: - state->handler = entity7; - return XML_ROLE_PARAM_ENTITY_NAME; - } - return common(state, tok); -} - -static int PTRCALL -entity2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { - state->handler = entity4; - return XML_ROLE_ENTITY_NONE; - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { - state->handler = entity3; - return XML_ROLE_ENTITY_NONE; - } - break; - case XML_TOK_LITERAL: - state->handler = declClose; - state->role_none = XML_ROLE_ENTITY_NONE; - return XML_ROLE_ENTITY_VALUE; - } - return common(state, tok); -} - -static int PTRCALL -entity3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_LITERAL: - state->handler = entity4; - return XML_ROLE_ENTITY_PUBLIC_ID; - } - return common(state, tok); -} - -static int PTRCALL -entity4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_LITERAL: - state->handler = entity5; - return XML_ROLE_ENTITY_SYSTEM_ID; - } - return common(state, tok); -} - -static int PTRCALL -entity5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_DECL_CLOSE: - setTopLevel(state); - return XML_ROLE_ENTITY_COMPLETE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { - state->handler = entity6; - return XML_ROLE_ENTITY_NONE; - } - break; - } - return common(state, tok); -} - -static int PTRCALL -entity6(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_NAME: - state->handler = declClose; - state->role_none = XML_ROLE_ENTITY_NONE; - return XML_ROLE_ENTITY_NOTATION_NAME; - } - return common(state, tok); -} - -static int PTRCALL -entity7(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { - state->handler = entity9; - return XML_ROLE_ENTITY_NONE; - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { - state->handler = entity8; - return XML_ROLE_ENTITY_NONE; - } - break; - case XML_TOK_LITERAL: - state->handler = declClose; - state->role_none = XML_ROLE_ENTITY_NONE; - return XML_ROLE_ENTITY_VALUE; - } - return common(state, tok); -} - -static int PTRCALL -entity8(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_LITERAL: - state->handler = entity9; - return XML_ROLE_ENTITY_PUBLIC_ID; - } - return common(state, tok); -} - -static int PTRCALL -entity9(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_LITERAL: - state->handler = entity10; - return XML_ROLE_ENTITY_SYSTEM_ID; - } - return common(state, tok); -} - -static int PTRCALL -entity10(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ENTITY_NONE; - case XML_TOK_DECL_CLOSE: - setTopLevel(state); - return XML_ROLE_ENTITY_COMPLETE; - } - return common(state, tok); -} - -static int PTRCALL -notation0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NOTATION_NONE; - case XML_TOK_NAME: - state->handler = notation1; - return XML_ROLE_NOTATION_NAME; - } - return common(state, tok); -} - -static int PTRCALL -notation1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NOTATION_NONE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { - state->handler = notation3; - return XML_ROLE_NOTATION_NONE; - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { - state->handler = notation2; - return XML_ROLE_NOTATION_NONE; - } - break; - } - return common(state, tok); -} - -static int PTRCALL -notation2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NOTATION_NONE; - case XML_TOK_LITERAL: - state->handler = notation4; - return XML_ROLE_NOTATION_PUBLIC_ID; - } - return common(state, tok); -} - -static int PTRCALL -notation3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NOTATION_NONE; - case XML_TOK_LITERAL: - state->handler = declClose; - state->role_none = XML_ROLE_NOTATION_NONE; - return XML_ROLE_NOTATION_SYSTEM_ID; - } - return common(state, tok); -} - -static int PTRCALL -notation4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NOTATION_NONE; - case XML_TOK_LITERAL: - state->handler = declClose; - state->role_none = XML_ROLE_NOTATION_NONE; - return XML_ROLE_NOTATION_SYSTEM_ID; - case XML_TOK_DECL_CLOSE: - setTopLevel(state); - return XML_ROLE_NOTATION_NO_SYSTEM_ID; - } - return common(state, tok); -} - -static int PTRCALL -attlist0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = attlist1; - return XML_ROLE_ATTLIST_ELEMENT_NAME; - } - return common(state, tok); -} - -static int PTRCALL -attlist1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_DECL_CLOSE: - setTopLevel(state); - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = attlist2; - return XML_ROLE_ATTRIBUTE_NAME; - } - return common(state, tok); -} - -static int PTRCALL -attlist2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_NAME: - { - static const char * const types[] = { - KW_CDATA, - KW_ID, - KW_IDREF, - KW_IDREFS, - KW_ENTITY, - KW_ENTITIES, - KW_NMTOKEN, - KW_NMTOKENS, - }; - int i; - for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) - if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { - state->handler = attlist8; - return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; - } - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { - state->handler = attlist5; - return XML_ROLE_ATTLIST_NONE; - } - break; - case XML_TOK_OPEN_PAREN: - state->handler = attlist3; - return XML_ROLE_ATTLIST_NONE; - } - return common(state, tok); -} - -static int PTRCALL -attlist3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_NMTOKEN: - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = attlist4; - return XML_ROLE_ATTRIBUTE_ENUM_VALUE; - } - return common(state, tok); -} - -static int PTRCALL -attlist4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_CLOSE_PAREN: - state->handler = attlist8; - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_OR: - state->handler = attlist3; - return XML_ROLE_ATTLIST_NONE; - } - return common(state, tok); -} - -static int PTRCALL -attlist5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_OPEN_PAREN: - state->handler = attlist6; - return XML_ROLE_ATTLIST_NONE; - } - return common(state, tok); -} - -static int PTRCALL -attlist6(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_NAME: - state->handler = attlist7; - return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; - } - return common(state, tok); -} - -static int PTRCALL -attlist7(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_CLOSE_PAREN: - state->handler = attlist8; - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_OR: - state->handler = attlist6; - return XML_ROLE_ATTLIST_NONE; - } - return common(state, tok); -} - -/* default value */ -static int PTRCALL -attlist8(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_POUND_NAME: - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, - KW_IMPLIED)) { - state->handler = attlist1; - return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; - } - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, - KW_REQUIRED)) { - state->handler = attlist1; - return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; - } - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, - KW_FIXED)) { - state->handler = attlist9; - return XML_ROLE_ATTLIST_NONE; - } - break; - case XML_TOK_LITERAL: - state->handler = attlist1; - return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; - } - return common(state, tok); -} - -static int PTRCALL -attlist9(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ATTLIST_NONE; - case XML_TOK_LITERAL: - state->handler = attlist1; - return XML_ROLE_FIXED_ATTRIBUTE_VALUE; - } - return common(state, tok); -} - -static int PTRCALL -element0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = element1; - return XML_ROLE_ELEMENT_NAME; - } - return common(state, tok); -} - -static int PTRCALL -element1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - return XML_ROLE_CONTENT_EMPTY; - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - return XML_ROLE_CONTENT_ANY; - } - break; - case XML_TOK_OPEN_PAREN: - state->handler = element2; - state->level = 1; - return XML_ROLE_GROUP_OPEN; - } - return common(state, tok); -} - -static int PTRCALL -element2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_POUND_NAME: - if (XmlNameMatchesAscii(enc, - ptr + MIN_BYTES_PER_CHAR(enc), - end, - KW_PCDATA)) { - state->handler = element3; - return XML_ROLE_CONTENT_PCDATA; - } - break; - case XML_TOK_OPEN_PAREN: - state->level = 2; - state->handler = element6; - return XML_ROLE_GROUP_OPEN; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT; - case XML_TOK_NAME_QUESTION: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT_OPT; - case XML_TOK_NAME_ASTERISK: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT_REP; - case XML_TOK_NAME_PLUS: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT_PLUS; - } - return common(state, tok); -} - -static int PTRCALL -element3(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_CLOSE_PAREN: - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - return XML_ROLE_GROUP_CLOSE; - case XML_TOK_CLOSE_PAREN_ASTERISK: - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - return XML_ROLE_GROUP_CLOSE_REP; - case XML_TOK_OR: - state->handler = element4; - return XML_ROLE_ELEMENT_NONE; - } - return common(state, tok); -} - -static int PTRCALL -element4(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = element5; - return XML_ROLE_CONTENT_ELEMENT; - } - return common(state, tok); -} - -static int PTRCALL -element5(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_CLOSE_PAREN_ASTERISK: - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - return XML_ROLE_GROUP_CLOSE_REP; - case XML_TOK_OR: - state->handler = element4; - return XML_ROLE_ELEMENT_NONE; - } - return common(state, tok); -} - -static int PTRCALL -element6(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_OPEN_PAREN: - state->level += 1; - return XML_ROLE_GROUP_OPEN; - case XML_TOK_NAME: - case XML_TOK_PREFIXED_NAME: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT; - case XML_TOK_NAME_QUESTION: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT_OPT; - case XML_TOK_NAME_ASTERISK: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT_REP; - case XML_TOK_NAME_PLUS: - state->handler = element7; - return XML_ROLE_CONTENT_ELEMENT_PLUS; - } - return common(state, tok); -} - -static int PTRCALL -element7(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_ELEMENT_NONE; - case XML_TOK_CLOSE_PAREN: - state->level -= 1; - if (state->level == 0) { - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - } - return XML_ROLE_GROUP_CLOSE; - case XML_TOK_CLOSE_PAREN_ASTERISK: - state->level -= 1; - if (state->level == 0) { - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - } - return XML_ROLE_GROUP_CLOSE_REP; - case XML_TOK_CLOSE_PAREN_QUESTION: - state->level -= 1; - if (state->level == 0) { - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - } - return XML_ROLE_GROUP_CLOSE_OPT; - case XML_TOK_CLOSE_PAREN_PLUS: - state->level -= 1; - if (state->level == 0) { - state->handler = declClose; - state->role_none = XML_ROLE_ELEMENT_NONE; - } - return XML_ROLE_GROUP_CLOSE_PLUS; - case XML_TOK_COMMA: - state->handler = element6; - return XML_ROLE_GROUP_SEQUENCE; - case XML_TOK_OR: - state->handler = element6; - return XML_ROLE_GROUP_CHOICE; - } - return common(state, tok); -} - -#ifdef XML_DTD - -static int PTRCALL -condSect0(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_NAME: - if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { - state->handler = condSect1; - return XML_ROLE_NONE; - } - if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { - state->handler = condSect2; - return XML_ROLE_NONE; - } - break; - } - return common(state, tok); -} - -static int PTRCALL -condSect1(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_OPEN_BRACKET: - state->handler = externalSubset1; - state->includeLevel += 1; - return XML_ROLE_NONE; - } - return common(state, tok); -} - -static int PTRCALL -condSect2(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return XML_ROLE_NONE; - case XML_TOK_OPEN_BRACKET: - state->handler = externalSubset1; - return XML_ROLE_IGNORE_SECT; - } - return common(state, tok); -} - -#endif /* XML_DTD */ - -static int PTRCALL -declClose(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - switch (tok) { - case XML_TOK_PROLOG_S: - return state->role_none; - case XML_TOK_DECL_CLOSE: - setTopLevel(state); - return state->role_none; - } - return common(state, tok); -} - -static int PTRCALL -error(PROLOG_STATE *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc) -{ - return XML_ROLE_NONE; -} - -static int FASTCALL -common(PROLOG_STATE *state, int tok) -{ -#ifdef XML_DTD - if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) - return XML_ROLE_INNER_PARAM_ENTITY_REF; -#endif - state->handler = error; - return XML_ROLE_ERROR; -} - -void -XmlPrologStateInit(PROLOG_STATE *state) -{ - state->handler = prolog0; -#ifdef XML_DTD - state->documentEntity = 1; - state->includeLevel = 0; - state->inEntityValue = 0; -#endif /* XML_DTD */ -} - -#ifdef XML_DTD - -void -XmlPrologStateInitExternalEntity(PROLOG_STATE *state) -{ - state->handler = externalSubset0; - state->documentEntity = 0; - state->includeLevel = 0; -} - -#endif /* XML_DTD */ diff --git a/apps/nesemu2/src/cartdb/expat/xmlrole.h b/apps/nesemu2/src/cartdb/expat/xmlrole.h deleted file mode 100644 index 0966364f..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmlrole.h +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#ifndef XmlRole_INCLUDED -#define XmlRole_INCLUDED 1 - -#ifdef __VMS -/* 0 1 2 3 0 1 2 3 - 1234567890123456789012345678901 1234567890123456789012345678901 */ -#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt -#endif - -#include "xmltok.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { - XML_ROLE_ERROR = -1, - XML_ROLE_NONE = 0, - XML_ROLE_XML_DECL, - XML_ROLE_INSTANCE_START, - XML_ROLE_DOCTYPE_NONE, - XML_ROLE_DOCTYPE_NAME, - XML_ROLE_DOCTYPE_SYSTEM_ID, - XML_ROLE_DOCTYPE_PUBLIC_ID, - XML_ROLE_DOCTYPE_INTERNAL_SUBSET, - XML_ROLE_DOCTYPE_CLOSE, - XML_ROLE_GENERAL_ENTITY_NAME, - XML_ROLE_PARAM_ENTITY_NAME, - XML_ROLE_ENTITY_NONE, - XML_ROLE_ENTITY_VALUE, - XML_ROLE_ENTITY_SYSTEM_ID, - XML_ROLE_ENTITY_PUBLIC_ID, - XML_ROLE_ENTITY_COMPLETE, - XML_ROLE_ENTITY_NOTATION_NAME, - XML_ROLE_NOTATION_NONE, - XML_ROLE_NOTATION_NAME, - XML_ROLE_NOTATION_SYSTEM_ID, - XML_ROLE_NOTATION_NO_SYSTEM_ID, - XML_ROLE_NOTATION_PUBLIC_ID, - XML_ROLE_ATTRIBUTE_NAME, - XML_ROLE_ATTRIBUTE_TYPE_CDATA, - XML_ROLE_ATTRIBUTE_TYPE_ID, - XML_ROLE_ATTRIBUTE_TYPE_IDREF, - XML_ROLE_ATTRIBUTE_TYPE_IDREFS, - XML_ROLE_ATTRIBUTE_TYPE_ENTITY, - XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, - XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, - XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, - XML_ROLE_ATTRIBUTE_ENUM_VALUE, - XML_ROLE_ATTRIBUTE_NOTATION_VALUE, - XML_ROLE_ATTLIST_NONE, - XML_ROLE_ATTLIST_ELEMENT_NAME, - XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, - XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, - XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, - XML_ROLE_FIXED_ATTRIBUTE_VALUE, - XML_ROLE_ELEMENT_NONE, - XML_ROLE_ELEMENT_NAME, - XML_ROLE_CONTENT_ANY, - XML_ROLE_CONTENT_EMPTY, - XML_ROLE_CONTENT_PCDATA, - XML_ROLE_GROUP_OPEN, - XML_ROLE_GROUP_CLOSE, - XML_ROLE_GROUP_CLOSE_REP, - XML_ROLE_GROUP_CLOSE_OPT, - XML_ROLE_GROUP_CLOSE_PLUS, - XML_ROLE_GROUP_CHOICE, - XML_ROLE_GROUP_SEQUENCE, - XML_ROLE_CONTENT_ELEMENT, - XML_ROLE_CONTENT_ELEMENT_REP, - XML_ROLE_CONTENT_ELEMENT_OPT, - XML_ROLE_CONTENT_ELEMENT_PLUS, - XML_ROLE_PI, - XML_ROLE_COMMENT, -#ifdef XML_DTD - XML_ROLE_TEXT_DECL, - XML_ROLE_IGNORE_SECT, - XML_ROLE_INNER_PARAM_ENTITY_REF, -#endif /* XML_DTD */ - XML_ROLE_PARAM_ENTITY_REF -}; - -typedef struct prolog_state { - int (PTRCALL *handler) (struct prolog_state *state, - int tok, - const char *ptr, - const char *end, - const ENCODING *enc); - unsigned level; - int role_none; -#ifdef XML_DTD - unsigned includeLevel; - int documentEntity; - int inEntityValue; -#endif /* XML_DTD */ -} PROLOG_STATE; - -void XmlPrologStateInit(PROLOG_STATE *); -#ifdef XML_DTD -void XmlPrologStateInitExternalEntity(PROLOG_STATE *); -#endif /* XML_DTD */ - -#define XmlTokenRole(state, tok, ptr, end, enc) \ - (((state)->handler)(state, tok, ptr, end, enc)) - -#ifdef __cplusplus -} -#endif - -#endif /* not XmlRole_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok.c b/apps/nesemu2/src/cartdb/expat/xmltok.c deleted file mode 100644 index 3c3fb697..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmltok.c +++ /dev/null @@ -1,1639 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#include - -#include "intconfig.h" - -#include "expat_external.h" -#include "internal.h" -#include "xmltok.h" -#include "nametab.h" - -#ifdef XML_DTD -#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) -#else -#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ -#endif - -#define VTABLE1 \ - { PREFIX(prologTok), PREFIX(contentTok), \ - PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ - { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ - PREFIX(sameName), \ - PREFIX(nameMatchesAscii), \ - PREFIX(nameLength), \ - PREFIX(skipS), \ - PREFIX(getAtts), \ - PREFIX(charRefNumber), \ - PREFIX(predefinedEntityName), \ - PREFIX(updatePosition), \ - PREFIX(isPublicId) - -#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) - -#define UCS2_GET_NAMING(pages, hi, lo) \ - (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) - -/* A 2 byte UTF-8 representation splits the characters 11 bits between - the bottom 5 and 6 bits of the bytes. We need 8 bits to index into - pages, 3 bits to add to that index and 5 bits to generate the mask. -*/ -#define UTF8_GET_NAMING2(pages, byte) \ - (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ - + ((((byte)[0]) & 3) << 1) \ - + ((((byte)[1]) >> 5) & 1)] \ - & (1 << (((byte)[1]) & 0x1F))) - -/* A 3 byte UTF-8 representation splits the characters 16 bits between - the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index - into pages, 3 bits to add to that index and 5 bits to generate the - mask. -*/ -#define UTF8_GET_NAMING3(pages, byte) \ - (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ - + ((((byte)[1]) >> 2) & 0xF)] \ - << 3) \ - + ((((byte)[1]) & 3) << 1) \ - + ((((byte)[2]) >> 5) & 1)] \ - & (1 << (((byte)[2]) & 0x1F))) - -#define UTF8_GET_NAMING(pages, p, n) \ - ((n) == 2 \ - ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ - : ((n) == 3 \ - ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ - : 0)) - -/* Detection of invalid UTF-8 sequences is based on Table 3.1B - of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ - with the additional restriction of not allowing the Unicode - code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). - Implementation details: - (A & 0x80) == 0 means A < 0x80 - and - (A & 0xC0) == 0xC0 means A > 0xBF -*/ - -#define UTF8_INVALID2(p) \ - ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) - -#define UTF8_INVALID3(p) \ - (((p)[2] & 0x80) == 0 \ - || \ - ((*p) == 0xEF && (p)[1] == 0xBF \ - ? \ - (p)[2] > 0xBD \ - : \ - ((p)[2] & 0xC0) == 0xC0) \ - || \ - ((*p) == 0xE0 \ - ? \ - (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ - : \ - ((p)[1] & 0x80) == 0 \ - || \ - ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) - -#define UTF8_INVALID4(p) \ - (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ - || \ - ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ - || \ - ((*p) == 0xF0 \ - ? \ - (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ - : \ - ((p)[1] & 0x80) == 0 \ - || \ - ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) - -static int PTRFASTCALL -isNever(const ENCODING *enc, const char *p) -{ - return 0; -} - -static int PTRFASTCALL -utf8_isName2(const ENCODING *enc, const char *p) -{ - return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); -} - -static int PTRFASTCALL -utf8_isName3(const ENCODING *enc, const char *p) -{ - return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); -} - -#define utf8_isName4 isNever - -static int PTRFASTCALL -utf8_isNmstrt2(const ENCODING *enc, const char *p) -{ - return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); -} - -static int PTRFASTCALL -utf8_isNmstrt3(const ENCODING *enc, const char *p) -{ - return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); -} - -#define utf8_isNmstrt4 isNever - -static int PTRFASTCALL -utf8_isInvalid2(const ENCODING *enc, const char *p) -{ - return UTF8_INVALID2((const unsigned char *)p); -} - -static int PTRFASTCALL -utf8_isInvalid3(const ENCODING *enc, const char *p) -{ - return UTF8_INVALID3((const unsigned char *)p); -} - -static int PTRFASTCALL -utf8_isInvalid4(const ENCODING *enc, const char *p) -{ - return UTF8_INVALID4((const unsigned char *)p); -} - -struct normal_encoding { - ENCODING enc; - unsigned char type[256]; -#ifdef XML_MIN_SIZE - int (PTRFASTCALL *byteType)(const ENCODING *, const char *); - int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); - int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); - int (PTRCALL *charMatches)(const ENCODING *, const char *, int); -#endif /* XML_MIN_SIZE */ - int (PTRFASTCALL *isName2)(const ENCODING *, const char *); - int (PTRFASTCALL *isName3)(const ENCODING *, const char *); - int (PTRFASTCALL *isName4)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); - int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); - int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); - int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); - int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); -}; - -#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) - -#ifdef XML_MIN_SIZE - -#define STANDARD_VTABLE(E) \ - E ## byteType, \ - E ## isNameMin, \ - E ## isNmstrtMin, \ - E ## byteToAscii, \ - E ## charMatches, - -#else - -#define STANDARD_VTABLE(E) /* as nothing */ - -#endif - -#define NORMAL_VTABLE(E) \ - E ## isName2, \ - E ## isName3, \ - E ## isName4, \ - E ## isNmstrt2, \ - E ## isNmstrt3, \ - E ## isNmstrt4, \ - E ## isInvalid2, \ - E ## isInvalid3, \ - E ## isInvalid4 - -static int FASTCALL checkCharRefNumber(int); - -#include "xmltok_impl.h" -#include "ascii.h" - -#ifdef XML_MIN_SIZE -#define sb_isNameMin isNever -#define sb_isNmstrtMin isNever -#endif - -#ifdef XML_MIN_SIZE -#define MINBPC(enc) ((enc)->minBytesPerChar) -#else -/* minimum bytes per character */ -#define MINBPC(enc) 1 -#endif - -#define SB_BYTE_TYPE(enc, p) \ - (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) - -#ifdef XML_MIN_SIZE -static int PTRFASTCALL -sb_byteType(const ENCODING *enc, const char *p) -{ - return SB_BYTE_TYPE(enc, p); -} -#define BYTE_TYPE(enc, p) \ - (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) -#else -#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) -#endif - -#ifdef XML_MIN_SIZE -#define BYTE_TO_ASCII(enc, p) \ - (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) -static int PTRFASTCALL -sb_byteToAscii(const ENCODING *enc, const char *p) -{ - return *p; -} -#else -#define BYTE_TO_ASCII(enc, p) (*(p)) -#endif - -#define IS_NAME_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) -#define IS_NMSTRT_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) -#define IS_INVALID_CHAR(enc, p, n) \ - (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) - -#ifdef XML_MIN_SIZE -#define IS_NAME_CHAR_MINBPC(enc, p) \ - (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ - (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) -#else -#define IS_NAME_CHAR_MINBPC(enc, p) (0) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) -#endif - -#ifdef XML_MIN_SIZE -#define CHAR_MATCHES(enc, p, c) \ - (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) -static int PTRCALL -sb_charMatches(const ENCODING *enc, const char *p, int c) -{ - return *p == c; -} -#else -/* c is an ASCII character */ -#define CHAR_MATCHES(enc, p, c) (*(p) == c) -#endif - -#define PREFIX(ident) normal_ ## ident -#define XML_TOK_IMPL_C -#include "xmltok_impl.c" -#undef XML_TOK_IMPL_C - -#undef MINBPC -#undef BYTE_TYPE -#undef BYTE_TO_ASCII -#undef CHAR_MATCHES -#undef IS_NAME_CHAR -#undef IS_NAME_CHAR_MINBPC -#undef IS_NMSTRT_CHAR -#undef IS_NMSTRT_CHAR_MINBPC -#undef IS_INVALID_CHAR - -enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ - UTF8_cval1 = 0x00, - UTF8_cval2 = 0xc0, - UTF8_cval3 = 0xe0, - UTF8_cval4 = 0xf0 -}; - -static void PTRCALL -utf8_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ - char *to; - const char *from; - if (fromLim - *fromP > toLim - *toP) { - /* Avoid copying partial characters. */ - for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) - if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) - break; - } - for (to = *toP, from = *fromP; from != fromLim; from++, to++) - *to = *from; - *fromP = from; - *toP = to; -} - -static void PTRCALL -utf8_toUtf16(const ENCODING *enc, - const char **fromP, const char *fromLim, - unsigned short **toP, const unsigned short *toLim) -{ - unsigned short *to = *toP; - const char *from = *fromP; - while (from != fromLim && to != toLim) { - switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { - case BT_LEAD2: - *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); - from += 2; - break; - case BT_LEAD3: - *to++ = (unsigned short)(((from[0] & 0xf) << 12) - | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); - from += 3; - break; - case BT_LEAD4: - { - unsigned long n; - if (to + 1 == toLim) - goto after; - n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) - | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); - n -= 0x10000; - to[0] = (unsigned short)((n >> 10) | 0xD800); - to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); - to += 2; - from += 4; - } - break; - default: - *to++ = *from++; - break; - } - } -after: - *fromP = from; - *toP = to; -} - -#ifdef XML_NS -static const struct normal_encoding utf8_encoding_ns = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { -#include "asciitab.h" -#include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; -#endif - -static const struct normal_encoding utf8_encoding = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { -#define BT_COLON BT_NMSTRT -#include "asciitab.h" -#undef BT_COLON -#include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; - -#ifdef XML_NS - -static const struct normal_encoding internal_utf8_encoding_ns = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { -#include "iasciitab.h" -#include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; - -#endif - -static const struct normal_encoding internal_utf8_encoding = { - { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, - { -#define BT_COLON BT_NMSTRT -#include "iasciitab.h" -#undef BT_COLON -#include "utf8tab.h" - }, - STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) -}; - -static void PTRCALL -latin1_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ - for (;;) { - unsigned char c; - if (*fromP == fromLim) - break; - c = (unsigned char)**fromP; - if (c & 0x80) { - if (toLim - *toP < 2) - break; - *(*toP)++ = (char)((c >> 6) | UTF8_cval2); - *(*toP)++ = (char)((c & 0x3f) | 0x80); - (*fromP)++; - } - else { - if (*toP == toLim) - break; - *(*toP)++ = *(*fromP)++; - } - } -} - -static void PTRCALL -latin1_toUtf16(const ENCODING *enc, - const char **fromP, const char *fromLim, - unsigned short **toP, const unsigned short *toLim) -{ - while (*fromP != fromLim && *toP != toLim) - *(*toP)++ = (unsigned char)*(*fromP)++; -} - -#ifdef XML_NS - -static const struct normal_encoding latin1_encoding_ns = { - { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, - { -#include "asciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(sb_) -}; - -#endif - -static const struct normal_encoding latin1_encoding = { - { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, - { -#define BT_COLON BT_NMSTRT -#include "asciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(sb_) -}; - -static void PTRCALL -ascii_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ - while (*fromP != fromLim && *toP != toLim) - *(*toP)++ = *(*fromP)++; -} - -#ifdef XML_NS - -static const struct normal_encoding ascii_encoding_ns = { - { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, - { -#include "asciitab.h" -/* BT_NONXML == 0 */ - }, - STANDARD_VTABLE(sb_) -}; - -#endif - -static const struct normal_encoding ascii_encoding = { - { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, - { -#define BT_COLON BT_NMSTRT -#include "asciitab.h" -#undef BT_COLON -/* BT_NONXML == 0 */ - }, - STANDARD_VTABLE(sb_) -}; - -static int PTRFASTCALL -unicode_byte_type(char hi, char lo) -{ - switch ((unsigned char)hi) { - case 0xD8: case 0xD9: case 0xDA: case 0xDB: - return BT_LEAD4; - case 0xDC: case 0xDD: case 0xDE: case 0xDF: - return BT_TRAIL; - case 0xFF: - switch ((unsigned char)lo) { - case 0xFF: - case 0xFE: - return BT_NONXML; - } - break; - } - return BT_NONASCII; -} - -#define DEFINE_UTF16_TO_UTF8(E) \ -static void PTRCALL \ -E ## toUtf8(const ENCODING *enc, \ - const char **fromP, const char *fromLim, \ - char **toP, const char *toLim) \ -{ \ - const char *from; \ - for (from = *fromP; from != fromLim; from += 2) { \ - int plane; \ - unsigned char lo2; \ - unsigned char lo = GET_LO(from); \ - unsigned char hi = GET_HI(from); \ - switch (hi) { \ - case 0: \ - if (lo < 0x80) { \ - if (*toP == toLim) { \ - *fromP = from; \ - return; \ - } \ - *(*toP)++ = lo; \ - break; \ - } \ - /* fall through */ \ - case 0x1: case 0x2: case 0x3: \ - case 0x4: case 0x5: case 0x6: case 0x7: \ - if (toLim - *toP < 2) { \ - *fromP = from; \ - return; \ - } \ - *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ - *(*toP)++ = ((lo & 0x3f) | 0x80); \ - break; \ - default: \ - if (toLim - *toP < 3) { \ - *fromP = from; \ - return; \ - } \ - /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ - *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ - *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ - *(*toP)++ = ((lo & 0x3f) | 0x80); \ - break; \ - case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ - if (toLim - *toP < 4) { \ - *fromP = from; \ - return; \ - } \ - plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ - *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ - *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ - from += 2; \ - lo2 = GET_LO(from); \ - *(*toP)++ = (((lo & 0x3) << 4) \ - | ((GET_HI(from) & 0x3) << 2) \ - | (lo2 >> 6) \ - | 0x80); \ - *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ - break; \ - } \ - } \ - *fromP = from; \ -} - -#define DEFINE_UTF16_TO_UTF16(E) \ -static void PTRCALL \ -E ## toUtf16(const ENCODING *enc, \ - const char **fromP, const char *fromLim, \ - unsigned short **toP, const unsigned short *toLim) \ -{ \ - /* Avoid copying first half only of surrogate */ \ - if (fromLim - *fromP > ((toLim - *toP) << 1) \ - && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ - fromLim -= 2; \ - for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ - *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ -} - -#define SET2(ptr, ch) \ - (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) -#define GET_LO(ptr) ((unsigned char)(ptr)[0]) -#define GET_HI(ptr) ((unsigned char)(ptr)[1]) - -DEFINE_UTF16_TO_UTF8(little2_) -DEFINE_UTF16_TO_UTF16(little2_) - -#undef SET2 -#undef GET_LO -#undef GET_HI - -#define SET2(ptr, ch) \ - (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) -#define GET_LO(ptr) ((unsigned char)(ptr)[1]) -#define GET_HI(ptr) ((unsigned char)(ptr)[0]) - -DEFINE_UTF16_TO_UTF8(big2_) -DEFINE_UTF16_TO_UTF16(big2_) - -#undef SET2 -#undef GET_LO -#undef GET_HI - -#define LITTLE2_BYTE_TYPE(enc, p) \ - ((p)[1] == 0 \ - ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ - : unicode_byte_type((p)[1], (p)[0])) -#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) -#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) -#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ - UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) -#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ - UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) - -#ifdef XML_MIN_SIZE - -static int PTRFASTCALL -little2_byteType(const ENCODING *enc, const char *p) -{ - return LITTLE2_BYTE_TYPE(enc, p); -} - -static int PTRFASTCALL -little2_byteToAscii(const ENCODING *enc, const char *p) -{ - return LITTLE2_BYTE_TO_ASCII(enc, p); -} - -static int PTRCALL -little2_charMatches(const ENCODING *enc, const char *p, int c) -{ - return LITTLE2_CHAR_MATCHES(enc, p, c); -} - -static int PTRFASTCALL -little2_isNameMin(const ENCODING *enc, const char *p) -{ - return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); -} - -static int PTRFASTCALL -little2_isNmstrtMin(const ENCODING *enc, const char *p) -{ - return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); -} - -#undef VTABLE -#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 - -#else /* not XML_MIN_SIZE */ - -#undef PREFIX -#define PREFIX(ident) little2_ ## ident -#define MINBPC(enc) 2 -/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ -#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) -#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) -#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) -#define IS_NAME_CHAR(enc, p, n) 0 -#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) -#define IS_NMSTRT_CHAR(enc, p, n) (0) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) - -#define XML_TOK_IMPL_C -#include "xmltok_impl.c" -#undef XML_TOK_IMPL_C - -#undef MINBPC -#undef BYTE_TYPE -#undef BYTE_TO_ASCII -#undef CHAR_MATCHES -#undef IS_NAME_CHAR -#undef IS_NAME_CHAR_MINBPC -#undef IS_NMSTRT_CHAR -#undef IS_NMSTRT_CHAR_MINBPC -#undef IS_INVALID_CHAR - -#endif /* not XML_MIN_SIZE */ - -#ifdef XML_NS - -static const struct normal_encoding little2_encoding_ns = { - { VTABLE, 2, 0, -#if BYTEORDER == 1234 - 1 -#else - 0 -#endif - }, - { -#include "asciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; - -#endif - -static const struct normal_encoding little2_encoding = { - { VTABLE, 2, 0, -#if BYTEORDER == 1234 - 1 -#else - 0 -#endif - }, - { -#define BT_COLON BT_NMSTRT -#include "asciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; - -#if BYTEORDER != 4321 - -#ifdef XML_NS - -static const struct normal_encoding internal_little2_encoding_ns = { - { VTABLE, 2, 0, 1 }, - { -#include "iasciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; - -#endif - -static const struct normal_encoding internal_little2_encoding = { - { VTABLE, 2, 0, 1 }, - { -#define BT_COLON BT_NMSTRT -#include "iasciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(little2_) -}; - -#endif - - -#define BIG2_BYTE_TYPE(enc, p) \ - ((p)[0] == 0 \ - ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ - : unicode_byte_type((p)[0], (p)[1])) -#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) -#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) -#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ - UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) -#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ - UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) - -#ifdef XML_MIN_SIZE - -static int PTRFASTCALL -big2_byteType(const ENCODING *enc, const char *p) -{ - return BIG2_BYTE_TYPE(enc, p); -} - -static int PTRFASTCALL -big2_byteToAscii(const ENCODING *enc, const char *p) -{ - return BIG2_BYTE_TO_ASCII(enc, p); -} - -static int PTRCALL -big2_charMatches(const ENCODING *enc, const char *p, int c) -{ - return BIG2_CHAR_MATCHES(enc, p, c); -} - -static int PTRFASTCALL -big2_isNameMin(const ENCODING *enc, const char *p) -{ - return BIG2_IS_NAME_CHAR_MINBPC(enc, p); -} - -static int PTRFASTCALL -big2_isNmstrtMin(const ENCODING *enc, const char *p) -{ - return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); -} - -#undef VTABLE -#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 - -#else /* not XML_MIN_SIZE */ - -#undef PREFIX -#define PREFIX(ident) big2_ ## ident -#define MINBPC(enc) 2 -/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ -#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) -#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) -#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) -#define IS_NAME_CHAR(enc, p, n) 0 -#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) -#define IS_NMSTRT_CHAR(enc, p, n) (0) -#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) - -#define XML_TOK_IMPL_C -#include "xmltok_impl.c" -#undef XML_TOK_IMPL_C - -#undef MINBPC -#undef BYTE_TYPE -#undef BYTE_TO_ASCII -#undef CHAR_MATCHES -#undef IS_NAME_CHAR -#undef IS_NAME_CHAR_MINBPC -#undef IS_NMSTRT_CHAR -#undef IS_NMSTRT_CHAR_MINBPC -#undef IS_INVALID_CHAR - -#endif /* not XML_MIN_SIZE */ - -#ifdef XML_NS - -static const struct normal_encoding big2_encoding_ns = { - { VTABLE, 2, 0, -#if BYTEORDER == 4321 - 1 -#else - 0 -#endif - }, - { -#include "asciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; - -#endif - -static const struct normal_encoding big2_encoding = { - { VTABLE, 2, 0, -#if BYTEORDER == 4321 - 1 -#else - 0 -#endif - }, - { -#define BT_COLON BT_NMSTRT -#include "asciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; - -#if BYTEORDER != 1234 - -#ifdef XML_NS - -static const struct normal_encoding internal_big2_encoding_ns = { - { VTABLE, 2, 0, 1 }, - { -#include "iasciitab.h" -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; - -#endif - -static const struct normal_encoding internal_big2_encoding = { - { VTABLE, 2, 0, 1 }, - { -#define BT_COLON BT_NMSTRT -#include "iasciitab.h" -#undef BT_COLON -#include "latin1tab.h" - }, - STANDARD_VTABLE(big2_) -}; - -#endif - -#undef PREFIX - -static int FASTCALL -streqci(const char *s1, const char *s2) -{ - for (;;) { - char c1 = *s1++; - char c2 = *s2++; - if (ASCII_a <= c1 && c1 <= ASCII_z) - c1 += ASCII_A - ASCII_a; - if (ASCII_a <= c2 && c2 <= ASCII_z) - c2 += ASCII_A - ASCII_a; - if (c1 != c2) - return 0; - if (!c1) - break; - } - return 1; -} - -static void PTRCALL -initUpdatePosition(const ENCODING *enc, const char *ptr, - const char *end, POSITION *pos) -{ - normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); -} - -static int -toAscii(const ENCODING *enc, const char *ptr, const char *end) -{ - char buf[1]; - char *p = buf; - XmlUtf8Convert(enc, &ptr, end, &p, p + 1); - if (p == buf) - return -1; - else - return buf[0]; -} - -static int FASTCALL -isSpace(int c) -{ - switch (c) { - case 0x20: - case 0xD: - case 0xA: - case 0x9: - return 1; - } - return 0; -} - -/* Return 1 if there's just optional white space or there's an S - followed by name=val. -*/ -static int -parsePseudoAttribute(const ENCODING *enc, - const char *ptr, - const char *end, - const char **namePtr, - const char **nameEndPtr, - const char **valPtr, - const char **nextTokPtr) -{ - int c; - char open; - if (ptr == end) { - *namePtr = NULL; - return 1; - } - if (!isSpace(toAscii(enc, ptr, end))) { - *nextTokPtr = ptr; - return 0; - } - do { - ptr += enc->minBytesPerChar; - } while (isSpace(toAscii(enc, ptr, end))); - if (ptr == end) { - *namePtr = NULL; - return 1; - } - *namePtr = ptr; - for (;;) { - c = toAscii(enc, ptr, end); - if (c == -1) { - *nextTokPtr = ptr; - return 0; - } - if (c == ASCII_EQUALS) { - *nameEndPtr = ptr; - break; - } - if (isSpace(c)) { - *nameEndPtr = ptr; - do { - ptr += enc->minBytesPerChar; - } while (isSpace(c = toAscii(enc, ptr, end))); - if (c != ASCII_EQUALS) { - *nextTokPtr = ptr; - return 0; - } - break; - } - ptr += enc->minBytesPerChar; - } - if (ptr == *namePtr) { - *nextTokPtr = ptr; - return 0; - } - ptr += enc->minBytesPerChar; - c = toAscii(enc, ptr, end); - while (isSpace(c)) { - ptr += enc->minBytesPerChar; - c = toAscii(enc, ptr, end); - } - if (c != ASCII_QUOT && c != ASCII_APOS) { - *nextTokPtr = ptr; - return 0; - } - open = (char)c; - ptr += enc->minBytesPerChar; - *valPtr = ptr; - for (;; ptr += enc->minBytesPerChar) { - c = toAscii(enc, ptr, end); - if (c == open) - break; - if (!(ASCII_a <= c && c <= ASCII_z) - && !(ASCII_A <= c && c <= ASCII_Z) - && !(ASCII_0 <= c && c <= ASCII_9) - && c != ASCII_PERIOD - && c != ASCII_MINUS - && c != ASCII_UNDERSCORE) { - *nextTokPtr = ptr; - return 0; - } - } - *nextTokPtr = ptr + enc->minBytesPerChar; - return 1; -} - -static const char KW_version[] = { - ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' -}; - -static const char KW_encoding[] = { - ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' -}; - -static const char KW_standalone[] = { - ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, - ASCII_n, ASCII_e, '\0' -}; - -static const char KW_yes[] = { - ASCII_y, ASCII_e, ASCII_s, '\0' -}; - -static const char KW_no[] = { - ASCII_n, ASCII_o, '\0' -}; - -static int -doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, - const char *, - const char *), - int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, - const char **encodingName, - const ENCODING **encoding, - int *standalone) -{ - const char *val = NULL; - const char *name = NULL; - const char *nameEnd = NULL; - ptr += 5 * enc->minBytesPerChar; - end -= 2 * enc->minBytesPerChar; - if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) - || !name) { - *badPtr = ptr; - return 0; - } - if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { - if (!isGeneralTextEntity) { - *badPtr = name; - return 0; - } - } - else { - if (versionPtr) - *versionPtr = val; - if (versionEndPtr) - *versionEndPtr = ptr; - if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { - *badPtr = ptr; - return 0; - } - if (!name) { - if (isGeneralTextEntity) { - /* a TextDecl must have an EncodingDecl */ - *badPtr = ptr; - return 0; - } - return 1; - } - } - if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { - int c = toAscii(enc, val, end); - if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { - *badPtr = val; - return 0; - } - if (encodingName) - *encodingName = val; - if (encoding) - *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); - if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { - *badPtr = ptr; - return 0; - } - if (!name) - return 1; - } - if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) - || isGeneralTextEntity) { - *badPtr = name; - return 0; - } - if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { - if (standalone) - *standalone = 1; - } - else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { - if (standalone) - *standalone = 0; - } - else { - *badPtr = val; - return 0; - } - while (isSpace(toAscii(enc, ptr, end))) - ptr += enc->minBytesPerChar; - if (ptr != end) { - *badPtr = ptr; - return 0; - } - return 1; -} - -static int FASTCALL -checkCharRefNumber(int result) -{ - switch (result >> 8) { - case 0xD8: case 0xD9: case 0xDA: case 0xDB: - case 0xDC: case 0xDD: case 0xDE: case 0xDF: - return -1; - case 0: - if (latin1_encoding.type[result] == BT_NONXML) - return -1; - break; - case 0xFF: - if (result == 0xFFFE || result == 0xFFFF) - return -1; - break; - } - return result; -} - -int FASTCALL -XmlUtf8Encode(int c, char *buf) -{ - enum { - /* minN is minimum legal resulting value for N byte sequence */ - min2 = 0x80, - min3 = 0x800, - min4 = 0x10000 - }; - - if (c < 0) - return 0; - if (c < min2) { - buf[0] = (char)(c | UTF8_cval1); - return 1; - } - if (c < min3) { - buf[0] = (char)((c >> 6) | UTF8_cval2); - buf[1] = (char)((c & 0x3f) | 0x80); - return 2; - } - if (c < min4) { - buf[0] = (char)((c >> 12) | UTF8_cval3); - buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); - buf[2] = (char)((c & 0x3f) | 0x80); - return 3; - } - if (c < 0x110000) { - buf[0] = (char)((c >> 18) | UTF8_cval4); - buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); - buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); - buf[3] = (char)((c & 0x3f) | 0x80); - return 4; - } - return 0; -} - -int FASTCALL -XmlUtf16Encode(int charNum, unsigned short *buf) -{ - if (charNum < 0) - return 0; - if (charNum < 0x10000) { - buf[0] = (unsigned short)charNum; - return 1; - } - if (charNum < 0x110000) { - charNum -= 0x10000; - buf[0] = (unsigned short)((charNum >> 10) + 0xD800); - buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); - return 2; - } - return 0; -} - -struct unknown_encoding { - struct normal_encoding normal; - CONVERTER convert; - void *userData; - unsigned short utf16[256]; - char utf8[256][4]; -}; - -#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) - -int -XmlSizeOfUnknownEncoding(void) -{ - return sizeof(struct unknown_encoding); -} - -static int PTRFASTCALL -unknown_isName(const ENCODING *enc, const char *p) -{ - const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); - int c = uenc->convert(uenc->userData, p); - if (c & ~0xFFFF) - return 0; - return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); -} - -static int PTRFASTCALL -unknown_isNmstrt(const ENCODING *enc, const char *p) -{ - const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); - int c = uenc->convert(uenc->userData, p); - if (c & ~0xFFFF) - return 0; - return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); -} - -static int PTRFASTCALL -unknown_isInvalid(const ENCODING *enc, const char *p) -{ - const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); - int c = uenc->convert(uenc->userData, p); - return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; -} - -static void PTRCALL -unknown_toUtf8(const ENCODING *enc, - const char **fromP, const char *fromLim, - char **toP, const char *toLim) -{ - const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); - char buf[XML_UTF8_ENCODE_MAX]; - for (;;) { - const char *utf8; - int n; - if (*fromP == fromLim) - break; - utf8 = uenc->utf8[(unsigned char)**fromP]; - n = *utf8++; - if (n == 0) { - int c = uenc->convert(uenc->userData, *fromP); - n = XmlUtf8Encode(c, buf); - if (n > toLim - *toP) - break; - utf8 = buf; - *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] - - (BT_LEAD2 - 2)); - } - else { - if (n > toLim - *toP) - break; - (*fromP)++; - } - do { - *(*toP)++ = *utf8++; - } while (--n != 0); - } -} - -static void PTRCALL -unknown_toUtf16(const ENCODING *enc, - const char **fromP, const char *fromLim, - unsigned short **toP, const unsigned short *toLim) -{ - const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); - while (*fromP != fromLim && *toP != toLim) { - unsigned short c = uenc->utf16[(unsigned char)**fromP]; - if (c == 0) { - c = (unsigned short) - uenc->convert(uenc->userData, *fromP); - *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] - - (BT_LEAD2 - 2)); - } - else - (*fromP)++; - *(*toP)++ = c; - } -} - -ENCODING * -XmlInitUnknownEncoding(void *mem, - int *table, - CONVERTER convert, - void *userData) -{ - int i; - struct unknown_encoding *e = (struct unknown_encoding *)mem; - for (i = 0; i < (int)sizeof(struct normal_encoding); i++) - ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; - for (i = 0; i < 128; i++) - if (latin1_encoding.type[i] != BT_OTHER - && latin1_encoding.type[i] != BT_NONXML - && table[i] != i) - return 0; - for (i = 0; i < 256; i++) { - int c = table[i]; - if (c == -1) { - e->normal.type[i] = BT_MALFORM; - /* This shouldn't really get used. */ - e->utf16[i] = 0xFFFF; - e->utf8[i][0] = 1; - e->utf8[i][1] = 0; - } - else if (c < 0) { - if (c < -4) - return 0; - e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); - e->utf8[i][0] = 0; - e->utf16[i] = 0; - } - else if (c < 0x80) { - if (latin1_encoding.type[c] != BT_OTHER - && latin1_encoding.type[c] != BT_NONXML - && c != i) - return 0; - e->normal.type[i] = latin1_encoding.type[c]; - e->utf8[i][0] = 1; - e->utf8[i][1] = (char)c; - e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); - } - else if (checkCharRefNumber(c) < 0) { - e->normal.type[i] = BT_NONXML; - /* This shouldn't really get used. */ - e->utf16[i] = 0xFFFF; - e->utf8[i][0] = 1; - e->utf8[i][1] = 0; - } - else { - if (c > 0xFFFF) - return 0; - if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) - e->normal.type[i] = BT_NMSTRT; - else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) - e->normal.type[i] = BT_NAME; - else - e->normal.type[i] = BT_OTHER; - e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); - e->utf16[i] = (unsigned short)c; - } - } - e->userData = userData; - e->convert = convert; - if (convert) { - e->normal.isName2 = unknown_isName; - e->normal.isName3 = unknown_isName; - e->normal.isName4 = unknown_isName; - e->normal.isNmstrt2 = unknown_isNmstrt; - e->normal.isNmstrt3 = unknown_isNmstrt; - e->normal.isNmstrt4 = unknown_isNmstrt; - e->normal.isInvalid2 = unknown_isInvalid; - e->normal.isInvalid3 = unknown_isInvalid; - e->normal.isInvalid4 = unknown_isInvalid; - } - e->normal.enc.utf8Convert = unknown_toUtf8; - e->normal.enc.utf16Convert = unknown_toUtf16; - return &(e->normal.enc); -} - -/* If this enumeration is changed, getEncodingIndex and encodings -must also be changed. */ -enum { - UNKNOWN_ENC = -1, - ISO_8859_1_ENC = 0, - US_ASCII_ENC, - UTF_8_ENC, - UTF_16_ENC, - UTF_16BE_ENC, - UTF_16LE_ENC, - /* must match encodingNames up to here */ - NO_ENC -}; - -static const char KW_ISO_8859_1[] = { - ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, - ASCII_MINUS, ASCII_1, '\0' -}; -static const char KW_US_ASCII[] = { - ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, - '\0' -}; -static const char KW_UTF_8[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' -}; -static const char KW_UTF_16[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' -}; -static const char KW_UTF_16BE[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, - '\0' -}; -static const char KW_UTF_16LE[] = { - ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, - '\0' -}; - -static int FASTCALL -getEncodingIndex(const char *name) -{ - static const char * const encodingNames[] = { - KW_ISO_8859_1, - KW_US_ASCII, - KW_UTF_8, - KW_UTF_16, - KW_UTF_16BE, - KW_UTF_16LE, - }; - int i; - if (name == NULL) - return NO_ENC; - for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) - if (streqci(name, encodingNames[i])) - return i; - return UNKNOWN_ENC; -} - -/* For binary compatibility, we store the index of the encoding - specified at initialization in the isUtf16 member. -*/ - -#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) -#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) - -/* This is what detects the encoding. encodingTable maps from - encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of - the external (protocol) specified encoding; state is - XML_CONTENT_STATE if we're parsing an external text entity, and - XML_PROLOG_STATE otherwise. -*/ - - -static int -initScan(const ENCODING * const *encodingTable, - const INIT_ENCODING *enc, - int state, - const char *ptr, - const char *end, - const char **nextTokPtr) -{ - const ENCODING **encPtr; - - if (ptr == end) - return XML_TOK_NONE; - encPtr = enc->encPtr; - if (ptr + 1 == end) { - /* only a single byte available for auto-detection */ -#ifndef XML_DTD /* FIXME */ - /* a well-formed document entity must have more than one byte */ - if (state != XML_CONTENT_STATE) - return XML_TOK_PARTIAL; -#endif - /* so we're parsing an external text entity... */ - /* if UTF-16 was externally specified, then we need at least 2 bytes */ - switch (INIT_ENC_INDEX(enc)) { - case UTF_16_ENC: - case UTF_16LE_ENC: - case UTF_16BE_ENC: - return XML_TOK_PARTIAL; - } - switch ((unsigned char)*ptr) { - case 0xFE: - case 0xFF: - case 0xEF: /* possibly first byte of UTF-8 BOM */ - if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC - && state == XML_CONTENT_STATE) - break; - /* fall through */ - case 0x00: - case 0x3C: - return XML_TOK_PARTIAL; - } - } - else { - switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { - case 0xFEFF: - if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC - && state == XML_CONTENT_STATE) - break; - *nextTokPtr = ptr + 2; - *encPtr = encodingTable[UTF_16BE_ENC]; - return XML_TOK_BOM; - /* 00 3C is handled in the default case */ - case 0x3C00: - if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC - || INIT_ENC_INDEX(enc) == UTF_16_ENC) - && state == XML_CONTENT_STATE) - break; - *encPtr = encodingTable[UTF_16LE_ENC]; - return XmlTok(*encPtr, state, ptr, end, nextTokPtr); - case 0xFFFE: - if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC - && state == XML_CONTENT_STATE) - break; - *nextTokPtr = ptr + 2; - *encPtr = encodingTable[UTF_16LE_ENC]; - return XML_TOK_BOM; - case 0xEFBB: - /* Maybe a UTF-8 BOM (EF BB BF) */ - /* If there's an explicitly specified (external) encoding - of ISO-8859-1 or some flavour of UTF-16 - and this is an external text entity, - don't look for the BOM, - because it might be a legal data. - */ - if (state == XML_CONTENT_STATE) { - int e = INIT_ENC_INDEX(enc); - if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC - || e == UTF_16LE_ENC || e == UTF_16_ENC) - break; - } - if (ptr + 2 == end) - return XML_TOK_PARTIAL; - if ((unsigned char)ptr[2] == 0xBF) { - *nextTokPtr = ptr + 3; - *encPtr = encodingTable[UTF_8_ENC]; - return XML_TOK_BOM; - } - break; - default: - if (ptr[0] == '\0') { - /* 0 isn't a legal data character. Furthermore a document - entity can only start with ASCII characters. So the only - way this can fail to be big-endian UTF-16 if it it's an - external parsed general entity that's labelled as - UTF-16LE. - */ - if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) - break; - *encPtr = encodingTable[UTF_16BE_ENC]; - return XmlTok(*encPtr, state, ptr, end, nextTokPtr); - } - else if (ptr[1] == '\0') { - /* We could recover here in the case: - - parsing an external entity - - second byte is 0 - - no externally specified encoding - - no encoding declaration - by assuming UTF-16LE. But we don't, because this would mean when - presented just with a single byte, we couldn't reliably determine - whether we needed further bytes. - */ - if (state == XML_CONTENT_STATE) - break; - *encPtr = encodingTable[UTF_16LE_ENC]; - return XmlTok(*encPtr, state, ptr, end, nextTokPtr); - } - break; - } - } - *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; - return XmlTok(*encPtr, state, ptr, end, nextTokPtr); -} - - -#define NS(x) x -#define ns(x) x -#define XML_TOK_NS_C -#include "xmltok_ns.c" -#undef XML_TOK_NS_C -#undef NS -#undef ns - -#ifdef XML_NS - -#define NS(x) x ## NS -#define ns(x) x ## _ns - -#define XML_TOK_NS_C -#include "xmltok_ns.c" -#undef XML_TOK_NS_C - -#undef NS -#undef ns - -ENCODING * -XmlInitUnknownEncodingNS(void *mem, - int *table, - CONVERTER convert, - void *userData) -{ - ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); - if (enc) - ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; - return enc; -} - -#endif /* XML_NS */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok.h b/apps/nesemu2/src/cartdb/expat/xmltok.h deleted file mode 100644 index 8e50406b..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmltok.h +++ /dev/null @@ -1,316 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -#ifndef XmlTok_INCLUDED -#define XmlTok_INCLUDED 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* The following token may be returned by XmlContentTok */ -#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be - start of illegal ]]> sequence */ -/* The following tokens may be returned by both XmlPrologTok and - XmlContentTok. -*/ -#define XML_TOK_NONE -4 /* The string to be scanned is empty */ -#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; - might be part of CRLF sequence */ -#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ -#define XML_TOK_PARTIAL -1 /* only part of a token */ -#define XML_TOK_INVALID 0 - -/* The following tokens are returned by XmlContentTok; some are also - returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. -*/ -#define XML_TOK_START_TAG_WITH_ATTS 1 -#define XML_TOK_START_TAG_NO_ATTS 2 -#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ -#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 -#define XML_TOK_END_TAG 5 -#define XML_TOK_DATA_CHARS 6 -#define XML_TOK_DATA_NEWLINE 7 -#define XML_TOK_CDATA_SECT_OPEN 8 -#define XML_TOK_ENTITY_REF 9 -#define XML_TOK_CHAR_REF 10 /* numeric character reference */ - -/* The following tokens may be returned by both XmlPrologTok and - XmlContentTok. -*/ -#define XML_TOK_PI 11 /* processing instruction */ -#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ -#define XML_TOK_COMMENT 13 -#define XML_TOK_BOM 14 /* Byte order mark */ - -/* The following tokens are returned only by XmlPrologTok */ -#define XML_TOK_PROLOG_S 15 -#define XML_TOK_DECL_OPEN 16 /* */ -#define XML_TOK_NAME 18 -#define XML_TOK_NMTOKEN 19 -#define XML_TOK_POUND_NAME 20 /* #name */ -#define XML_TOK_OR 21 /* | */ -#define XML_TOK_PERCENT 22 -#define XML_TOK_OPEN_PAREN 23 -#define XML_TOK_CLOSE_PAREN 24 -#define XML_TOK_OPEN_BRACKET 25 -#define XML_TOK_CLOSE_BRACKET 26 -#define XML_TOK_LITERAL 27 -#define XML_TOK_PARAM_ENTITY_REF 28 -#define XML_TOK_INSTANCE_START 29 - -/* The following occur only in element type declarations */ -#define XML_TOK_NAME_QUESTION 30 /* name? */ -#define XML_TOK_NAME_ASTERISK 31 /* name* */ -#define XML_TOK_NAME_PLUS 32 /* name+ */ -#define XML_TOK_COND_SECT_OPEN 33 /* */ -#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ -#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ -#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ -#define XML_TOK_COMMA 38 - -/* The following token is returned only by XmlAttributeValueTok */ -#define XML_TOK_ATTRIBUTE_VALUE_S 39 - -/* The following token is returned only by XmlCdataSectionTok */ -#define XML_TOK_CDATA_SECT_CLOSE 40 - -/* With namespace processing this is returned by XmlPrologTok for a - name with a colon. -*/ -#define XML_TOK_PREFIXED_NAME 41 - -#ifdef XML_DTD -#define XML_TOK_IGNORE_SECT 42 -#endif /* XML_DTD */ - -#ifdef XML_DTD -#define XML_N_STATES 4 -#else /* not XML_DTD */ -#define XML_N_STATES 3 -#endif /* not XML_DTD */ - -#define XML_PROLOG_STATE 0 -#define XML_CONTENT_STATE 1 -#define XML_CDATA_SECTION_STATE 2 -#ifdef XML_DTD -#define XML_IGNORE_SECTION_STATE 3 -#endif /* XML_DTD */ - -#define XML_N_LITERAL_TYPES 2 -#define XML_ATTRIBUTE_VALUE_LITERAL 0 -#define XML_ENTITY_VALUE_LITERAL 1 - -/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ -#define XML_UTF8_ENCODE_MAX 4 -/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ -#define XML_UTF16_ENCODE_MAX 2 - -typedef struct position { - /* first line and first column are 0 not 1 */ - XML_Size lineNumber; - XML_Size columnNumber; -} POSITION; - -typedef struct { - const char *name; - const char *valuePtr; - const char *valueEnd; - char normalized; -} ATTRIBUTE; - -struct encoding; -typedef struct encoding ENCODING; - -typedef int (PTRCALL *SCANNER)(const ENCODING *, - const char *, - const char *, - const char **); - -struct encoding { - SCANNER scanners[XML_N_STATES]; - SCANNER literalScanners[XML_N_LITERAL_TYPES]; - int (PTRCALL *sameName)(const ENCODING *, - const char *, - const char *); - int (PTRCALL *nameMatchesAscii)(const ENCODING *, - const char *, - const char *, - const char *); - int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); - const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); - int (PTRCALL *getAtts)(const ENCODING *enc, - const char *ptr, - int attsMax, - ATTRIBUTE *atts); - int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); - int (PTRCALL *predefinedEntityName)(const ENCODING *, - const char *, - const char *); - void (PTRCALL *updatePosition)(const ENCODING *, - const char *ptr, - const char *end, - POSITION *); - int (PTRCALL *isPublicId)(const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr); - void (PTRCALL *utf8Convert)(const ENCODING *enc, - const char **fromP, - const char *fromLim, - char **toP, - const char *toLim); - void (PTRCALL *utf16Convert)(const ENCODING *enc, - const char **fromP, - const char *fromLim, - unsigned short **toP, - const unsigned short *toLim); - int minBytesPerChar; - char isUtf8; - char isUtf16; -}; - -/* Scan the string starting at ptr until the end of the next complete - token, but do not scan past eptr. Return an integer giving the - type of token. - - Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. - - Return XML_TOK_PARTIAL when the string does not contain a complete - token; nextTokPtr will not be set. - - Return XML_TOK_INVALID when the string does not start a valid - token; nextTokPtr will be set to point to the character which made - the token invalid. - - Otherwise the string starts with a valid token; nextTokPtr will be - set to point to the character following the end of that token. - - Each data character counts as a single token, but adjacent data - characters may be returned together. Similarly for characters in - the prolog outside literals, comments and processing instructions. -*/ - - -#define XmlTok(enc, state, ptr, end, nextTokPtr) \ - (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) - -#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) - -#define XmlContentTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) - -#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) - -#ifdef XML_DTD - -#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ - XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) - -#endif /* XML_DTD */ - -/* This is used for performing a 2nd-level tokenization on the content - of a literal that has already been returned by XmlTok. -*/ -#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ - (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) - -#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ - XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) - -#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ - XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) - -#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) - -#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ - (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) - -#define XmlNameLength(enc, ptr) \ - (((enc)->nameLength)(enc, ptr)) - -#define XmlSkipS(enc, ptr) \ - (((enc)->skipS)(enc, ptr)) - -#define XmlGetAttributes(enc, ptr, attsMax, atts) \ - (((enc)->getAtts)(enc, ptr, attsMax, atts)) - -#define XmlCharRefNumber(enc, ptr) \ - (((enc)->charRefNumber)(enc, ptr)) - -#define XmlPredefinedEntityName(enc, ptr, end) \ - (((enc)->predefinedEntityName)(enc, ptr, end)) - -#define XmlUpdatePosition(enc, ptr, end, pos) \ - (((enc)->updatePosition)(enc, ptr, end, pos)) - -#define XmlIsPublicId(enc, ptr, end, badPtr) \ - (((enc)->isPublicId)(enc, ptr, end, badPtr)) - -#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ - (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) - -#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ - (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) - -typedef struct { - ENCODING initEnc; - const ENCODING **encPtr; -} INIT_ENCODING; - -int XmlParseXmlDecl(int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, - const char **encodingNamePtr, - const ENCODING **namedEncodingPtr, - int *standalonePtr); - -int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); -const ENCODING *XmlGetUtf8InternalEncoding(void); -const ENCODING *XmlGetUtf16InternalEncoding(void); -int FASTCALL XmlUtf8Encode(int charNumber, char *buf); -int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); -int XmlSizeOfUnknownEncoding(void); - - -typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); - -ENCODING * -XmlInitUnknownEncoding(void *mem, - int *table, - CONVERTER convert, - void *userData); - -int XmlParseXmlDeclNS(int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, - const char **encodingNamePtr, - const ENCODING **namedEncodingPtr, - int *standalonePtr); - -int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); -const ENCODING *XmlGetUtf8InternalEncodingNS(void); -const ENCODING *XmlGetUtf16InternalEncodingNS(void); -ENCODING * -XmlInitUnknownEncodingNS(void *mem, - int *table, - CONVERTER convert, - void *userData); -#ifdef __cplusplus -} -#endif - -#endif /* not XmlTok_INCLUDED */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok_impl.c b/apps/nesemu2/src/cartdb/expat/xmltok_impl.c deleted file mode 100644 index 8223c68d..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmltok_impl.c +++ /dev/null @@ -1,1783 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -/* This file is included! */ -#ifdef XML_TOK_IMPL_C - -#ifndef IS_INVALID_CHAR -#define IS_INVALID_CHAR(enc, ptr, n) (0) -#endif - -#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (IS_INVALID_CHAR(enc, ptr, n)) { \ - *(nextTokPtr) = (ptr); \ - return XML_TOK_INVALID; \ - } \ - ptr += n; \ - break; - -#define INVALID_CASES(ptr, nextTokPtr) \ - INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ - INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ - INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ - case BT_NONXML: \ - case BT_MALFORM: \ - case BT_TRAIL: \ - *(nextTokPtr) = (ptr); \ - return XML_TOK_INVALID; - -#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (!IS_NAME_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - ptr += n; \ - break; - -#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ - case BT_NONASCII: \ - if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - case BT_NMSTRT: \ - case BT_HEX: \ - case BT_DIGIT: \ - case BT_NAME: \ - case BT_MINUS: \ - ptr += MINBPC(enc); \ - break; \ - CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ - CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ - CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) - -#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - ptr += n; \ - break; - -#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ - case BT_NONASCII: \ - if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; \ - } \ - case BT_NMSTRT: \ - case BT_HEX: \ - ptr += MINBPC(enc); \ - break; \ - CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ - CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ - CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) - -#ifndef PREFIX -#define PREFIX(ident) ident -#endif - -/* ptr points to character following " */ - switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { - case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - /* fall through */ - case BT_S: case BT_CR: case BT_LF: - *nextTokPtr = ptr; - return XML_TOK_DECL_OPEN; - case BT_NMSTRT: - case BT_HEX: - ptr += MINBPC(enc); - break; - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - } - return XML_TOK_PARTIAL; -} - -static int PTRCALL -PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, - const char *end, int *tokPtr) -{ - int upper = 0; - *tokPtr = XML_TOK_PI; - if (end - ptr != MINBPC(enc)*3) - return 1; - switch (BYTE_TO_ASCII(enc, ptr)) { - case ASCII_x: - break; - case ASCII_X: - upper = 1; - break; - default: - return 1; - } - ptr += MINBPC(enc); - switch (BYTE_TO_ASCII(enc, ptr)) { - case ASCII_m: - break; - case ASCII_M: - upper = 1; - break; - default: - return 1; - } - ptr += MINBPC(enc); - switch (BYTE_TO_ASCII(enc, ptr)) { - case ASCII_l: - break; - case ASCII_L: - upper = 1; - break; - default: - return 1; - } - if (upper) - return 0; - *tokPtr = XML_TOK_XML_DECL; - return 1; -} - -/* ptr points to character following " 1) { - size_t n = end - ptr; - if (n & (MINBPC(enc) - 1)) { - n &= ~(MINBPC(enc) - 1); - if (n == 0) - return XML_TOK_PARTIAL; - end = ptr + n; - } - } - switch (BYTE_TYPE(enc, ptr)) { - case BT_RSQB: - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) - break; - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { - ptr -= MINBPC(enc); - break; - } - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_CDATA_SECT_CLOSE; - case BT_CR: - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - if (BYTE_TYPE(enc, ptr) == BT_LF) - ptr += MINBPC(enc); - *nextTokPtr = ptr; - return XML_TOK_DATA_NEWLINE; - case BT_LF: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_DATA_NEWLINE; - INVALID_CASES(ptr, nextTokPtr) - default: - ptr += MINBPC(enc); - break; - } - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_DATA_CHARS; \ - } \ - ptr += n; \ - break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_NONXML: - case BT_MALFORM: - case BT_TRAIL: - case BT_CR: - case BT_LF: - case BT_RSQB: - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - default: - ptr += MINBPC(enc); - break; - } - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; -} - -/* ptr points to character following " 1) { - size_t n = end - ptr; - if (n & (MINBPC(enc) - 1)) { - n &= ~(MINBPC(enc) - 1); - if (n == 0) - return XML_TOK_PARTIAL; - end = ptr + n; - } - } - switch (BYTE_TYPE(enc, ptr)) { - case BT_LT: - return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_AMP: - return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_CR: - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_TRAILING_CR; - if (BYTE_TYPE(enc, ptr) == BT_LF) - ptr += MINBPC(enc); - *nextTokPtr = ptr; - return XML_TOK_DATA_NEWLINE; - case BT_LF: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_DATA_NEWLINE; - case BT_RSQB: - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_TRAILING_RSQB; - if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) - break; - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_TRAILING_RSQB; - if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { - ptr -= MINBPC(enc); - break; - } - *nextTokPtr = ptr; - return XML_TOK_INVALID; - INVALID_CASES(ptr, nextTokPtr) - default: - ptr += MINBPC(enc); - break; - } - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ - *nextTokPtr = ptr; \ - return XML_TOK_DATA_CHARS; \ - } \ - ptr += n; \ - break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_RSQB: - if (ptr + MINBPC(enc) != end) { - if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { - ptr += MINBPC(enc); - break; - } - if (ptr + 2*MINBPC(enc) != end) { - if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { - ptr += MINBPC(enc); - break; - } - *nextTokPtr = ptr + 2*MINBPC(enc); - return XML_TOK_INVALID; - } - } - /* fall through */ - case BT_AMP: - case BT_LT: - case BT_NONXML: - case BT_MALFORM: - case BT_TRAIL: - case BT_CR: - case BT_LF: - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - default: - ptr += MINBPC(enc); - break; - } - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; -} - -/* ptr points to character following "%" */ - -static int PTRCALL -PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - if (ptr == end) - return XML_TOK_PARTIAL; - switch (BYTE_TYPE(enc, ptr)) { - CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) - case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: - *nextTokPtr = ptr; - return XML_TOK_PERCENT; - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) - case BT_SEMI: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_PARAM_ENTITY_REF; - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - } - return XML_TOK_PARTIAL; -} - -static int PTRCALL -PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - if (ptr == end) - return XML_TOK_PARTIAL; - switch (BYTE_TYPE(enc, ptr)) { - CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) - case BT_CR: case BT_LF: case BT_S: - case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: - *nextTokPtr = ptr; - return XML_TOK_POUND_NAME; - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - } - return -XML_TOK_POUND_NAME; -} - -static int PTRCALL -PREFIX(scanLit)(int open, const ENCODING *enc, - const char *ptr, const char *end, - const char **nextTokPtr) -{ - while (ptr != end) { - int t = BYTE_TYPE(enc, ptr); - switch (t) { - INVALID_CASES(ptr, nextTokPtr) - case BT_QUOT: - case BT_APOS: - ptr += MINBPC(enc); - if (t != open) - break; - if (ptr == end) - return -XML_TOK_LITERAL; - *nextTokPtr = ptr; - switch (BYTE_TYPE(enc, ptr)) { - case BT_S: case BT_CR: case BT_LF: - case BT_GT: case BT_PERCNT: case BT_LSQB: - return XML_TOK_LITERAL; - default: - return XML_TOK_INVALID; - } - default: - ptr += MINBPC(enc); - break; - } - } - return XML_TOK_PARTIAL; -} - -static int PTRCALL -PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - int tok; - if (ptr == end) - return XML_TOK_NONE; - if (MINBPC(enc) > 1) { - size_t n = end - ptr; - if (n & (MINBPC(enc) - 1)) { - n &= ~(MINBPC(enc) - 1); - if (n == 0) - return XML_TOK_PARTIAL; - end = ptr + n; - } - } - switch (BYTE_TYPE(enc, ptr)) { - case BT_QUOT: - return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_APOS: - return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_LT: - { - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_PARTIAL; - switch (BYTE_TYPE(enc, ptr)) { - case BT_EXCL: - return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_QUEST: - return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_NMSTRT: - case BT_HEX: - case BT_NONASCII: - case BT_LEAD2: - case BT_LEAD3: - case BT_LEAD4: - *nextTokPtr = ptr - MINBPC(enc); - return XML_TOK_INSTANCE_START; - } - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - case BT_CR: - if (ptr + MINBPC(enc) == end) { - *nextTokPtr = end; - /* indicate that this might be part of a CR/LF pair */ - return -XML_TOK_PROLOG_S; - } - /* fall through */ - case BT_S: case BT_LF: - for (;;) { - ptr += MINBPC(enc); - if (ptr == end) - break; - switch (BYTE_TYPE(enc, ptr)) { - case BT_S: case BT_LF: - break; - case BT_CR: - /* don't split CR/LF pair */ - if (ptr + MINBPC(enc) != end) - break; - /* fall through */ - default: - *nextTokPtr = ptr; - return XML_TOK_PROLOG_S; - } - } - *nextTokPtr = ptr; - return XML_TOK_PROLOG_S; - case BT_PERCNT: - return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); - case BT_COMMA: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_COMMA; - case BT_LSQB: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_OPEN_BRACKET; - case BT_RSQB: - ptr += MINBPC(enc); - if (ptr == end) - return -XML_TOK_CLOSE_BRACKET; - if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { - if (ptr + MINBPC(enc) == end) - return XML_TOK_PARTIAL; - if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { - *nextTokPtr = ptr + 2*MINBPC(enc); - return XML_TOK_COND_SECT_CLOSE; - } - } - *nextTokPtr = ptr; - return XML_TOK_CLOSE_BRACKET; - case BT_LPAR: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_OPEN_PAREN; - case BT_RPAR: - ptr += MINBPC(enc); - if (ptr == end) - return -XML_TOK_CLOSE_PAREN; - switch (BYTE_TYPE(enc, ptr)) { - case BT_AST: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_CLOSE_PAREN_ASTERISK; - case BT_QUEST: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_CLOSE_PAREN_QUESTION; - case BT_PLUS: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_CLOSE_PAREN_PLUS; - case BT_CR: case BT_LF: case BT_S: - case BT_GT: case BT_COMMA: case BT_VERBAR: - case BT_RPAR: - *nextTokPtr = ptr; - return XML_TOK_CLOSE_PAREN; - } - *nextTokPtr = ptr; - return XML_TOK_INVALID; - case BT_VERBAR: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_OR; - case BT_GT: - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_DECL_CLOSE; - case BT_NUM: - return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (end - ptr < n) \ - return XML_TOK_PARTIAL_CHAR; \ - if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ - ptr += n; \ - tok = XML_TOK_NAME; \ - break; \ - } \ - if (IS_NAME_CHAR(enc, ptr, n)) { \ - ptr += n; \ - tok = XML_TOK_NMTOKEN; \ - break; \ - } \ - *nextTokPtr = ptr; \ - return XML_TOK_INVALID; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_NMSTRT: - case BT_HEX: - tok = XML_TOK_NAME; - ptr += MINBPC(enc); - break; - case BT_DIGIT: - case BT_NAME: - case BT_MINUS: -#ifdef XML_NS - case BT_COLON: -#endif - tok = XML_TOK_NMTOKEN; - ptr += MINBPC(enc); - break; - case BT_NONASCII: - if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { - ptr += MINBPC(enc); - tok = XML_TOK_NAME; - break; - } - if (IS_NAME_CHAR_MINBPC(enc, ptr)) { - ptr += MINBPC(enc); - tok = XML_TOK_NMTOKEN; - break; - } - /* fall through */ - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) - case BT_GT: case BT_RPAR: case BT_COMMA: - case BT_VERBAR: case BT_LSQB: case BT_PERCNT: - case BT_S: case BT_CR: case BT_LF: - *nextTokPtr = ptr; - return tok; -#ifdef XML_NS - case BT_COLON: - ptr += MINBPC(enc); - switch (tok) { - case XML_TOK_NAME: - if (ptr == end) - return XML_TOK_PARTIAL; - tok = XML_TOK_PREFIXED_NAME; - switch (BYTE_TYPE(enc, ptr)) { - CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) - default: - tok = XML_TOK_NMTOKEN; - break; - } - break; - case XML_TOK_PREFIXED_NAME: - tok = XML_TOK_NMTOKEN; - break; - } - break; -#endif - case BT_PLUS: - if (tok == XML_TOK_NMTOKEN) { - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_NAME_PLUS; - case BT_AST: - if (tok == XML_TOK_NMTOKEN) { - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_NAME_ASTERISK; - case BT_QUEST: - if (tok == XML_TOK_NMTOKEN) { - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_NAME_QUESTION; - default: - *nextTokPtr = ptr; - return XML_TOK_INVALID; - } - } - return -tok; -} - -static int PTRCALL -PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, - const char *end, const char **nextTokPtr) -{ - const char *start; - if (ptr == end) - return XML_TOK_NONE; - start = ptr; - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: ptr += n; break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_AMP: - if (ptr == start) - return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - case BT_LT: - /* this is for inside entity references */ - *nextTokPtr = ptr; - return XML_TOK_INVALID; - case BT_LF: - if (ptr == start) { - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_DATA_NEWLINE; - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - case BT_CR: - if (ptr == start) { - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_TRAILING_CR; - if (BYTE_TYPE(enc, ptr) == BT_LF) - ptr += MINBPC(enc); - *nextTokPtr = ptr; - return XML_TOK_DATA_NEWLINE; - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - case BT_S: - if (ptr == start) { - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_ATTRIBUTE_VALUE_S; - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - default: - ptr += MINBPC(enc); - break; - } - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; -} - -static int PTRCALL -PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, - const char *end, const char **nextTokPtr) -{ - const char *start; - if (ptr == end) - return XML_TOK_NONE; - start = ptr; - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: ptr += n; break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_AMP: - if (ptr == start) - return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - case BT_PERCNT: - if (ptr == start) { - int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), - end, nextTokPtr); - return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - case BT_LF: - if (ptr == start) { - *nextTokPtr = ptr + MINBPC(enc); - return XML_TOK_DATA_NEWLINE; - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - case BT_CR: - if (ptr == start) { - ptr += MINBPC(enc); - if (ptr == end) - return XML_TOK_TRAILING_CR; - if (BYTE_TYPE(enc, ptr) == BT_LF) - ptr += MINBPC(enc); - *nextTokPtr = ptr; - return XML_TOK_DATA_NEWLINE; - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; - default: - ptr += MINBPC(enc); - break; - } - } - *nextTokPtr = ptr; - return XML_TOK_DATA_CHARS; -} - -#ifdef XML_DTD - -static int PTRCALL -PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, - const char *end, const char **nextTokPtr) -{ - int level = 0; - if (MINBPC(enc) > 1) { - size_t n = end - ptr; - if (n & (MINBPC(enc) - 1)) { - n &= ~(MINBPC(enc) - 1); - end = ptr + n; - } - } - while (ptr != end) { - switch (BYTE_TYPE(enc, ptr)) { - INVALID_CASES(ptr, nextTokPtr) - case BT_LT: - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; - if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; - if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { - ++level; - ptr += MINBPC(enc); - } - } - break; - case BT_RSQB: - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; - if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { - if ((ptr += MINBPC(enc)) == end) - return XML_TOK_PARTIAL; - if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { - ptr += MINBPC(enc); - if (level == 0) { - *nextTokPtr = ptr; - return XML_TOK_IGNORE_SECT; - } - --level; - } - } - break; - default: - ptr += MINBPC(enc); - break; - } - } - return XML_TOK_PARTIAL; -} - -#endif /* XML_DTD */ - -static int PTRCALL -PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, - const char **badPtr) -{ - ptr += MINBPC(enc); - end -= MINBPC(enc); - for (; ptr != end; ptr += MINBPC(enc)) { - switch (BYTE_TYPE(enc, ptr)) { - case BT_DIGIT: - case BT_HEX: - case BT_MINUS: - case BT_APOS: - case BT_LPAR: - case BT_RPAR: - case BT_PLUS: - case BT_COMMA: - case BT_SOL: - case BT_EQUALS: - case BT_QUEST: - case BT_CR: - case BT_LF: - case BT_SEMI: - case BT_EXCL: - case BT_AST: - case BT_PERCNT: - case BT_NUM: -#ifdef XML_NS - case BT_COLON: -#endif - break; - case BT_S: - if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { - *badPtr = ptr; - return 0; - } - break; - case BT_NAME: - case BT_NMSTRT: - if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) - break; - default: - switch (BYTE_TO_ASCII(enc, ptr)) { - case 0x24: /* $ */ - case 0x40: /* @ */ - break; - default: - *badPtr = ptr; - return 0; - } - break; - } - } - return 1; -} - -/* This must only be called for a well-formed start-tag or empty - element tag. Returns the number of attributes. Pointers to the - first attsMax attributes are stored in atts. -*/ - -static int PTRCALL -PREFIX(getAtts)(const ENCODING *enc, const char *ptr, - int attsMax, ATTRIBUTE *atts) -{ - enum { other, inName, inValue } state = inName; - int nAtts = 0; - int open = 0; /* defined when state == inValue; - initialization just to shut up compilers */ - - for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { - switch (BYTE_TYPE(enc, ptr)) { -#define START_NAME \ - if (state == other) { \ - if (nAtts < attsMax) { \ - atts[nAtts].name = ptr; \ - atts[nAtts].normalized = 1; \ - } \ - state = inName; \ - } -#define LEAD_CASE(n) \ - case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_NONASCII: - case BT_NMSTRT: - case BT_HEX: - START_NAME - break; -#undef START_NAME - case BT_QUOT: - if (state != inValue) { - if (nAtts < attsMax) - atts[nAtts].valuePtr = ptr + MINBPC(enc); - state = inValue; - open = BT_QUOT; - } - else if (open == BT_QUOT) { - state = other; - if (nAtts < attsMax) - atts[nAtts].valueEnd = ptr; - nAtts++; - } - break; - case BT_APOS: - if (state != inValue) { - if (nAtts < attsMax) - atts[nAtts].valuePtr = ptr + MINBPC(enc); - state = inValue; - open = BT_APOS; - } - else if (open == BT_APOS) { - state = other; - if (nAtts < attsMax) - atts[nAtts].valueEnd = ptr; - nAtts++; - } - break; - case BT_AMP: - if (nAtts < attsMax) - atts[nAtts].normalized = 0; - break; - case BT_S: - if (state == inName) - state = other; - else if (state == inValue - && nAtts < attsMax - && atts[nAtts].normalized - && (ptr == atts[nAtts].valuePtr - || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE - || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE - || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) - atts[nAtts].normalized = 0; - break; - case BT_CR: case BT_LF: - /* This case ensures that the first attribute name is counted - Apart from that we could just change state on the quote. */ - if (state == inName) - state = other; - else if (state == inValue && nAtts < attsMax) - atts[nAtts].normalized = 0; - break; - case BT_GT: - case BT_SOL: - if (state != inValue) - return nAtts; - break; - default: - break; - } - } - /* not reached */ -} - -static int PTRFASTCALL -PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) -{ - int result = 0; - /* skip &# */ - ptr += 2*MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_x)) { - for (ptr += MINBPC(enc); - !CHAR_MATCHES(enc, ptr, ASCII_SEMI); - ptr += MINBPC(enc)) { - int c = BYTE_TO_ASCII(enc, ptr); - switch (c) { - case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: - case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: - result <<= 4; - result |= (c - ASCII_0); - break; - case ASCII_A: case ASCII_B: case ASCII_C: - case ASCII_D: case ASCII_E: case ASCII_F: - result <<= 4; - result += 10 + (c - ASCII_A); - break; - case ASCII_a: case ASCII_b: case ASCII_c: - case ASCII_d: case ASCII_e: case ASCII_f: - result <<= 4; - result += 10 + (c - ASCII_a); - break; - } - if (result >= 0x110000) - return -1; - } - } - else { - for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { - int c = BYTE_TO_ASCII(enc, ptr); - result *= 10; - result += (c - ASCII_0); - if (result >= 0x110000) - return -1; - } - } - return checkCharRefNumber(result); -} - -static int PTRCALL -PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, - const char *end) -{ - switch ((end - ptr)/MINBPC(enc)) { - case 2: - if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { - switch (BYTE_TO_ASCII(enc, ptr)) { - case ASCII_l: - return ASCII_LT; - case ASCII_g: - return ASCII_GT; - } - } - break; - case 3: - if (CHAR_MATCHES(enc, ptr, ASCII_a)) { - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_m)) { - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_p)) - return ASCII_AMP; - } - } - break; - case 4: - switch (BYTE_TO_ASCII(enc, ptr)) { - case ASCII_q: - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_u)) { - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_o)) { - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_t)) - return ASCII_QUOT; - } - } - break; - case ASCII_a: - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_p)) { - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_o)) { - ptr += MINBPC(enc); - if (CHAR_MATCHES(enc, ptr, ASCII_s)) - return ASCII_APOS; - } - } - break; - } - } - return 0; -} - -static int PTRCALL -PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) -{ - for (;;) { - switch (BYTE_TYPE(enc, ptr1)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - if (*ptr1++ != *ptr2++) \ - return 0; - LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) -#undef LEAD_CASE - /* fall through */ - if (*ptr1++ != *ptr2++) - return 0; - break; - case BT_NONASCII: - case BT_NMSTRT: -#ifdef XML_NS - case BT_COLON: -#endif - case BT_HEX: - case BT_DIGIT: - case BT_NAME: - case BT_MINUS: - if (*ptr2++ != *ptr1++) - return 0; - if (MINBPC(enc) > 1) { - if (*ptr2++ != *ptr1++) - return 0; - if (MINBPC(enc) > 2) { - if (*ptr2++ != *ptr1++) - return 0; - if (MINBPC(enc) > 3) { - if (*ptr2++ != *ptr1++) - return 0; - } - } - } - break; - default: - if (MINBPC(enc) == 1 && *ptr1 == *ptr2) - return 1; - switch (BYTE_TYPE(enc, ptr2)) { - case BT_LEAD2: - case BT_LEAD3: - case BT_LEAD4: - case BT_NONASCII: - case BT_NMSTRT: -#ifdef XML_NS - case BT_COLON: -#endif - case BT_HEX: - case BT_DIGIT: - case BT_NAME: - case BT_MINUS: - return 0; - default: - return 1; - } - } - } - /* not reached */ -} - -static int PTRCALL -PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, - const char *end1, const char *ptr2) -{ - for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { - if (ptr1 == end1) - return 0; - if (!CHAR_MATCHES(enc, ptr1, *ptr2)) - return 0; - } - return ptr1 == end1; -} - -static int PTRFASTCALL -PREFIX(nameLength)(const ENCODING *enc, const char *ptr) -{ - const char *start = ptr; - for (;;) { - switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: ptr += n; break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_NONASCII: - case BT_NMSTRT: -#ifdef XML_NS - case BT_COLON: -#endif - case BT_HEX: - case BT_DIGIT: - case BT_NAME: - case BT_MINUS: - ptr += MINBPC(enc); - break; - default: - return (int)(ptr - start); - } - } -} - -static const char * PTRFASTCALL -PREFIX(skipS)(const ENCODING *enc, const char *ptr) -{ - for (;;) { - switch (BYTE_TYPE(enc, ptr)) { - case BT_LF: - case BT_CR: - case BT_S: - ptr += MINBPC(enc); - break; - default: - return ptr; - } - } -} - -static void PTRCALL -PREFIX(updatePosition)(const ENCODING *enc, - const char *ptr, - const char *end, - POSITION *pos) -{ - while (ptr < end) { - switch (BYTE_TYPE(enc, ptr)) { -#define LEAD_CASE(n) \ - case BT_LEAD ## n: \ - ptr += n; \ - break; - LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) -#undef LEAD_CASE - case BT_LF: - pos->columnNumber = (XML_Size)-1; - pos->lineNumber++; - ptr += MINBPC(enc); - break; - case BT_CR: - pos->lineNumber++; - ptr += MINBPC(enc); - if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) - ptr += MINBPC(enc); - pos->columnNumber = (XML_Size)-1; - break; - default: - ptr += MINBPC(enc); - break; - } - pos->columnNumber++; - } -} - -#undef DO_LEAD_CASE -#undef MULTIBYTE_CASES -#undef INVALID_CASES -#undef CHECK_NAME_CASE -#undef CHECK_NAME_CASES -#undef CHECK_NMSTRT_CASE -#undef CHECK_NMSTRT_CASES - -#endif /* XML_TOK_IMPL_C */ diff --git a/apps/nesemu2/src/cartdb/expat/xmltok_impl.h b/apps/nesemu2/src/cartdb/expat/xmltok_impl.h deleted file mode 100644 index 9cbcc12d..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmltok_impl.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd -See the file COPYING for copying permission. -*/ - -enum { - BT_NONXML, - BT_MALFORM, - BT_LT, - BT_AMP, - BT_RSQB, - BT_LEAD2, - BT_LEAD3, - BT_LEAD4, - BT_TRAIL, - BT_CR, - BT_LF, - BT_GT, - BT_QUOT, - BT_APOS, - BT_EQUALS, - BT_QUEST, - BT_EXCL, - BT_SOL, - BT_SEMI, - BT_NUM, - BT_LSQB, - BT_S, - BT_NMSTRT, - BT_COLON, - BT_HEX, - BT_DIGIT, - BT_NAME, - BT_MINUS, - BT_OTHER, /* known not to be a name or name start character */ - BT_NONASCII, /* might be a name or name start character */ - BT_PERCNT, - BT_LPAR, - BT_RPAR, - BT_AST, - BT_PLUS, - BT_COMMA, - BT_VERBAR -}; - -#include diff --git a/apps/nesemu2/src/cartdb/expat/xmltok_ns.c b/apps/nesemu2/src/cartdb/expat/xmltok_ns.c deleted file mode 100644 index 94f8506d..00000000 --- a/apps/nesemu2/src/cartdb/expat/xmltok_ns.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd - See the file COPYING for copying permission. -*/ - -/* This file is included! */ -#ifdef XML_TOK_NS_C - -const ENCODING * -NS(XmlGetUtf8InternalEncoding)(void) -{ - return &ns(internal_utf8_encoding).enc; -} - -const ENCODING * -NS(XmlGetUtf16InternalEncoding)(void) -{ -#if BYTEORDER == 1234 - return &ns(internal_little2_encoding).enc; -#elif BYTEORDER == 4321 - return &ns(internal_big2_encoding).enc; -#else - const short n = 1; - return (*(const char *)&n - ? &ns(internal_little2_encoding).enc - : &ns(internal_big2_encoding).enc); -#endif -} - -static const ENCODING * const NS(encodings)[] = { - &ns(latin1_encoding).enc, - &ns(ascii_encoding).enc, - &ns(utf8_encoding).enc, - &ns(big2_encoding).enc, - &ns(big2_encoding).enc, - &ns(little2_encoding).enc, - &ns(utf8_encoding).enc /* NO_ENC */ -}; - -static int PTRCALL -NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - return initScan(NS(encodings), (const INIT_ENCODING *)enc, - XML_PROLOG_STATE, ptr, end, nextTokPtr); -} - -static int PTRCALL -NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, - const char **nextTokPtr) -{ - return initScan(NS(encodings), (const INIT_ENCODING *)enc, - XML_CONTENT_STATE, ptr, end, nextTokPtr); -} - -int -NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, - const char *name) -{ - int i = getEncodingIndex(name); - if (i == UNKNOWN_ENC) - return 0; - SET_INIT_ENC_INDEX(p, i); - p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); - p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); - p->initEnc.updatePosition = initUpdatePosition; - p->encPtr = encPtr; - *encPtr = &(p->initEnc); - return 1; -} - -static const ENCODING * -NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) -{ -#define ENCODING_MAX 128 - char buf[ENCODING_MAX]; - char *p = buf; - int i; - XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); - if (ptr != end) - return 0; - *p = 0; - if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) - return enc; - i = getEncodingIndex(buf); - if (i == UNKNOWN_ENC) - return 0; - return NS(encodings)[i]; -} - -int -NS(XmlParseXmlDecl)(int isGeneralTextEntity, - const ENCODING *enc, - const char *ptr, - const char *end, - const char **badPtr, - const char **versionPtr, - const char **versionEndPtr, - const char **encodingName, - const ENCODING **encoding, - int *standalone) -{ - return doParseXmlDecl(NS(findEncoding), - isGeneralTextEntity, - enc, - ptr, - end, - badPtr, - versionPtr, - versionEndPtr, - encodingName, - encoding, - standalone); -} - -#endif /* XML_TOK_NS_C */ diff --git a/apps/nesemu2/src/cartdb/parser.c b/apps/nesemu2/src/cartdb/parser.c deleted file mode 100644 index c4d58e26..00000000 --- a/apps/nesemu2/src/cartdb/parser.c +++ /dev/null @@ -1,313 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include -#include -#include "cartdb/parser.h" -#include "misc/log.h" - -#define BUFSIZE 4096 - -static int num_malloc = 0; -static int num_malloc_str = 0; -static int num_malloc_node = 0; -static int num_malloc_attr = 0; -static int num_free = 0; -static int num_free_str = 0; -static int num_free_node = 0; -static int num_free_attr = 0; -size_t num_bytes = 0; - -void *mem_malloc(size_t sz) -{ - num_malloc++; - num_bytes += sz; - return(malloc(sz)); -} - -void *mem_malloc_str(size_t sz) -{ - num_malloc_str++; - return(mem_malloc(sz)); -} - -void *mem_malloc_node(size_t sz) -{ - num_malloc_node++; - return(mem_malloc(sz)); -} - -void *mem_malloc_attr(size_t sz) -{ - num_malloc_attr++; - return(mem_malloc(sz)); -} - -void mem_free(void *ptr) -{ - num_free++; - free(ptr); -} - -void mem_free_str(void *ptr) -{ - num_free_str++; - mem_free(ptr); -} - -void mem_free_node(void *ptr) -{ - num_free_node++; - mem_free(ptr); -} - -void mem_free_attr(void *ptr) -{ - num_free_attr++; - mem_free(ptr); -} - -static char *copystr(const char *str) -{ - char *ret; - size_t sz; - - sz = strlen(str); - ret = mem_malloc_str(sz + 1); - memset(ret,0,sz + 1); - memcpy(ret,str,sz); - return(ret); -} - -static node_t *create_node(const char *name) -{ - node_t *ret = 0; - - ret = (node_t*)mem_malloc_node(sizeof(node_t)); - memset(ret,0,sizeof(node_t)); - ret->name = copystr(name); - return(ret); -} - -static attribute_t *create_attribute(const char *name,const char *data) -{ - attribute_t *ret = 0; - - ret = (attribute_t*)mem_malloc_attr(sizeof(attribute_t)); - memset(ret,0,sizeof(attribute_t)); - ret->name = copystr(name); - ret->data = copystr(data); - return(ret); -} - -static void destroy_node(node_t *node) -{ - mem_free_str(node->name); - mem_free_node(node); -} - -static void destroy_attribute(attribute_t *attr) -{ - mem_free_str(attr->name); - mem_free_str(attr->data); - mem_free_attr(attr); -} - -static node_t *add_child(node_t *parent,const char *name) -{ - node_t *ret = 0; - node_t *node; - - ret = create_node(name); - ret->parent = parent; - if(parent->child == 0) { - parent->child = ret; - } - else { - node = parent->child; - while(node->next) { - node = node->next; - } - node->next = ret; - } - return(ret); -} - -static void free_attributes(attribute_t *attr) -{ - attribute_t *a; - - while(attr) { - a = attr; - attr = attr->next; - destroy_attribute(a); - } -} - -static void free_nodes(node_t *node) -{ - node_t *n; - - while(node) { - if(node->child) - free_nodes(node->child); - n = node; - node = node->next; - free_attributes(n->attributes); - destroy_node(n); - } -} - -static void XMLCALL start(void *data,const char *el,const char **attr) -{ - xml_t *xmlinfo = (xml_t*)data; - int i; - - if(xmlinfo->root == 0) { - xmlinfo->root = create_node(el); - xmlinfo->cur = xmlinfo->root; - } - else { - xmlinfo->cur = add_child(xmlinfo->cur,el); - } - for(i=0;attr[i];i+=2) { - attribute_t *at; - - at = create_attribute(attr[i],attr[i+1]); - at->next = xmlinfo->cur->attributes; - xmlinfo->cur->attributes = at; - } -} - -static void XMLCALL end(void *data,const char *el) -{ - xml_t *xmlinfo = (xml_t*)data; - - xmlinfo->cur = xmlinfo->cur->parent; -} - -xml_t *parser_load(char *filename) -{ - FILE *fp; - char *buf; - int filelen,len; - xml_t *xml = 0; - - //open xml file - if((fp = fopen(filename,"rb")) == 0) { - log_printf("parser_loadxml: error opening file '%s'\n",filename); - return(0); - } - - //get size of xml file - fseek(fp,0,SEEK_END); - filelen = ftell(fp); - fseek(fp,0,SEEK_SET); - - //allocate temporary buffer - buf = mem_malloc(BUFSIZE + 1); - - //initialize xml struct - xml = (xml_t*)mem_malloc(sizeof(xml_t)); - memset(xml,0,sizeof(xml_t)); - xml->parser = XML_ParserCreate(NULL); - xml->root = xml->cur = 0; - - //setup expat - XML_SetElementHandler(xml->parser,start,end); - XML_SetUserData(xml->parser,(void*)xml); - - //help expat parse the xml file - while(filelen) { - if(BUFSIZE > filelen) { - fread(buf,1,filelen,fp); - len = filelen; - } - else { - fread(buf,1,BUFSIZE,fp); - len = BUFSIZE; - } - filelen -= len; - XML_Parse(xml->parser,buf,len,0); - } - XML_Parse(xml->parser,"",0,1); - - //cleanup - XML_ParserFree(xml->parser); - xml->parser = 0; - mem_free(buf); - fclose(fp); - - //we are done - return(xml); -} - -void parser_free(xml_t *xml) -{ - free_nodes(xml->root); - mem_free(xml); -} - -void parser_merge(xml_t *dest,xml_t **src) -{ - node_t *node; - - //ensure we are working with the same types - if(strcmp(dest->root->name,(*src)->root->name) != 0) { - log_printf("parser_merge: different root element names, cannot merge\n"); - return; - } - - //find the last node - node = dest->root->child; - while(node->next) { - node = node->next; - } - - //move nodes to the dest xml struct - node->next = (*src)->root->child; - (*src)->root->child = 0; - - //free the rest of the src xml struct - parser_free(*src); - *src = 0; -} - -#define IS_OK(cc) ((cc) ? "OK" : "Bad!") - -void parser_verifymemory() -{ - int result; - - result = num_malloc_str - num_free_str; - result += num_malloc_node - num_free_node; - result += num_malloc_attr - num_free_attr; - result += num_malloc - num_free; - if(result) { - log_printf("xml memory verification:\n"); - log_printf("------------------------\n"); - log_printf(" str mallocs: %d, frees %d (%s)\n",num_malloc_str,num_free_str,IS_OK(num_malloc_str == num_free_str)); - log_printf(" node mallocs: %d, frees %d (%s)\n",num_malloc_node,num_free_node,IS_OK(num_malloc_node == num_free_node)); - log_printf(" attr mallocs: %d, frees %d (%s)\n",num_malloc_attr,num_free_attr,IS_OK(num_malloc_attr == num_free_attr)); - log_printf(" total mallocs: %d, frees %d (%s)\n",num_malloc,num_free,IS_OK(num_malloc == num_free)); - } - log_printf("total bytes used by xml: %.3fmb\n",(double)num_bytes / 1024.0f / 1024.0f); -} diff --git a/apps/nesemu2/src/cartdb/parser.h b/apps/nesemu2/src/cartdb/parser.h deleted file mode 100644 index 6da6e01f..00000000 --- a/apps/nesemu2/src/cartdb/parser.h +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __cartdb__parser_h__ -#define __cartdb__parser_h__ - -#include "cartdb/expat/expat.h" - -typedef struct attribute_s { - struct attribute_s *next; - char *name,*data; -} attribute_t; - -typedef struct node_s { - struct node_s *next; - struct node_s *parent,*child; - attribute_t *attributes; - char *name,*data; -} node_t; - -typedef struct xml_s { - XML_Parser parser; - node_t *root,*cur; -} xml_t; - -xml_t *parser_load(char *filename); -void parser_free(xml_t *xml); -void parser_merge(xml_t *dest,xml_t **src); -void parser_verifymemory(); - -#endif diff --git a/apps/nesemu2/src/emu/emu.c b/apps/nesemu2/src/emu/emu.c index a039ed59..c98bc91c 100644 --- a/apps/nesemu2/src/emu/emu.c +++ b/apps/nesemu2/src/emu/emu.c @@ -160,12 +160,19 @@ int emu_mainloop() video_endframe(); total += system_gettick() - t; frames++; + + if (total > 1000) { + int fps = frames * system_getfrequency() / total; + log_printf("fps: %d\n", fps); + total = 0; + frames = 0; + } } - log_printf("fps: %f (%d frames)\n",(double)frames / (double)total * system_getfrequency(),frames); mem_free(line); return(0); } +#if 0 int emu_mainloop_test(char *script) { u64 t,total,frames; @@ -228,3 +235,4 @@ int emu_mainloop_test(char *script) memfile_close(file); return(0); } +#endif diff --git a/apps/nesemu2/src/emu/events.c b/apps/nesemu2/src/emu/events.c index 3b337235..d9c25de6 100644 --- a/apps/nesemu2/src/emu/events.c +++ b/apps/nesemu2/src/emu/events.c @@ -146,6 +146,7 @@ int emu_event(int id,void *data) case E_DUMPDISK: if(nes->cart == 0) break; +#if 0 if((nes->cart->mapperid & B_TYPEMASK) == B_FDS) { FILE *fp; @@ -155,6 +156,7 @@ int emu_event(int id,void *data) fclose(fp); } } +#endif break; case E_TOGGLERUNNING: diff --git a/apps/nesemu2/src/inputdev/zapper.c b/apps/nesemu2/src/inputdev/zapper.c index 6d6955dc..f9807e6e 100644 --- a/apps/nesemu2/src/inputdev/zapper.c +++ b/apps/nesemu2/src/inputdev/zapper.c @@ -21,6 +21,7 @@ #include "inputdev.h" #include "system/video.h" #include "system/input.h" +#include static u8 portdata; static u8 counter,buttons,strobe; @@ -59,8 +60,9 @@ static u8 read() break; if((Y == (int)nes->ppu.scanline) && (X >= (int)nes->ppu.linecycles)) break; - if(video_zapperhit(X,Y)) - hits++; + assert(0); + //if(video_zapperhit(X,Y)) + // hits++; } } } diff --git a/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c b/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c index c378468d..9d432117 100644 --- a/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c +++ b/apps/nesemu2/src/mappers/boards/other/bmc/15in1.c @@ -33,7 +33,7 @@ static void sync() static void write(u32 addr,u8 data) { reg = data & 3; - printf("205 write $%04X = $%02X\n",addr,data); + printf("205 write $%04lX = $%02X\n",addr,data); sync(); } diff --git a/apps/nesemu2/src/mappers/fds/calls/vector.c b/apps/nesemu2/src/mappers/fds/calls/vector.c index 636c2249..8317c1b9 100644 --- a/apps/nesemu2/src/mappers/fds/calls/vector.c +++ b/apps/nesemu2/src/mappers/fds/calls/vector.c @@ -20,10 +20,11 @@ #include #include -#include #include "mappers/fds/calls.h" #include "mappers/fds/hle.h" +u32 uptime(); + //nmi vector HLECALL(nmi) { @@ -162,7 +163,8 @@ HLECALL(reset) { //u8 m102,m103; - srand((u32)time(0)); + //srand((u32)time(0)); + srand(uptime()); // showdisasm = 1; //flag setup diff --git a/apps/nesemu2/src/mappers/fds/hle.c b/apps/nesemu2/src/mappers/fds/hle.c index b6a11d92..3b8e8d36 100644 --- a/apps/nesemu2/src/mappers/fds/hle.c +++ b/apps/nesemu2/src/mappers/fds/hle.c @@ -20,7 +20,6 @@ #include #include -#include #include "mappers/fds/fds.h" #include "mappers/fds/calls.h" #include "nes/nes.h" diff --git a/apps/nesemu2/src/mappers/nsf/nsf.c b/apps/nesemu2/src/mappers/nsf/nsf.c index 4b7153ea..2231a82e 100644 --- a/apps/nesemu2/src/mappers/nsf/nsf.c +++ b/apps/nesemu2/src/mappers/nsf/nsf.c @@ -159,14 +159,14 @@ static void write_bios(u32 addr,u8 data) //playspeed low case 0x3410: playspeed = (playspeed & 0xFF00) | data; - irqlatch = (u32)(1786840.0f * (double)playspeed / 1000000.0f); + irqlatch = (u32)((u64)178684 * playspeed / 100000); log_printf("irqlatch = %d (playspeed = %d)\n",irqlatch,playspeed); break; //playspeed high case 0x3411: playspeed = (playspeed & 0x00FF) | (data << 8); - irqlatch = (u32)(1786840.0f * (double)playspeed / 1000000.0f); + irqlatch = (u32)((u64)178684 * playspeed / 100000); log_printf("irqlatch = %d (playspeed = %d)\n",irqlatch,playspeed); break; diff --git a/apps/nesemu2/src/mappers/sound/s_FDS.c b/apps/nesemu2/src/mappers/sound/s_FDS.c index db1a1597..16633dce 100644 --- a/apps/nesemu2/src/mappers/sound/s_FDS.c +++ b/apps/nesemu2/src/mappers/sound/s_FDS.c @@ -5,6 +5,7 @@ * $Id: s_FDS.c 376 2008-06-29 20:58:13Z Quietust $ */ +#if 0 //#include "..\..\interface.h" #include #include @@ -323,3 +324,12 @@ int FDSsound_SaveLoad (int mode, int x, unsigned char *data) { return x; } +#endif + +void FDSsound_Load (void) { } +void FDSsound_Reset (void) { } +void FDSsound_Unload (void) { } +int FDSsound_Read (int Addr) { return -1; } +void FDSsound_Write (int Addr, int Val) { } +int FDSsound_Get (int numCycles) { return 0; } +int FDSsound_SaveLoad (int mode, int x, unsigned char *data) { return x; } diff --git a/apps/nesemu2/src/mappers/sound/s_VRC7.c b/apps/nesemu2/src/mappers/sound/s_VRC7.c index b0338765..0666c4f7 100644 --- a/apps/nesemu2/src/mappers/sound/s_VRC7.c +++ b/apps/nesemu2/src/mappers/sound/s_VRC7.c @@ -53,6 +53,7 @@ YM2143 data sheet **************************************************************************************/ +#if 0 #include "misc/log.h" #include "s_VRC7.h" #include @@ -1486,3 +1487,11 @@ int VRC7sound_SaveLoad (int mode, int x, unsigned char *data) else MessageBox(hWnd,_T("Invalid save/load type!"),_T(__FILE__),MB_OK); */ return x; } +#endif + +void VRC7sound_Load (void) { } +void VRC7sound_Reset (void) { } +void VRC7sound_Unload (void) { } +void VRC7sound_Write (int Addr, int Val) { } +int VRC7sound_Get (int numCycles) { return 0; } +int VRC7sound_SaveLoad (int mode, int x, unsigned char *data) { return x; } diff --git a/apps/nesemu2/src/mappers/unif.c b/apps/nesemu2/src/mappers/unif.c index 39443bd2..578a614d 100644 --- a/apps/nesemu2/src/mappers/unif.c +++ b/apps/nesemu2/src/mappers/unif.c @@ -20,7 +20,7 @@ #include #include "mapperinc.h" -#include "misc/slre/slre.h" +//#include "misc/slre/slre.h" struct unif_board_s { const char *name; @@ -175,15 +175,16 @@ int mapper_get_mapperid_unif(char *str) { int ret,i; const char *err; - char tmpstr[4][256]; + //char tmpstr[4][256]; ret = B_UNSUPPORTED; for(i=0;boards[i].boardid != -1;i++) { - err = slre_match(SLRE_CASE_INSENSITIVE,boards[i].name,str,strlen(str), - SLRE_STRING,sizeof(tmpstr[0]),tmpstr[0], - SLRE_STRING,sizeof(tmpstr[1]),tmpstr[1], - SLRE_STRING,sizeof(tmpstr[2]),tmpstr[2], - SLRE_STRING,sizeof(tmpstr[3]),tmpstr[3]); + //err = slre_match(SLRE_CASE_INSENSITIVE,boards[i].name,str,strlen(str), + // SLRE_STRING,sizeof(tmpstr[0]),tmpstr[0], + // SLRE_STRING,sizeof(tmpstr[1]),tmpstr[1], + // SLRE_STRING,sizeof(tmpstr[2]),tmpstr[2], + // SLRE_STRING,sizeof(tmpstr[3]),tmpstr[3]); + err = NULL; if(err == NULL) { if(ret != B_UNSUPPORTED) log_printf("mapper_get_mapperid_unif: duplicate match for board '%s'\n",str); diff --git a/apps/nesemu2/src/misc/config.c b/apps/nesemu2/src/misc/config.c index 1fe8090e..81c745c9 100644 --- a/apps/nesemu2/src/misc/config.c +++ b/apps/nesemu2/src/misc/config.c @@ -32,10 +32,10 @@ static vars_t *configvars = 0; +#if 0 //this path stuff needs to be moved static void mkdirr(char *path) { -#if 0 char *tmp = mem_strdup(path); char *p = tmp; int num = 0; @@ -64,7 +64,6 @@ static void mkdirr(char *path) //free tmp string mem_free(tmp); -#endif } static void makepath(char *str) @@ -130,12 +129,13 @@ static int findconfig(char *dest) return(1); } +#endif //initialize the configuration defaults static vars_t *config_get_defaults() { vars_t *ret = vars_create(); - char *str; + //char *str; vars_set_int (ret,F_CONFIG,"video.framelimit", 1); vars_set_int (ret,F_CONFIG,"video.fullscreen", 0); @@ -207,8 +207,8 @@ static vars_t *config_get_defaults() vars_set_string(ret,0,"version",VERSION); vars_set_string(ret,0,"exepath",exepath); - if((str = getenv("HOME")) != 0) - vars_set_string(ret,0,"home",str); + //if((str = getenv("HOME")) != 0) + // vars_set_string(ret,0,"home",str); return(ret); } @@ -216,13 +216,13 @@ static vars_t *config_get_defaults() int config_init() { vars_t *v; - char tmp[1024]; + //char tmp[1024]; //find configuration file - if(findconfig(configfilename) == 0) - log_printf("main: found configuration at '%s'\n",configfilename); - else - log_printf("main: creating new configuration at '%s'\n",configfilename); + //if(findconfig(configfilename) == 0) + // log_printf("main: found configuration at '%s'\n",configfilename); + //else + // log_printf("main: creating new configuration at '%s'\n",configfilename); configvars = config_get_defaults(); if((v = vars_load(configfilename)) == 0) { @@ -237,10 +237,10 @@ int config_init() } //make the directories - makepath(config_get_eval_string(tmp,"path.user")); - makepath(config_get_eval_string(tmp,"path.save")); - makepath(config_get_eval_string(tmp,"path.state")); - makepath(config_get_eval_string(tmp,"path.cheat")); + //makepath(config_get_eval_string(tmp,"path.user")); + //makepath(config_get_eval_string(tmp,"path.save")); + //makepath(config_get_eval_string(tmp,"path.state")); + //makepath(config_get_eval_string(tmp,"path.cheat")); return(0); } diff --git a/apps/nesemu2/src/misc/log.c b/apps/nesemu2/src/misc/log.c index 1b3a9060..2dfd3f60 100644 --- a/apps/nesemu2/src/misc/log.c +++ b/apps/nesemu2/src/misc/log.c @@ -36,9 +36,10 @@ #define LOGFILENAME "nesemu2.log" +#if 0 +static void (*loghook)(char*) = 0; static FILE *logfd = 0; static char logfilename[MAX_PATH] = ""; -static void (*loghook)(char*) = 0; static history_t history = {0,0}; int log_init() @@ -94,10 +95,14 @@ void log_kill() fclose(logfd); logfd = 0; } +#endif + +int log_init() { return 0; } +void log_kill() { } void log_sethook(void (*hook)(char*)) { - loghook = hook; + //loghook = hook; } void log_print(char *str) @@ -105,6 +110,7 @@ void log_print(char *str) //output message to stdout printf("%s",str); +#if 0 //output message to hook function if(loghook) { loghook(str); @@ -121,6 +127,7 @@ void log_print(char *str) //flush file fflush(logfd); +#endif } void log_printf(char *str,...) diff --git a/apps/nesemu2/src/misc/memfile.c b/apps/nesemu2/src/misc/memfile.c index cd0f9df0..4c570bdb 100644 --- a/apps/nesemu2/src/misc/memfile.c +++ b/apps/nesemu2/src/misc/memfile.c @@ -23,6 +23,7 @@ #include "misc/memfile.h" #include "misc/memutil.h" #include "misc/log.h" +#include memfile_t *memfile_create() { @@ -36,6 +37,7 @@ memfile_t *memfile_create() return(ret); } +#ifndef NO_FILE_SYSTEM memfile_t *memfile_open(char *filename,char *mode) { FILE *fp; @@ -83,16 +85,6 @@ memfile_t *memfile_open(char *filename,char *mode) return(ret); } -memfile_t *memfile_open_memory(u8 *data,u32 size) -{ - memfile_t *ret; - - ret = memfile_create(); - ret->size = size; - ret->data = (u8*)mem_dup(data,size); - return(ret); -} - void memfile_close(memfile_t *mf) { FILE *fp = (FILE*)mf->handle; @@ -114,6 +106,41 @@ void memfile_close(memfile_t *mf) mem_free(mf->data); mem_free(mf); } +#else +#include "roms/gen/roms.h" + +memfile_t *memfile_open(char *filename,char *mode) { + struct rom *rom = &roms[0]; + for (int i = 1; i < nroms; i++) { + struct rom *cur = &roms[i]; + if (strcmp(cur->name, filename) == 0) { + rom = cur; + } + } + + printf("Using ROM: %s\n", rom->name); + + memfile_t *mf = memfile_open_memory(rom->body, *(rom->size)); + + return mf; +} + +void memfile_close(memfile_t *mf) { + if(mf->data) + mem_free(mf->data); + mem_free(mf); +} +#endif + +memfile_t *memfile_open_memory(u8 *data,u32 size) +{ + memfile_t *ret; + + ret = memfile_create(); + ret->size = size; + ret->data = (u8*)mem_dup(data,size); + return(ret); +} u32 memfile_size(memfile_t *mf) { diff --git a/apps/nesemu2/src/misc/memutil.c b/apps/nesemu2/src/misc/memutil.c index 878835a1..45c0e4da 100644 --- a/apps/nesemu2/src/misc/memutil.c +++ b/apps/nesemu2/src/misc/memutil.c @@ -22,6 +22,7 @@ #include #include "misc/memutil.h" #include "misc/log.h" +#include #define MAX_CHUNKS 1024 @@ -46,13 +47,7 @@ static size_t num_bytes; static char *bytestr(size_t sz) { static char str[64]; - - if(sz < 1024) - sprintf(str,"%ub",sz); - else if(sz < 1024 * 1024) - sprintf(str,"%.2fkb",(double)sz / 1024.0f); - else if(sz < 1024 * 1024 * 1024) - sprintf(str,"%.2fmb",(double)sz / 1024.0f / 1024.0f); + sprintf(str,"%ub",sz); return(str); } @@ -142,7 +137,7 @@ void *memutil_alloc(size_t size,char *file,int line) checkinited(); if ((ret = malloc(size)) == 0) { log_printf("memutil_alloc: unable to alloc %d bytes\n", size); - exit(-1); + assert(0); } memset(ret,0,size); num_alloc++; @@ -167,13 +162,13 @@ void *memutil_alloc(size_t size,char *file,int line) void *memutil_realloc(void *ptr,size_t size,char *file,int line) { - void *ret; - int i; - checkinited(); if(ptr == 0) return(memutil_alloc(size,file,line)); - ret = realloc(ptr,size); + assert(0); +#if 0 + void *ret = realloc(ptr,size); + int i; num_realloc++; for(i=0;i -// All rights reserved -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include -#include -#include -#include -#include -//#include - -#include "slre.h" - -#ifdef _WIN32 -#define snprintf _snprintf -#endif - -// Compiled regular expression -struct slre { - unsigned char code[256]; - unsigned char data[256]; - int code_size; - int data_size; - int num_caps; // Number of bracket pairs - int anchored; // Must match from string start - enum slre_option options; - const char *error_string; // Error string -}; - -// Captured substring -struct cap { - const char *ptr; // Pointer to the substring - int len; // Substring length -}; - -enum { - END, BRANCH, ANY, EXACT, ANYOF, ANYBUT, OPEN, CLOSE, BOL, EOL, STAR, PLUS, - STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT -}; - -// Commands and operands are all unsigned char (1 byte long). All code offsets -// are relative to current address, and positive (always point forward). Data -// offsets are absolute. Commands with operands: -// -// BRANCH offset1 offset2 -// Try to match the code block that follows the BRANCH instruction -// (code block ends with END). If no match, try to match code block that -// starts at offset1. If either of these match, jump to offset2. -// -// EXACT data_offset data_length -// Try to match exact string. String is recorded in data section from -// data_offset, and has length data_length. -// -// OPEN capture_number -// CLOSE capture_number -// If the user have passed 'struct cap' array for captures, OPEN -// records the beginning of the matched substring (cap->ptr), CLOSE -// sets the length (cap->len) for respective capture_number. -// -// STAR code_offset -// PLUS code_offset -// QUEST code_offset -// *, +, ?, respectively. Try to gobble as much as possible from the -// matched buffer while code block that follows these instructions -// matches. When the longest possible string is matched, -// jump to code_offset -// -// STARQ, PLUSQ are non-greedy versions of STAR and PLUS. - -static const char *meta_characters = "|.^$*+?()[\\"; -static const char *error_no_match = "No match"; - -static void set_jump_offset(struct slre *r, int pc, int offset) { - assert(offset < r->code_size); - if (r->code_size - offset > 0xff) { - r->error_string = "Jump offset is too big"; - } else { - r->code[pc] = (unsigned char) (r->code_size - offset); - } -} - -static void emit(struct slre *r, int code) { - if (r->code_size >= (int) (sizeof(r->code) / sizeof(r->code[0]))) { - r->error_string = "RE is too long (code overflow)"; - } else { - r->code[r->code_size++] = (unsigned char) code; - } -} - -static void store_char_in_data(struct slre *r, int ch) { - if (r->data_size >= (int) sizeof(r->data)) { - r->error_string = "RE is too long (data overflow)"; - } else { - r->data[r->data_size++] = ch; - } -} - -static void exact(struct slre *r, const char **re) { - int old_data_size = r->data_size; - - while (**re != '\0' && (strchr(meta_characters, **re)) == NULL) { - store_char_in_data(r, *(*re)++); - } - - emit(r, EXACT); - emit(r, old_data_size); - emit(r, r->data_size - old_data_size); -} - -static int get_escape_char(const char **re) { - int res; - - switch (*(*re)++) { - case 'n': res = '\n'; break; - case 'r': res = '\r'; break; - case 't': res = '\t'; break; - case '0': res = 0; break; - case 'S': res = NONSPACE << 8; break; - case 's': res = SPACE << 8; break; - case 'd': res = DIGIT << 8; break; - default: res = (*re)[-1]; break; - } - - return res; -} - -static void anyof(struct slre *r, const char **re) { - int esc, old_data_size = r->data_size, op = ANYOF; - - if (**re == '^') { - op = ANYBUT; - (*re)++; - } - - while (**re != '\0') - - switch (*(*re)++) { - case ']': - emit(r, op); - emit(r, old_data_size); - emit(r, r->data_size - old_data_size); - return; - // NOTREACHED - break; - case '\\': - esc = get_escape_char(re); - if ((esc & 0xff) == 0) { - store_char_in_data(r, 0); - store_char_in_data(r, esc >> 8); - } else { - store_char_in_data(r, esc); - } - break; - default: - store_char_in_data(r, (*re)[-1]); - break; - } - - r->error_string = "No closing ']' bracket"; -} - -static void relocate(struct slre *r, int begin, int shift) { - emit(r, END); - memmove(r->code + begin + shift, r->code + begin, r->code_size - begin); - r->code_size += shift; -} - -static void quantifier(struct slre *r, int prev, int op) { - if (r->code[prev] == EXACT && r->code[prev + 2] > 1) { - r->code[prev + 2]--; - emit(r, EXACT); - emit(r, r->code[prev + 1] + r->code[prev + 2]); - emit(r, 1); - prev = r->code_size - 3; - } - relocate(r, prev, 2); - r->code[prev] = op; - set_jump_offset(r, prev + 1, prev); -} - -static void exact_one_char(struct slre *r, int ch) { - emit(r, EXACT); - emit(r, r->data_size); - emit(r, 1); - store_char_in_data(r, ch); -} - -static void fixup_branch(struct slre *r, int fixup) { - if (fixup > 0) { - emit(r, END); - set_jump_offset(r, fixup, fixup - 2); - } -} - -static void compile(struct slre *r, const char **re) { - int op, esc, branch_start, last_op, fixup, cap_no, level; - - fixup = 0; - level = r->num_caps; - branch_start = last_op = r->code_size; - - for (;;) - switch (*(*re)++) { - - case '\0': - (*re)--; - return; - // NOTREACHED - break; - - case '^': - emit(r, BOL); - break; - - case '$': - emit(r, EOL); - break; - - case '.': - last_op = r->code_size; - emit(r, ANY); - break; - - case '[': - last_op = r->code_size; - anyof(r, re); - break; - - case '\\': - last_op = r->code_size; - esc = get_escape_char(re); - if (esc & 0xff00) { - emit(r, esc >> 8); - } else { - exact_one_char(r, esc); - } - break; - - case '(': - last_op = r->code_size; - cap_no = ++r->num_caps; - emit(r, OPEN); - emit(r, cap_no); - - compile(r, re); - if (*(*re)++ != ')') { - r->error_string = "No closing bracket"; - return; - } - - emit(r, CLOSE); - emit(r, cap_no); - break; - - case ')': - (*re)--; - fixup_branch(r, fixup); - if (level == 0) { - r->error_string = "Unbalanced brackets"; - return; - } - return; - // NOTREACHED - break; - - case '+': - case '*': - op = (*re)[-1] == '*' ? STAR: PLUS; - if (**re == '?') { - (*re)++; - op = op == STAR ? STARQ : PLUSQ; - } - quantifier(r, last_op, op); - break; - - case '?': - quantifier(r, last_op, QUEST); - break; - - case '|': - fixup_branch(r, fixup); - relocate(r, branch_start, 3); - r->code[branch_start] = BRANCH; - set_jump_offset(r, branch_start + 1, branch_start); - fixup = branch_start + 2; - r->code[fixup] = 0xff; - break; - - default: - (*re)--; - last_op = r->code_size; - exact(r, re); - break; - } -} - -// Compile regular expression. If success, 1 is returned. -// If error, 0 is returned and slre.error_string points to the error message. -static const char *compile2(struct slre *r, const char *re) { - r->error_string = NULL; - r->code_size = r->data_size = r->num_caps = r->anchored = 0; - - if (*re == '^') { - r->anchored++; - } - - emit(r, OPEN); // This will capture what matches full RE - emit(r, 0); - - while (*re != '\0') { - compile(r, &re); - } - - if (r->code[2] == BRANCH) { - fixup_branch(r, 4); - } - - emit(r, CLOSE); - emit(r, 0); - emit(r, END); - -#if 0 - static void dump(const struct slre *, FILE *); - dump(r, stdout); -#endif - - return r->error_string; -} - -static const char *match(const struct slre *, int, const char *, int, int *, - struct cap *, int caps_size); - -static void loop_greedy(const struct slre *r, int pc, const char *s, int len, - int *ofs) { - int saved_offset, matched_offset; - - saved_offset = matched_offset = *ofs; - - while (!match(r, pc + 2, s, len, ofs, NULL, 0)) { - saved_offset = *ofs; - if (!match(r, pc + r->code[pc + 1], s, len, ofs, NULL, 0)) { - matched_offset = saved_offset; - } - *ofs = saved_offset; - } - - *ofs = matched_offset; -} - -static void loop_non_greedy(const struct slre *r, int pc, const char *s, - int len, int *ofs) { - int saved_offset = *ofs; - - while (!match(r, pc + 2, s, len, ofs, NULL, 0)) { - saved_offset = *ofs; - if (!match(r, pc + r->code[pc + 1], s, len, ofs, NULL, 0)) - break; - } - - *ofs = saved_offset; -} - -static int is_any_of(const unsigned char *p, int len, const char *s, int *ofs) { - int i, ch; - - ch = s[*ofs]; - - for (i = 0; i < len; i++) - if (p[i] == ch) { - (*ofs)++; - return 1; - } - - return 0; -} - -static int is_any_but(const unsigned char *p, int len, const char *s, - int *ofs) { - int i, ch; - - ch = s[*ofs]; - - for (i = 0; i < len; i++) - if (p[i] == ch) { - return 0; - } - - (*ofs)++; - return 1; -} - -static int lowercase(const char *s) { - return tolower(* (const unsigned char *) s); -} - -static int casecmp(const void *p1, const void *p2, size_t len) { - const char *s1 = p1, *s2 = p2; - int diff = 0; - - if (len > 0) - do { - diff = lowercase(s1++) - lowercase(s2++); - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - - return diff; -} - -static const char *match(const struct slre *r, int pc, const char *s, int len, - int *ofs, struct cap *caps, int caps_size) { - int n, saved_offset; - const char *error_string = NULL; - int (*cmp)(const void *string1, const void *string2, size_t len); - - while (error_string == NULL && r->code[pc] != END) { - - assert(pc < r->code_size); - assert(pc < (int) (sizeof(r->code) / sizeof(r->code[0]))); - - switch (r->code[pc]) { - case BRANCH: - saved_offset = *ofs; - error_string = match(r, pc + 3, s, len, ofs, caps, caps_size); - if (error_string != NULL) { - *ofs = saved_offset; - error_string = match(r, pc + r->code[pc + 1], s, len, ofs, caps, - caps_size); - } - pc += r->code[pc + 2]; - break; - - case EXACT: - error_string = error_no_match; - n = r->code[pc + 2]; // String length - cmp = r->options & SLRE_CASE_INSENSITIVE ? casecmp : memcmp; - if (n <= len - *ofs && !cmp(s + *ofs, r->data + r->code[pc + 1], n)) { - (*ofs) += n; - error_string = NULL; - } - pc += 3; - break; - - case QUEST: - error_string = NULL; - saved_offset = *ofs; - if (match(r, pc + 2, s, len, ofs, caps, caps_size) != NULL) { - *ofs = saved_offset; - } - pc += r->code[pc + 1]; - break; - - case STAR: - error_string = NULL; - loop_greedy(r, pc, s, len, ofs); - pc += r->code[pc + 1]; - break; - - case STARQ: - error_string = NULL; - loop_non_greedy(r, pc, s, len, ofs); - pc += r->code[pc + 1]; - break; - - case PLUS: - if ((error_string = match(r, pc + 2, s, len, ofs, - caps, caps_size)) != NULL) { - break; - } - loop_greedy(r, pc, s, len, ofs); - pc += r->code[pc + 1]; - break; - - case PLUSQ: - if ((error_string = match(r, pc + 2, s, len, ofs, - caps, caps_size)) != NULL) { - break; - } - loop_non_greedy(r, pc, s, len, ofs); - pc += r->code[pc + 1]; - break; - - case SPACE: - error_string = error_no_match; - if (*ofs < len && isspace(((unsigned char *)s)[*ofs])) { - (*ofs)++; - error_string = NULL; - } - pc++; - break; - - case NONSPACE: - error_string = error_no_match; - if (*ofs data + r->code[pc + 1], r->code[pc + 2], - s, ofs) ? NULL : error_no_match; - pc += 3; - break; - - case ANYBUT: - error_string = error_no_match; - if (*ofs < len) - error_string = is_any_but(r->data + r->code[pc + 1], r->code[pc + 2], - s, ofs) ? NULL : error_no_match; - pc += 3; - break; - - case BOL: - error_string = *ofs == 0 ? NULL : error_no_match; - pc++; - break; - - case EOL: - error_string = *ofs == len ? NULL : error_no_match; - pc++; - break; - - case OPEN: - if (caps != NULL) { - if (caps_size - 2 < r->code[pc + 1]) { - error_string = "Too many brackets"; - } else { - caps[r->code[pc + 1]].ptr = s + *ofs; - } - } - pc += 2; - break; - - case CLOSE: - if (caps != NULL) { - assert(r->code[pc + 1] >= 0); - assert(r->code[pc + 1] < caps_size); - caps[r->code[pc + 1]].len = (s + *ofs) - - caps[r->code[pc + 1]].ptr; - } - pc += 2; - break; - - case END: - pc++; - break; - - default: - printf("unknown cmd (%d) at %d\n", r->code[pc], pc); - assert(0); - break; - } - } - - return error_string; -} - -// Return 1 if match, 0 if no match. -// If `captured_substrings' array is not NULL, then it is filled with the -// values of captured substrings. captured_substrings[0] element is always -// a full matched substring. The round bracket captures start from -// captured_substrings[1]. -// It is assumed that the size of captured_substrings array is enough to -// hold all captures. The caller function must make sure it is! So, the -// array_size = number_of_round_bracket_pairs + 1 -static const char *match2(const struct slre *r, const char *buf, int len, - struct cap *caps, int caps_size) { - int i, ofs = 0; - const char *error_string = error_no_match; - - if (caps != NULL) { - memset(caps, 0, caps_size * sizeof(caps[0])); - } - - if (r->anchored) { - error_string = match(r, 0, buf, len, &ofs, caps, caps_size); - } else { - for (i = 0; i < len && error_string != NULL; i++) { - ofs = i; - error_string = match(r, 0, buf, len, &ofs, caps, caps_size); - } - } - - return error_string; -} - -static const char *capture_float(const struct cap *cap, void *p, size_t len) { - const char *fmt; - char buf[20]; - - switch (len) { - case sizeof(float): fmt = "f"; break; - case sizeof(double): fmt = "lf"; break; - default: return "SLRE_FLOAT: unsupported size"; - } - - snprintf(buf, sizeof(buf), "%%%d%s", cap->len, fmt); - return sscanf(cap->ptr, buf, p) == 1 ? NULL : "SLRE_FLOAT: capture failed"; -} - -static const char *capture_string(const struct cap *cap, void *p, size_t len) { - if ((int) len <= cap->len) { - return "SLRE_STRING: buffer size too small"; - } - memcpy(p, cap->ptr, cap->len); - ((char *) p)[cap->len] = '\0'; - return NULL; -} - -static const char *capture_int(const struct cap *cap, void *p, size_t len) { - const char *fmt; - char buf[20]; - - switch (len) { - case sizeof(char): fmt = "hh"; break; - case sizeof(short): fmt = "h"; break; - case sizeof(int): fmt = "d"; break; - case sizeof(long long int): fmt = "lld"; break; - default: return "SLRE_INT: unsupported size"; - } - - snprintf(buf, sizeof(buf), "%%%d%s", cap->len, fmt); - return sscanf(cap->ptr, buf, p) == 1 ? NULL : "SLRE_INT: capture failed"; -} - -static const char *capture(const struct cap *caps, int num_caps, va_list ap) { - int i, type; - size_t size; - void *p; - const char *err = NULL; - - for (i = 0; i < num_caps; i++) { - type = va_arg(ap, int); - size = va_arg(ap, size_t); - p = va_arg(ap, void *); - switch (type) { - case SLRE_INT: err = capture_int(&caps[i], p, size); break; - case SLRE_FLOAT: err = capture_float(&caps[i], p, size); break; - case SLRE_STRING: err = capture_string(&caps[i], p, size); break; - default: err = "Unknown type, expected SLRE_(INT|FLOAT|STRING)"; break; - } - } - return err; -} - -const char *slre_match(enum slre_option options, const char *re, - const char *buf, int buf_len, ...) { - struct slre slre = {}; - struct cap caps[20]; - va_list ap; - const char *error_string = NULL; - - slre.options = options; - if ((error_string = compile2(&slre, re)) == NULL && - (error_string = match2(&slre, buf, buf_len, caps, - sizeof(caps) / sizeof(caps[0]))) == NULL) { - va_start(ap, buf_len); - error_string = capture(caps + 1, slre.num_caps, ap); - va_end(ap); - } - - return error_string; -} - -#if defined(SLRE_UNIT_TEST) -static struct { - const char *name; - int narg; - const char *flags; -} opcodes[] = { - {"END", 0, "" }, // End of code block or program - {"BRANCH", 2, "oo"}, // Alternative operator, "|" - {"ANY", 0, "" }, // Match any character, "." - {"EXACT", 2, "d" }, // Match exact string - {"ANYOF", 2, "D" }, // Match any from set, "[]" - {"ANYBUT", 2, "D" }, // Match any but from set, "[^]" - {"OPEN ", 1, "i" }, // Capture start, "(" - {"CLOSE", 1, "i" }, // Capture end, ")" - {"BOL", 0, "" }, // Beginning of string, "^" - {"EOL", 0, "" }, // End of string, "$" - {"STAR", 1, "o" }, // Match zero or more times "*" - {"PLUS", 1, "o" }, // Match one or more times, "+" - {"STARQ", 1, "o" }, // Non-greedy STAR, "*?" - {"PLUSQ", 1, "o" }, // Non-greedy PLUS, "+?" - {"QUEST", 1, "o" }, // Match zero or one time, "?" - {"SPACE", 0, "" }, // Match whitespace, "\s" - {"NONSPACE", 0, "" }, // Match non-space, "\S" - {"DIGIT", 0, "" } // Match digit, "\d" -}; - -static void print_character_set(FILE *fp, const unsigned char *p, int len) { - int i; - - for (i = 0; i < len; i++) { - if (i > 0) - (void) fputc(',', fp); - if (p[i] == 0) { - i++; - if (p[i] == 0) - (void) fprintf(fp, "\\x%02x", p[i]); - else - (void) fprintf(fp, "%s", opcodes[p[i]].name); - } else if (isprint(p[i])) { - (void) fputc(p[i], fp); - } else { - (void) fprintf(fp,"\\x%02x", p[i]); - } - } -} - -static void dump(const struct slre *r, FILE *fp) { - int i, j, ch, op, pc; - - for (pc = 0; pc < r->code_size; pc++) { - - op = r->code[pc]; - (void) fprintf(fp, "%3d %s ", pc, opcodes[op].name); - - for (i = 0; opcodes[op].flags[i] != '\0'; i++) - switch (opcodes[op].flags[i]) { - case 'i': - fprintf(fp, "%d ", r->code[pc + 1]); - pc++; - break; - case 'o': - fprintf(fp, "%d ", pc + r->code[pc + 1] - i); - pc++; - break; - case 'D': - print_character_set(fp, r->data + - r->code[pc + 1], r->code[pc + 2]); - pc += 2; - break; - case 'd': - (void) fputc('"', fp); - for (j = 0; j < r->code[pc + 2]; j++) { - ch = r->data[r->code[pc + 1] + j]; - if (isprint(ch)) - fputc(ch, fp); - else - fprintf(fp,"\\x%02x",ch); - } - (void) fputc('"', fp); - pc += 2; - break; - } - - fputc('\n', fp); - } -} - - -int main(void) { - static const struct { const char *str, *regex, *msg; } tests[] = { - {"aa", ".+", NULL}, - {"aa", ".", NULL}, - {"", ".", "No match"}, - {" cc 1234", "c.\\s\\d+", NULL}, - }; - char buf[20]; - int int_value; - const char *msg, *str, *re; - size_t i; - - char method[10], uri[100]; - int http_version_minor, http_version_major; - const char *error; - const char *request = " \tGET /index.html HTTP/1.0\r\n\r\n"; - - error = slre_match(0, "^\\s*(GET|POST)\\s+(\\S+)\\s+HTTP/(\\d)\\.(\\d)", - request, strlen(request), - SLRE_STRING, sizeof(method), method, - SLRE_STRING, sizeof(uri), uri, - SLRE_INT, sizeof(http_version_major), &http_version_major, - SLRE_INT, sizeof(http_version_minor), &http_version_minor); - - if (error != NULL) { - printf("Error parsing HTTP request: %s\n", error); - } else { - printf("Requested URI: %s\n", uri); - } - - for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { - msg = slre_match(0, tests[i].regex, tests[i].str, - strlen(tests[i].str), NULL); - if ((msg != NULL && tests[i].msg == NULL) || - (msg != NULL && strcmp(msg, tests[i].msg) != 0)) { - printf("Test %d failed: [%s] [%s] -> [%s]\n", (int) i, tests[i].str, - tests[i].regex, msg ? msg : "(null)"); - return EXIT_FAILURE; - } - } - - assert(slre_match(0, "a (\\d+)4\\s*(\\S+)", "aa 1234 xy\nz", 12, - SLRE_INT, sizeof(int_value), &int_value, - SLRE_STRING, sizeof(buf), buf) == NULL); - assert(int_value == 123); - assert(!strcmp(buf, "xy")); - - str = "Hello превед!"; - re = "^hello (\\S+)"; - assert(strcmp(error_no_match, slre_match(0, re, str, strlen(str), SLRE_STRING, - sizeof(buf), buf)) == 0); - assert(slre_match(SLRE_CASE_INSENSITIVE, re, str, strlen(str), - SLRE_STRING, sizeof(buf), buf) == NULL); - assert(!strcmp(buf, "превед!")); - - assert(strcmp(error_no_match, slre_match(0, "bC", "aBc", 3)) == 0); - assert(slre_match(SLRE_CASE_INSENSITIVE, "bC", "aBc", 3) == NULL); - assert(slre_match(0, "3?9", "9", 1) == NULL); - - // TODO: fix this! - //assert(slre_match(0, "9?9", "9", 1) == NULL); - - { - struct slre slre; - struct cap caps[10]; - char a[10], b[10]; - - memset(caps, 'x', sizeof(caps)); - slre.options = 0; - assert(compile2(&slre, "(\\d(\\d)?)") == NULL); - assert(!strcmp(match2(&slre, "1", 1, caps, 2), "Too many brackets")); - assert(match2(&slre, "1", 1, caps, 3) == NULL); - assert(slre.num_caps == 2); - assert(caps[1].len == 1); - assert(caps[2].len == 0); - assert(caps[1].ptr[0] == '1'); - - a[0] = b[0] = 'x'; - assert(slre_match(0, "(\\d(\\d)?)", "1", 1, - SLRE_STRING, sizeof(a), a, - SLRE_STRING, sizeof(b), b) == NULL); - assert(!strcmp(a, "1")); - assert(b[0] == '\0'); - } - - printf("%s\n", "All tests passed"); - return EXIT_SUCCESS; -} -#endif // SLRE_UNIT_TEST diff --git a/apps/nesemu2/src/misc/slre/slre.h b/apps/nesemu2/src/misc/slre/slre.h deleted file mode 100644 index 69f68ab2..00000000 --- a/apps/nesemu2/src/misc/slre/slre.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2004-2012 Sergey Lyubka -// All rights reserved -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef SLRE_HEADER_DEFINED -#define SLRE_HEADER_DEFINED - -#ifdef __cplusplus -extern "C" { -#endif - -// This is a regular expression library that implements a subset of Perl RE. -// Please refer to http://slre.googlecode.com for detailed description. -// -// Supported syntax: -// ^ Match beginning of a buffer -// $ Match end of a buffer -// () Grouping and substring capturing -// [...] Match any character from set -// [^...] Match any character but ones from set -// \s Match whitespace -// \S Match non-whitespace -// \d Match decimal digit -// \r Match carriage return -// \n Match newline -// + Match one or more times (greedy) -// +? Match one or more times (non-greedy) -// * Match zero or more times (greedy) -// *? Match zero or more times (non-greedy) -// ? Match zero or once -// \xDD Match byte with hex value 0xDD -// \meta Match one of the meta character: ^$().[*+\? - -// Match string buffer "buf" of length "buf_len" against "regexp", which should -// conform the syntax outlined above. "options" could be either 0 or -// SLRE_CASE_INSENSITIVE for case-insensitive match. If regular expression -// "regexp" contains brackets, slre_match() will capture the respective -// substring into the passed placeholder. Thus, each opening parenthesis -// should correspond to three arguments passed: -// placeholder_type, placeholder_size, placeholder_address -// -// Usage example: parsing HTTP request line. -// -// char method[10], uri[100]; -// int http_version_minor, http_version_major; -// const char *error; -// const char *request = " \tGET /index.html HTTP/1.0\r\n\r\n"; - -// error = slre_match(0, "^\\s*(GET|POST)\\s+(\\S+)\\s+HTTP/(\\d)\\.(\\d)", -// request, strlen(request), -// SLRE_STRING, sizeof(method), method, -// SLRE_STRING, sizeof(uri), uri, -// SLRE_INT,sizeof(http_version_major),&http_version_major, -// SLRE_INT,sizeof(http_version_minor),&http_version_minor); -// -// if (error != NULL) { -// printf("Error parsing HTTP request: %s\n", error); -// } else { -// printf("Requested URI: %s\n", uri); -// } -// -// -// Return: -// NULL: string matched and all captures successfully made -// non-NULL: in this case, the return value is an error string - -enum slre_option {SLRE_CASE_INSENSITIVE = 1}; -enum slre_capture {SLRE_STRING, SLRE_INT, SLRE_FLOAT}; - -const char *slre_match(enum slre_option options, const char *regexp, - const char *buf, int buf_len, ...); - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // SLRE_HEADER_DEFINED diff --git a/apps/nesemu2/src/misc/vars.c b/apps/nesemu2/src/misc/vars.c index e77936dd..d67d52b8 100644 --- a/apps/nesemu2/src/misc/vars.c +++ b/apps/nesemu2/src/misc/vars.c @@ -25,12 +25,13 @@ vars_add_var is able to add another var with the same name */ #include -#include -#include +//#include +//#include #include "misc/memutil.h" #include "misc/strutil.h" #include "misc/vars.h" #include "misc/log.h" +#include vars_t *vars_create() { @@ -47,6 +48,7 @@ void vars_destroy(vars_t *vs) mem_free(vs); } +#if 0 vars_t *vars_load(char *filename) { vars_t *ret = 0; @@ -113,6 +115,10 @@ int vars_save(vars_t *vs,char *filename) vs->changed = 0; return(0); } +#endif + +vars_t *vars_load(char *filename) { return NULL; } +int vars_save(vars_t *vs,char *filename) { return 1; } void vars_merge(vars_t *dest,vars_t *src) { @@ -220,8 +226,11 @@ int vars_get_bool(vars_t *vs,char *name,int def) double vars_get_double(vars_t *vs,char *name,double def) { +#if 0 sprintf(tmpstr,"%f",def); return(atof(vars_get_string(vs,name,tmpstr))); +#endif + return 0; } var_t *vars_set_string(vars_t *vs,int flags,char *name,char *data) diff --git a/apps/nesemu2/src/nes/cart/nsf.c b/apps/nesemu2/src/nes/cart/nsf.c index bdf1a9ef..874f12b4 100644 --- a/apps/nesemu2/src/nes/cart/nsf.c +++ b/apps/nesemu2/src/nes/cart/nsf.c @@ -28,6 +28,7 @@ #include "misc/strutil.h" #include "mappers/mapperid.h" +#if 0 static int loadbios(cart_t *ret,char *filename) { FILE *fp; @@ -81,6 +82,9 @@ static int loadbios(cart_t *ret,char *filename) //success return(0); } +#endif + +static int loadbios(cart_t *ret,char *filename) { return -1; } //makes the size a multiple of 4096 for padding //static u32 padsize(u32 size) diff --git a/apps/nesemu2/src/nes/genie.c b/apps/nesemu2/src/nes/genie.c index 79857151..b8222584 100644 --- a/apps/nesemu2/src/nes/genie.c +++ b/apps/nesemu2/src/nes/genie.c @@ -37,7 +37,7 @@ typedef struct codedata_s { } codedata_t; //ines ident for determining if the genie rom has ines header -static u8 inesident[4] = {'N','E','S',0x1A}; +//static u8 inesident[4] = {'N','E','S',0x1A}; //genie rom image (prg + chr) static u8 *genierom = 0; @@ -132,6 +132,7 @@ static void genie_write(u32 addr,u8 data) } } +#if 0 int genie_loadrom(char *filename) { FILE *fp; @@ -192,6 +193,9 @@ int genie_loadrom(char *filename) log_printf("genie_load: loaded game genie rom '%s' (%d bytes)\n",filename,len); return(0); } +#endif + +int genie_loadrom(char *filename) { return 1; } int genie_load() { diff --git a/apps/nesemu2/src/nes/state/state.h b/apps/nesemu2/src/nes/state/state.h index 7db013b0..431f7fff 100644 --- a/apps/nesemu2/src/nes/state/state.h +++ b/apps/nesemu2/src/nes/state/state.h @@ -21,7 +21,7 @@ #ifndef __state_h__ #define __state_h__ -#include +#include #include "nes/state/block.h" #define STATE_LOAD 0 //load state diff --git a/apps/nesemu2/src/palette/generator.c b/apps/nesemu2/src/palette/generator.c deleted file mode 100644 index 94fd501e..00000000 --- a/apps/nesemu2/src/palette/generator.c +++ /dev/null @@ -1,171 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -//palette generator borrowed from nintendulator - -#include -#include "palette/palette.h" - -#ifndef M_PI -#define M_PI 3.14159f -#endif - -#define CLIP(x,min,max) (((x) > (max)) ? (max) : (((x) < (min)) ? (min) : (x))) - -static int getPhase (double *wave) -{ - double max = -999, min = 999; - double amp, offset; - double angle = 0, base; - int i, j, k; - for (i = 0; i < 12; i++) - { - if (wave[i] > max) - max = wave[i]; - if (wave[i] < min) - min = wave[i]; - } - amp = (max - min) / 2; - offset = (max + min) / 2; - - for (k = 0; k < 3; k++) - { - double error[12], curerror = 0; - double segsize = 360; - for (i = 0; i <= k; i++) - segsize /= 12.0; - - for (j = 0; j < 12; j++) - { - error[j] = 0; - for (i = 0; i < 12; i++) - error[j] += fabs((amp * sin((i * 30 + j * segsize + angle) * M_PI / 180.0) + offset) - wave[i]); - curerror += error[j]; - } - base = 0; - for (j = 0; j < 12; j++) - { - if (error[j] < curerror) - { - base = j * segsize; - curerror = error[j]; - } - } - angle += base; - } - - return (int)angle; -} - -static void GenerateNTSC(palette_t *palette,int hue,int sat) -{ - const double black = 0.519; - const double white = 1.443; - const double voltage[2][4] = {{1.094,1.506,1.962,1.962},{0.350,0.519,0.962,1.550}}; - - const char phases[12][12] = { - {1,1,1,1,1,1,0,0,0,0,0,0}, - {1,1,1,1,1,0,0,0,0,0,0,1}, // blue - {1,1,1,1,0,0,0,0,0,0,1,1}, - {1,1,1,0,0,0,0,0,0,1,1,1}, // magenta - {1,1,0,0,0,0,0,0,1,1,1,1}, - {1,0,0,0,0,0,0,1,1,1,1,1}, // red - {0,0,0,0,0,0,1,1,1,1,1,1}, - {0,0,0,0,0,1,1,1,1,1,1,0}, // yellow - {0,0,0,0,1,1,1,1,1,1,0,0}, - {0,0,0,1,1,1,1,1,1,0,0,0}, // green - {0,0,1,1,1,1,1,1,0,0,0,0}, - {0,1,1,1,1,1,1,0,0,0,0,0}, // cyan - }; - const char emphasis[8][12] = { - {0,0,0,0,0,0,0,0,0,0,0,0}, // none - {0,1,1,1,1,1,1,0,0,0,0,0}, // red - {1,1,1,0,0,0,0,0,0,1,1,1}, // green - {1,1,1,1,1,1,1,0,0,1,1,1}, // yellow - {0,0,0,0,0,1,1,1,1,1,1,0}, // blue - {0,1,1,1,1,1,1,1,1,1,1,0}, // magenta - {1,1,1,0,0,1,1,1,1,1,1,1}, // cyan - {1,1,1,1,1,1,1,1,1,1,1,1} // all - }; - - int i, x, y, z; - for (x = 0; x < 8; x++) - { - for (y = 0; y < 4; y++) - { - for (z = 0; z < 16; z++) - { - double wave[12]; - double Y, I, Q; - double R, G, B; - - double H = 0, S = 0; - - for (i = 0; i < 12; i++) - { - if (z == 0) - wave[i] = voltage[0][y]; - else if (z < 13) - wave[i] = phases[z-1][i] ? voltage[0][y] : voltage[1][y]; - else if (z == 13) - wave[i] = voltage[1][y]; - else wave[i] = black; - if ((emphasis[x][i]) && (z < 14)) - wave[i] = wave[i] * 0.75; - } - - Y = 0.0; S = 0; - for (i = 0; i < 12; i++) - Y += wave[i] / 12.0; - for (i = 0; i < 12; i++) - S += (wave[i] - Y) * (wave[i] - Y); - Y = (Y - black) / white; - S = S / white; // don't remove black offset, since this is already relative - S = sqrt(S / 12.0) * sat / 50.0; - - H = M_PI * (270 + getPhase(wave) + hue) / 180.0; - - I = S * sin(H); - Q = S * cos(H); - - R = Y + 0.956 * I + 0.621 * Q; - G = Y - 0.272 * I - 0.647 * Q; - B = Y - 1.107 * I + 1.705 * Q; - - R *= 256; - G *= 256; - B *= 256; - - palette->pal[x][(y << 4) | z].r = (unsigned char)CLIP(R,0,255); - palette->pal[x][(y << 4) | z].g = (unsigned char)CLIP(G,0,255); - palette->pal[x][(y << 4) | z].b = (unsigned char)CLIP(B,0,255); - } - } - } -} - -palette_t *palette_generate(int hue,int sat) //generate nes palette -{ - palette_t *ret = 0; - - ret = palette_create(); - GenerateNTSC(ret,hue,sat); - return(ret); -} diff --git a/apps/nesemu2/src/palette/generator.h b/apps/nesemu2/src/palette/generator.h deleted file mode 100644 index d8b5b08a..00000000 --- a/apps/nesemu2/src/palette/generator.h +++ /dev/null @@ -1,28 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __generator_h__ -#define __generator_h__ - -#include "palette/palette.h" - -palette_t *palette_generate(int hue,int sat); - -#endif diff --git a/apps/nesemu2/src/palette/palette.c b/apps/nesemu2/src/palette/palette.c index 27010c17..abdcb3ef 100644 --- a/apps/nesemu2/src/palette/palette.c +++ b/apps/nesemu2/src/palette/palette.c @@ -18,20 +18,104 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include "palette/palette.h" #include +#include "system/video.h" +#if 0 #include #include #include "misc/memutil.h" #include "misc/strutil.h" #include "misc/log.h" #include "misc/config.h" -#include "palette/palette.h" #include "palette/generator.h" -#include "system/video.h" #include "misc/paths.h" +#endif -static palette_t *pal = 0; +static palette_t pal = {{ +{ + 0x00666666,0x00723700,0x00a51d00,0x00ad003d,0x00850060,0x00420073,0x00000571,0x00001d5a, + 0x00003634,0x00004b0a,0x00005500,0x00055200,0x002b4b00,0x00000000,0x00000000,0x00000000, + 0x00afafaf,0x00e6600f,0x00ff3e41,0x00ff2378,0x00d815a7,0x007f1ac1,0x001c2ebe,0x00004e9f, + 0x0000706d,0x00008c36,0x00009907,0x002f9500,0x00618c00,0x00000000,0x00000000,0x00000000, + 0x00ffffff,0x00ffc14b,0x00ff9e75,0x00ff72c9,0x00ff64f8,0x00cf68ff,0x009d71ff,0x00169ef0, + 0x0000c0be,0x0000dc85,0x0024e956,0x007ee53c,0x00e3d03f,0x004e4e4e,0x00000000,0x00000000, + 0x00ffffff,0x00ffe6b5,0x00ffd7c7,0x00ffcadd,0x00ffc1f3,0x00ecc1ff,0x00d7c5ff,0x00afd0ff, + 0x008ce5e4,0x008becd9,0x0098f5c2,0x00caf5af,0x00dff1ae,0x00b6b6b6,0x00000000,0x00000000, +}, +{ + 0x004a3f6b,0x00660f00,0x008a000e,0x00890037,0x00760052,0x0039006d,0x00000072,0x00000461, + 0x0000114a,0x00002523,0x00003200,0x00003300,0x00281f00,0x00000000,0x00000000,0x00000000, + 0x008979b6,0x00b1370f,0x00dd1f3b,0x00dd0b6f,0x00c50394,0x007306b9,0x004010c5,0x00002ca8, + 0x00003e89,0x00005855,0x00006921,0x00006a00,0x003c5600,0x00000002,0x00000000,0x00000000, + 0x00cfbaff,0x00fa785f,0x00ff569f,0x00ff4ac2,0x00ff43e0,0x00bd46ff,0x00854fff,0x001d6cfb, + 0x000083d5,0x00009f9c,0x0000a284,0x0031ae41,0x00809744,0x00362c53,0x00000000,0x00000000, + 0x00cfbaff,0x00e99cc4,0x00f093d9,0x00fe8ee2,0x00d08dfc,0x00d589ff,0x00b08eff,0x008b99ff, + 0x0070a4f2,0x0061afde,0x0066b3ce,0x0071bab8,0x00afabb8,0x00907fbe,0x00000000,0x00000000, +}, +{ + 0x00215e3d,0x003d3000,0x006b1600,0x00790009,0x00490031,0x0017004a,0x0000004d,0x0000153b, + 0x00002927,0x00004300,0x00005300,0x00004d00,0x00004300,0x00000000,0x00000000,0x00000000, + 0x0050a577,0x00776600,0x00b44401,0x00c71f37,0x00850d67,0x003e1089,0x0000248c,0x00004375, + 0x00005d5b,0x00008024,0x00009600,0x00008e00,0x00238100,0x00000000,0x00000000,0x00000000, + 0x0084f2b7,0x00c0ae23,0x00cf9d32,0x00ff6878,0x00cf5c9a,0x00735bcb,0x00097bc0,0x000091b3, + 0x0000a5a2,0x0000cb67,0x0000e42d,0x001ddb20,0x0066cb18,0x0011482a,0x00000000,0x00000000, + 0x0084f2b7,0x009dd877,0x00abce7f,0x00c6bf8e,0x00a3b4ab,0x0078b3c3,0x0059bebd,0x0047c3c1, + 0x003cd89f,0x002fde9e,0x0050ee78,0x0058e87c,0x007be176,0x0055ac7d,0x00000000,0x00000000, +}, +{ + 0x001a3f47,0x00371700,0x00620200,0x00690018,0x004b0031,0x0021004a,0x0000004d,0x0000053d, + 0x00001d1b,0x0000240e,0x00002f00,0x00003100,0x00082300,0x00000000,0x00000000,0x00000000, + 0x00417e7d,0x006e4400,0x00a8280b,0x00b00946,0x008a0067,0x00520088,0x0000198c,0x00002d77, + 0x00004c4a,0x00005639,0x00006517,0x00006800,0x00295600,0x00000000,0x00000000,0x00000000, + 0x0071c0bf,0x00978925,0x00e5674b,0x00e6458e,0x00c73fa5,0x00933ccb,0x000d5bcd,0x00006cbb, + 0x00008d8a,0x00009974,0x0000a854,0x001ca922,0x00569723,0x00082f2f,0x00000000,0x00000000, + 0x0071c0bf,0x00aba678,0x00d3938d,0x00d086a7,0x00b484b7,0x007e87ca,0x004896c6,0x003a9fba, + 0x002eaaa9,0x002eaaa9,0x002cb398,0x0050b77d,0x0077ae7c,0x004b808b,0x00000000,0x00000000, +}, +{ + 0x007d4b40,0x008b2000,0x00ac1500,0x00b2001e,0x009d0032,0x006c004b,0x002a0053,0x00000045, + 0x00001716,0x00002e00,0x00003b00,0x000f3a00,0x00433500,0x00000000,0x00000000,0x00000000, + 0x00cf8a7b,0x00e15200,0x00ff4214,0x00ff1d4e,0x00f90f69,0x00b7048a,0x005e0a95,0x0000237f, + 0x00004544,0x00006514,0x00007400,0x003a7400,0x00806d00,0x000b0000,0x00000000,0x00000000, + 0x00ffd0bc,0x00ff9839,0x00ff7275,0x00ff628e,0x00ff50b2,0x00ff47d4,0x00d94ad0,0x004667c4, + 0x001a8887,0x0010ab53,0x0061b729,0x0097b81e,0x00cab61c,0x0063372d,0x00000000,0x00000000, + 0x00ffd0bc,0x00ffbe82,0x00ffb291,0x00ffa4a6,0x00ff9bb9,0x00ff96c7,0x00ff97ca,0x00dc9acd, + 0x00bdafac,0x00b2c38c,0x00cfc682,0x00d6cc79,0x00ffbc86,0x00d79181,0x00000000,0x00000000, +}, +{ + 0x005e3049,0x00610e00,0x008f0009,0x00890022,0x007a0034,0x0051004a,0x00270054,0x00000041, + 0x00000922,0x00001908,0x00002a00,0x00002700,0x00261c00,0x00000000,0x00000000,0x00000000, + 0x00a56688,0x00a73900,0x00e51532,0x00dc0554,0x00ab007f,0x00920089,0x005a0096,0x00001d7c, + 0x00003057,0x00004731,0x00005d00,0x00115a00,0x00584b00,0x00000000,0x00000000,0x00000000, + 0x00f3a0cd,0x00eb7833,0x00ff4d7a,0x00ff4195,0x00f334c3,0x00d333d0,0x009c38dc,0x002157c1, + 0x0006689d,0x00007f7d,0x00139b35,0x00649522,0x00978925,0x00481f35,0x00000000,0x00000000, + 0x00f3a0cd,0x00f28e90,0x00ff7daf,0x00fc78bc,0x00e775ca,0x00e775ca,0x00ce75d4,0x009282cd, + 0x007890b6,0x005da1a0,0x0061ab8a,0x0093ab76,0x00b6a27a,0x00ac6b8e,0x00000000,0x00000000, +}, +{ + 0x00494528,0x00492100,0x006d1200,0x007f000c,0x005f0024,0x003d0037,0x00010040,0x0000012d, + 0x00001412,0x00002900,0x00003900,0x00003600,0x00123000,0x00000000,0x00000000,0x00000000, + 0x007a8658,0x00885200,0x00b83e00,0x00cf1136,0x00a70452,0x0077006f,0x0027087b,0x00002861, + 0x0000403e,0x00005d16,0x00007200,0x00116d00,0x006c5a00,0x00000000,0x00000000,0x00000000, + 0x00bbcb8f,0x00d39119,0x00ff7d2d,0x00ff526f,0x00eb4787,0x00c73fa5,0x006648b8,0x00006d97, + 0x00007f7d,0x00009e54,0x001fb516,0x004db10d,0x007ea80b,0x002c3417,0x00000000,0x00000000, + 0x00bbcb8f,0x00beb360,0x00d9ab67,0x00ec9585,0x00d68b9c,0x00c284b2,0x009e86bb,0x006698ac, + 0x005ba39b,0x005eb974,0x0084bf5e,0x009ebb5d,0x00a1ba5d,0x00808d5e,0x00000000,0x00000000, +}, +{ + 0x00353535,0x00540900,0x006e0000,0x006b0016,0x004c0031,0x001a003f,0x0000003e,0x00000037, + 0x00001210,0x00002100,0x00002900,0x00002600,0x00092100,0x00000000,0x00000000,0x00000000, + 0x006c6c6c,0x00953100,0x00b71719,0x00b30343,0x008b0066,0x00480079,0x00000c77,0x00002360, + 0x00003d3b,0x00005211,0x00005c00,0x000c5800,0x00325200,0x00000000,0x00000000,0x00000000, + 0x00a8a8a8,0x00b67a21,0x00e86041,0x00f8476a,0x00e13793,0x008437b6,0x005e3eb8,0x00005f9d, + 0x00007977,0x00008e4d,0x00049829,0x00489516,0x006e8e14,0x00232323,0x00000000,0x00000000, + 0x00a8a8a8,0x00ae9571,0x00c38a7e,0x00c9808f,0x00c07a9f,0x00a878ab,0x008a7daf,0x006c85a9, + 0x0057909c,0x00549e83,0x005ba07b,0x0072a26f,0x00a09a6d,0x00727272,0x00000000,0x00000000, +} +}}; +#if 0 /* measurement by Quietust */ static const double emphasis_factor[8][3]={ {1.00, 1.00, 1.00}, @@ -57,15 +141,6 @@ static void generate_emphasis(palette_t *p) } } -palette_t *palette_create() -{ - palette_t *ret = 0; - - ret = (palette_t*)mem_alloc(sizeof(palette_t)); - memset(ret,0,sizeof(palette_t)); - return(ret); -} - void palette_destroy(palette_t *p) { mem_free(p); @@ -107,6 +182,20 @@ int palette_save(char *filename,palette_t *p) return(1); } +void palette_kill() +{ + palette_destroy(pal); +} + +palette_t *palette_create() +{ + palette_t *ret = 0; + + ret = (palette_t*)mem_alloc(sizeof(palette_t)); + memset(ret,0,sizeof(palette_t)); + return(ret); +} + int palette_init() { char file[1024]; @@ -129,11 +218,17 @@ int palette_init() if(pal == 0) { pal = palette_generate(config_get_int("palette.hue"),config_get_int("palette.saturation")); } - video_setpalette(pal); + video_setpalette(&pal); + return(0); } -void palette_kill() -{ - palette_destroy(pal); +#endif + +palette_t *palette_load(char *filename) { return NULL; } +int palette_save(char *filename,palette_t *p) {return 1; } +int palette_init() { + video_setpalette(&pal); + return 0; } +void palette_kill() { } diff --git a/apps/nesemu2/src/palette/palette.h b/apps/nesemu2/src/palette/palette.h index 3205250c..8a5c5331 100644 --- a/apps/nesemu2/src/palette/palette.h +++ b/apps/nesemu2/src/palette/palette.h @@ -23,12 +23,15 @@ #include "types.h" -typedef struct palentry_s { - u8 r,g,b; +typedef union palentry_s { + struct { + u8 r,g,b; + }; + u32 val; } palentry_t; typedef struct palette_s { - palentry_t pal[8][64]; + u32 pal[8][64]; } palette_t; palette_t *palette_create(); diff --git a/apps/nesemu2/src/roms/.gitignore b/apps/nesemu2/src/roms/.gitignore new file mode 100644 index 00000000..68359a78 --- /dev/null +++ b/apps/nesemu2/src/roms/.gitignore @@ -0,0 +1,2 @@ +*.c +*.h diff --git a/apps/nesemu2/src/roms/build-roms.py b/apps/nesemu2/src/roms/build-roms.py new file mode 120000 index 00000000..06c83c84 --- /dev/null +++ b/apps/nesemu2/src/roms/build-roms.py @@ -0,0 +1 @@ +../../../litenes/src/roms/build-roms.py \ No newline at end of file diff --git a/apps/nesemu2/src/roms/rom b/apps/nesemu2/src/roms/rom new file mode 120000 index 00000000..9040f3c9 --- /dev/null +++ b/apps/nesemu2/src/roms/rom @@ -0,0 +1 @@ +../../../litenes/src/roms/rom \ No newline at end of file diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.h b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.h deleted file mode 100644 index 1f53187e..00000000 --- a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.h +++ /dev/null @@ -1,192 +0,0 @@ -/* NES NTSC video filter */ - -/* nes_ntsc 0.2.2 */ -#ifndef NES_NTSC_H -#define NES_NTSC_H - -#include "nes_ntsc_config.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown -in parenthesis and should remain fairly stable in future versions. */ -typedef struct nes_ntsc_setup_t -{ - /* Basic parameters */ - double hue; /* -1 = -180 degrees +1 = +180 degrees */ - double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ - double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ - double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ - double sharpness; /* edge contrast enhancement/blurring */ - - /* Advanced parameters */ - double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ - double resolution; /* image resolution */ - double artifacts; /* artifacts caused by color changes */ - double fringing; /* color artifacts caused by brightness changes */ - double bleed; /* color bleed (color resolution reduction) */ - int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ - - unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ - - /* You can replace the standard NES color generation with an RGB palette. The - first replaces all color generation, while the second replaces only the core - 64-color generation and does standard color emphasis calculations on it. */ - unsigned char const* palette;/* optional 512-entry RGB palette in, 3 bytes per color */ - unsigned char const* base_palette;/* optional 64-entry RGB palette in, 3 bytes per color */ -} nes_ntsc_setup_t; - -/* Video format presets */ -extern nes_ntsc_setup_t const nes_ntsc_composite; /* color bleeding + artifacts */ -extern nes_ntsc_setup_t const nes_ntsc_svideo; /* color bleeding only */ -extern nes_ntsc_setup_t const nes_ntsc_rgb; /* crisp image */ -extern nes_ntsc_setup_t const nes_ntsc_monochrome;/* desaturated + artifacts */ - -#ifdef NES_NTSC_EMPHASIS - enum { nes_ntsc_palette_size = 64 * 8 }; -#else - enum { nes_ntsc_palette_size = 64 }; -#endif - -/* Initializes and adjusts parameters. Can be called multiple times on the same -nes_ntsc_t object. Can pass NULL for either parameter. */ -typedef struct nes_ntsc_t nes_ntsc_t; -void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup ); - -/* Filters one or more rows of pixels. Input pixels are 6/9-bit palette indicies. -In_row_width is the number of pixels to get to the next input row. Out_pitch -is the number of *bytes* to get to the next output row. Output pixel format -is set by NES_NTSC_OUT_DEPTH (defaults to 16-bit RGB). */ -void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* nes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ); - -/* Number of output pixels written by blitter for given input width. Width might -be rounded down slightly; use NES_NTSC_IN_WIDTH() on result to find rounded -value. Guaranteed not to round 256 down at all. */ -#define NES_NTSC_OUT_WIDTH( in_width ) \ - ((((in_width) - 1) / nes_ntsc_in_chunk + 1) * nes_ntsc_out_chunk) - -/* Number of input pixels that will fit within given output width. Might be -rounded down slightly; use NES_NTSC_OUT_WIDTH() on result to find rounded -value. */ -#define NES_NTSC_IN_WIDTH( out_width ) \ - (((out_width) / nes_ntsc_out_chunk - 1) * nes_ntsc_in_chunk + 1) - - -/* Interface for user-defined custom blitters */ - -enum { nes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ -enum { nes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ -enum { nes_ntsc_black = 15 }; /* palette index for black */ -enum { nes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ - -/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. -Use nes_ntsc_black for unused pixels. Declares variables, so must be before first -statement in a block (unless you're using C++). */ -#define NES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ - char const* const ktable = \ - (char const*) (ntsc)->table [0] + burst * (nes_ntsc_burst_size * sizeof (nes_ntsc_rgb_t));\ - NES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, NES_NTSC_ENTRY_, ktable ) - -/* Begins input pixel */ -#define NES_NTSC_COLOR_IN( in_index, color_in ) \ - NES_NTSC_COLOR_IN_( in_index, color_in, NES_NTSC_ENTRY_, ktable ) - -/* Generates output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: -24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) -16: RRRRRGGG GGGBBBBB (5-6-5 RGB) -15: RRRRRGG GGGBBBBB (5-5-5 RGB) - 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ -#define NES_NTSC_RGB_OUT( index, rgb_out, bits ) \ - NES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 ) - - -/* private */ -enum { nes_ntsc_entry_size = 128 }; -typedef unsigned long nes_ntsc_rgb_t; -struct nes_ntsc_t { - nes_ntsc_rgb_t table [nes_ntsc_palette_size] [nes_ntsc_entry_size]; -}; -enum { nes_ntsc_burst_size = nes_ntsc_entry_size / nes_ntsc_burst_count }; - -#define NES_NTSC_ENTRY_( ktable, n ) \ - (nes_ntsc_rgb_t const*) (ktable + (n) * (nes_ntsc_entry_size * sizeof (nes_ntsc_rgb_t))) - -/* deprecated */ -#define NES_NTSC_RGB24_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 24 ) -#define NES_NTSC_RGB16_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 16 ) -#define NES_NTSC_RGB15_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 15 ) -#define NES_NTSC_RAW_OUT( x, out ) NES_NTSC_RGB_OUT( x, out, 0 ) - -enum { nes_ntsc_min_in_width = 256 }; -enum { nes_ntsc_min_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_min_in_width ) }; - -enum { nes_ntsc_640_in_width = 271 }; -enum { nes_ntsc_640_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_640_in_width ) }; -enum { nes_ntsc_640_overscan_left = 8 }; -enum { nes_ntsc_640_overscan_right = nes_ntsc_640_in_width - 256 - nes_ntsc_640_overscan_left }; - -enum { nes_ntsc_full_in_width = 283 }; -enum { nes_ntsc_full_out_width = NES_NTSC_OUT_WIDTH( nes_ntsc_full_in_width ) }; -enum { nes_ntsc_full_overscan_left = 16 }; -enum { nes_ntsc_full_overscan_right = nes_ntsc_full_in_width - 256 - nes_ntsc_full_overscan_left }; - -/* common 3->7 ntsc macros */ -#define NES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ - unsigned const nes_ntsc_pixel0_ = (pixel0);\ - nes_ntsc_rgb_t const* kernel0 = ENTRY( table, nes_ntsc_pixel0_ );\ - unsigned const nes_ntsc_pixel1_ = (pixel1);\ - nes_ntsc_rgb_t const* kernel1 = ENTRY( table, nes_ntsc_pixel1_ );\ - unsigned const nes_ntsc_pixel2_ = (pixel2);\ - nes_ntsc_rgb_t const* kernel2 = ENTRY( table, nes_ntsc_pixel2_ );\ - nes_ntsc_rgb_t const* kernelx0;\ - nes_ntsc_rgb_t const* kernelx1 = kernel0;\ - nes_ntsc_rgb_t const* kernelx2 = kernel0 - -#define NES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ - nes_ntsc_rgb_t raw_ =\ - kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ - kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ - NES_NTSC_CLAMP_( raw_, shift );\ - NES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ -} - -/* common ntsc macros */ -#define nes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) -#define nes_ntsc_clamp_mask (nes_ntsc_rgb_builder * 3 / 2) -#define nes_ntsc_clamp_add (nes_ntsc_rgb_builder * 0x101) -#define NES_NTSC_CLAMP_( io, shift ) {\ - nes_ntsc_rgb_t sub = (io) >> (9-(shift)) & nes_ntsc_clamp_mask;\ - nes_ntsc_rgb_t clamp = nes_ntsc_clamp_add - sub;\ - io |= clamp;\ - clamp -= sub;\ - io &= clamp;\ -} - -#define NES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ - unsigned color_;\ - kernelx##index = kernel##index;\ - kernel##index = (color_ = (color), ENTRY( table, color_ ));\ -} - -/* x is always zero except in snes_ntsc library */ -#define NES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ - if ( bits == 16 )\ - rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ - if ( bits == 24 || bits == 32 )\ - rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ - if ( bits == 15 )\ - rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ - if ( bits == 0 )\ - rgb_out = raw_ << x;\ -} - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl deleted file mode 100644 index 004abb6f..00000000 --- a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc.inl +++ /dev/null @@ -1,289 +0,0 @@ -/* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ - -#include "nes_ntsc.h" - -/* Copyright (C) 2006-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -nes_ntsc_setup_t const nes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0, 0, 0 }; -nes_ntsc_setup_t const nes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; -nes_ntsc_setup_t const nes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0, 0, 0 }; -nes_ntsc_setup_t const nes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0, 0, 0 }; - -#define alignment_count 3 -#define burst_count 3 -#define rescale_in 8 -#define rescale_out 7 - -#define artifacts_mid 1.0f -#define fringing_mid 1.0f -#define std_decoder_hue -15 - -#define STD_HUE_CONDITION( setup ) !(setup->base_palette || setup->palette) - -#include "nes_ntsc_impl.h" - -/* 3 input pixels -> 8 composite samples */ -pixel_info_t const nes_ntsc_pixels [alignment_count] = { - { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, - { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, - { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, -}; - -static void merge_kernel_fields( nes_ntsc_rgb_t* io ) -{ - int n; - for ( n = burst_size; n; --n ) - { - nes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; - nes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; - nes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; - /* merge colors without losing precision */ - io [burst_size * 0] = - ((p0 + p1 - ((p0 ^ p1) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; - io [burst_size * 1] = - ((p1 + p2 - ((p1 ^ p2) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; - io [burst_size * 2] = - ((p2 + p0 - ((p2 ^ p0) & nes_ntsc_rgb_builder)) >> 1) - rgb_bias; - ++io; - } -} - -static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ) -{ - int n; - for ( n = burst_count; n; --n ) - { - unsigned i; - for ( i = 0; i < rgb_kernel_size / 2; i++ ) - { - nes_ntsc_rgb_t error = color - - out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - - out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; - DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); - } - out += alignment_count * rgb_kernel_size; - } -} - -void nes_ntsc_init( nes_ntsc_t* ntsc, nes_ntsc_setup_t const* setup ) -{ - int merge_fields; - int entry; - init_t impl; - float gamma_factor; - - if ( !setup ) - setup = &nes_ntsc_composite; - init( &impl, setup ); - - /* setup fast gamma */ - { - float gamma = (float) setup->gamma * -0.5f; - if ( STD_HUE_CONDITION( setup ) ) - gamma += 0.1333f; - - gamma_factor = (float) pow( (float) fabs( gamma ), 0.73f ); - if ( gamma < 0 ) - gamma_factor = -gamma_factor; - } - - merge_fields = setup->merge_fields; - if ( setup->artifacts <= -1 && setup->fringing <= -1 ) - merge_fields = 1; - - for ( entry = 0; entry < nes_ntsc_palette_size; entry++ ) - { - /* Base 64-color generation */ - static float const lo_levels [4] = { -0.12f, 0.00f, 0.31f, 0.72f }; - static float const hi_levels [4] = { 0.40f, 0.68f, 1.00f, 1.00f }; - int level = entry >> 4 & 0x03; - float lo = lo_levels [level]; - float hi = hi_levels [level]; - - int color = entry & 0x0F; - if ( color == 0 ) - lo = hi; - if ( color == 0x0D ) - hi = lo; - if ( color > 0x0D ) - hi = lo = 0.0f; - - { - /* phases [i] = cos( i * PI / 6 ) */ - static float const phases [0x10 + 3] = { - -1.0f, -0.866025f, -0.5f, 0.0f, 0.5f, 0.866025f, - 1.0f, 0.866025f, 0.5f, 0.0f, -0.5f, -0.866025f, - -1.0f, -0.866025f, -0.5f, 0.0f, 0.5f, 0.866025f, - 1.0f - }; - #define TO_ANGLE_SIN( color ) phases [color] - #define TO_ANGLE_COS( color ) phases [(color) + 3] - - /* Convert raw waveform to YIQ */ - float sat = (hi - lo) * 0.5f; - float i = TO_ANGLE_SIN( color ) * sat; - float q = TO_ANGLE_COS( color ) * sat; - float y = (hi + lo) * 0.5f; - - /* Optionally use base palette instead */ - if ( setup->base_palette ) - { - unsigned char const* in = &setup->base_palette [(entry & 0x3F) * 3]; - static float const to_float = 1.0f / 0xFF; - float r = to_float * in [0]; - float g = to_float * in [1]; - float b = to_float * in [2]; - q = RGB_TO_YIQ( r, g, b, y, i ); - } - - /* Apply color emphasis */ - #ifdef NES_NTSC_EMPHASIS - { - int tint = entry >> 6 & 7; - if ( tint && color <= 0x0D ) - { - static float const atten_mul = 0.79399f; - static float const atten_sub = 0.0782838f; - - if ( tint == 7 ) - { - y = y * (atten_mul * 1.13f) - (atten_sub * 1.13f); - } - else - { - static unsigned char const tints [8] = { 0, 6, 10, 8, 2, 4, 0, 0 }; - int const tint_color = tints [tint]; - float sat = hi * (0.5f - atten_mul * 0.5f) + atten_sub * 0.5f; - y -= sat * 0.5f; - if ( tint >= 3 && tint != 4 ) - { - /* combined tint bits */ - sat *= 0.6f; - y -= sat; - } - i += TO_ANGLE_SIN( tint_color ) * sat; - q += TO_ANGLE_COS( tint_color ) * sat; - } - } - } - #endif - - /* Optionally use palette instead */ - if ( setup->palette ) - { - unsigned char const* in = &setup->palette [entry * 3]; - static float const to_float = 1.0f / 0xFF; - float r = to_float * in [0]; - float g = to_float * in [1]; - float b = to_float * in [2]; - q = RGB_TO_YIQ( r, g, b, y, i ); - } - - /* Apply brightness, contrast, and gamma */ - y *= (float) setup->contrast * 0.5f + 1; - /* adjustment reduces error when using input palette */ - y += (float) setup->brightness * 0.5f - 0.5f / 256; - - { - float r, g, b = YIQ_TO_RGB( y, i, q, default_decoder, float, r, g ); - - /* fast approximation of n = pow( n, gamma ) */ - r = (r * gamma_factor - gamma_factor) * r + r; - g = (g * gamma_factor - gamma_factor) * g + g; - b = (b * gamma_factor - gamma_factor) * b + b; - - q = RGB_TO_YIQ( r, g, b, y, i ); - } - - i *= rgb_unit; - q *= rgb_unit; - y *= rgb_unit; - y += rgb_offset; - - /* Generate kernel */ - { - int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); - /* blue tends to overflow, so clamp it */ - nes_ntsc_rgb_t rgb = PACK_RGB( r, g, (b < 0x3E0 ? b: 0x3E0) ); - - if ( setup->palette_out ) - RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); - - if ( ntsc ) - { - nes_ntsc_rgb_t* kernel = ntsc->table [entry]; - gen_kernel( &impl, y, i, q, kernel ); - if ( merge_fields ) - merge_kernel_fields( kernel ); - correct_errors( rgb, kernel ); - } - } - } - } -} - -#ifndef NES_NTSC_NO_BLITTERS - -void nes_ntsc_blit( nes_ntsc_t const* ntsc, NES_NTSC_IN_T const* input, long in_row_width, - int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) -{ - int chunk_count = (in_width - 1) / nes_ntsc_in_chunk; - for ( ; in_height; --in_height ) - { - NES_NTSC_IN_T const* line_in = input; - NES_NTSC_BEGIN_ROW( ntsc, burst_phase, - nes_ntsc_black, nes_ntsc_black, NES_NTSC_ADJ_IN( *line_in ) ); - nes_ntsc_out_t* restrict line_out = (nes_ntsc_out_t*) rgb_out; - int n; - ++line_in; - - for ( n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - NES_NTSC_COLOR_IN( 0, NES_NTSC_ADJ_IN( line_in [0] ) ); - NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 1, NES_NTSC_ADJ_IN( line_in [1] ) ); - NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 2, NES_NTSC_ADJ_IN( line_in [2] ) ); - NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); - - line_in += 3; - line_out += 7; - } - - /* finish final pixels */ - NES_NTSC_COLOR_IN( 0, nes_ntsc_black ); - NES_NTSC_RGB_OUT( 0, line_out [0], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 1, line_out [1], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 1, nes_ntsc_black ); - NES_NTSC_RGB_OUT( 2, line_out [2], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 3, line_out [3], NES_NTSC_OUT_DEPTH ); - - NES_NTSC_COLOR_IN( 2, nes_ntsc_black ); - NES_NTSC_RGB_OUT( 4, line_out [4], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 5, line_out [5], NES_NTSC_OUT_DEPTH ); - NES_NTSC_RGB_OUT( 6, line_out [6], NES_NTSC_OUT_DEPTH ); - - burst_phase = (burst_phase + 1) % nes_ntsc_burst_count; - input += in_row_width; - rgb_out = (char*) rgb_out + out_pitch; - } -} - -#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h deleted file mode 100644 index 35b5637e..00000000 --- a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_config.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Configure library by modifying this file */ - -#ifndef NES_NTSC_CONFIG_H -#define NES_NTSC_CONFIG_H - -/* Uncomment to enable emphasis support and use a 512 color palette instead -of the base 64 color palette. */ -#define NES_NTSC_EMPHASIS 1 - -/* The following affect the built-in blitter only; a custom blitter can -handle things however it wants. */ - -/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ -#define NES_NTSC_OUT_DEPTH 32 - -/* Type of input pixel values. You'll probably use unsigned short -if you enable emphasis above. */ -#define NES_NTSC_IN_T unsigned char - -/* Each raw pixel input value is passed through this. You might want to mask -the pixel index if you use the high bits as flags, etc. */ -#define NES_NTSC_ADJ_IN( in ) in - -/* For each pixel, this is the basic operation: -output_color = color_palette [NES_NTSC_ADJ_IN( NES_NTSC_IN_T )] */ - -#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h b/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h deleted file mode 100644 index de3672b4..00000000 --- a/apps/nesemu2/src/system/common/filters/ntsc/nes_ntsc/nes_ntsc_impl.h +++ /dev/null @@ -1,439 +0,0 @@ -/* nes_ntsc 0.2.2. http://www.slack.net/~ant/ */ - -/* Common implementation of NTSC filters */ - -#include -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module 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 Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#define DISABLE_CORRECTION 0 - -#undef PI -#define PI 3.14159265358979323846f - -#ifndef LUMA_CUTOFF - #define LUMA_CUTOFF 0.20 -#endif -#ifndef gamma_size - #define gamma_size 1 -#endif -#ifndef rgb_bits - #define rgb_bits 8 -#endif -#ifndef artifacts_max - #define artifacts_max (artifacts_mid * 1.5f) -#endif -#ifndef fringing_max - #define fringing_max (fringing_mid * 2) -#endif -#ifndef STD_HUE_CONDITION - #define STD_HUE_CONDITION( setup ) 1 -#endif - -#define ext_decoder_hue (std_decoder_hue + 15) -#define rgb_unit (1 << rgb_bits) -#define rgb_offset (rgb_unit * 2 + 0.5f) - -enum { burst_size = nes_ntsc_entry_size / burst_count }; -enum { kernel_half = 16 }; -enum { kernel_size = kernel_half * 2 + 1 }; - -typedef struct init_t -{ - float to_rgb [burst_count * 6]; - float to_float [gamma_size]; - float contrast; - float brightness; - float artifacts; - float fringing; - float kernel [rescale_out * kernel_size * 2]; -} init_t; - -#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ - float t;\ - t = i * cos_b - q * sin_b;\ - q = i * sin_b + q * cos_b;\ - i = t;\ -} - -static void init_filters( init_t* impl, nes_ntsc_setup_t const* setup ) -{ -#if rescale_out > 1 - float kernels [kernel_size * 2]; -#else - float* const kernels = impl->kernel; -#endif - - /* generate luma (y) filter using sinc kernel */ - { - /* sinc with rolloff (dsf) */ - float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; - float const maxh = 32; - float const pow_a_n = (float) pow( rolloff, maxh ); - float sum; - int i; - /* quadratic mapping to reduce negative (blurring) range */ - float to_angle = (float) setup->resolution + 1; - to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); - - kernels [kernel_size * 3 / 2] = maxh; /* default center value */ - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = i - kernel_half; - float angle = x * to_angle; - /* instability occurs at center point with rolloff very close to 1.0 */ - if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) - { - float rolloff_cos_a = rolloff * (float) cos( angle ); - float num = 1 - rolloff_cos_a - - pow_a_n * (float) cos( maxh * angle ) + - pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); - float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - float dsf = num / den; - kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; - } - } - - /* apply blackman window and find sum */ - sum = 0; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - float x = PI * 2 / (kernel_half * 2) * i; - float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); - sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); - } - - /* normalize kernel */ - sum = 1.0f / sum; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = kernel_size * 3 / 2 - kernel_half + i; - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - - /* generate chroma (iq) filter using gaussian kernel */ - { - float const cutoff_factor = -0.03125f; - float cutoff = (float) setup->bleed; - int i; - - if ( cutoff < 0 ) - { - /* keep extreme value accessible only near upper end of scale (1.0) */ - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= -30.0f / 0.65f; - } - cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; - - for ( i = -kernel_half; i <= kernel_half; i++ ) - kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); - - /* normalize even and odd phases separately */ - for ( i = 0; i < 2; i++ ) - { - float sum = 0; - int x; - for ( x = i; x < kernel_size; x += 2 ) - sum += kernels [x]; - - sum = 1.0f / sum; - for ( x = i; x < kernel_size; x += 2 ) - { - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - } - - /* - printf( "luma:\n" ); - for ( i = kernel_size; i < kernel_size * 2; i++ ) - printf( "%f\n", kernels [i] ); - printf( "chroma:\n" ); - for ( i = 0; i < kernel_size; i++ ) - printf( "%f\n", kernels [i] ); - */ - - /* generate linear rescale kernels */ - #if rescale_out > 1 - { - float weight = 1.0f; - float* out = impl->kernel; - int n = rescale_out; - do - { - float remain = 0; - int i; - weight -= 1.0f / rescale_in; - for ( i = 0; i < kernel_size * 2; i++ ) - { - float cur = kernels [i]; - float m = cur * weight; - *out++ = m + remain; - remain = cur - m; - } - } - while ( --n ); - } - #endif -} - -static float const default_decoder [6] = - { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; - -static void init( init_t* impl, nes_ntsc_setup_t const* setup ) -{ - impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; - impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; - #ifdef default_palette_contrast - if ( !setup->palette ) - impl->contrast *= default_palette_contrast; - #endif - - impl->artifacts = (float) setup->artifacts; - if ( impl->artifacts > 0 ) - impl->artifacts *= artifacts_max - artifacts_mid; - impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; - - impl->fringing = (float) setup->fringing; - if ( impl->fringing > 0 ) - impl->fringing *= fringing_max - fringing_mid; - impl->fringing = impl->fringing * fringing_mid + fringing_mid; - - init_filters( impl, setup ); - - /* generate gamma table */ - if ( gamma_size > 1 ) - { - float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); - float const gamma = 1.1333f - (float) setup->gamma * 0.5f; - /* match common PC's 2.2 gamma to TV's 2.65 gamma */ - int i; - for ( i = 0; i < gamma_size; i++ ) - impl->to_float [i] = - (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; - } - - /* setup decoder matricies */ - { - float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; - float sat = (float) setup->saturation + 1; - float const* decoder = setup->decoder_matrix; - if ( !decoder ) - { - decoder = default_decoder; - if ( STD_HUE_CONDITION( setup ) ) - hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); - } - - { - float s = (float) sin( hue ) * sat; - float c = (float) cos( hue ) * sat; - float* out = impl->to_rgb; - int n; - - n = burst_count; - do - { - float const* in = decoder; - int n = 3; - do - { - float i = *in++; - float q = *in++; - *out++ = i * c - q * s; - *out++ = i * s + q * c; - } - while ( --n ); - if ( burst_count <= 1 ) - break; - ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ - } - while ( --n ); - } - } -} - -/* kernel generation */ - -#define RGB_TO_YIQ( r, g, b, y, i ) (\ - (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ - (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ - ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ -) - -#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ - r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ - g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ - (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ -) - -#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) - -enum { rgb_kernel_size = burst_size / alignment_count }; -enum { rgb_bias = rgb_unit * 2 * nes_ntsc_rgb_builder }; - -typedef struct pixel_info_t -{ - int offset; - float negate; - float kernel [4]; -} pixel_info_t; - -#if rescale_in > 1 - #define PIXEL_OFFSET_( ntsc, scaled ) \ - (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ - (kernel_size * 2 * scaled)) - - #define PIXEL_OFFSET( ntsc, scaled ) \ - PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ - (((scaled) + rescale_out * 10) % rescale_out) ),\ - (1.0f - (((ntsc) + 100) & 2)) -#else - #define PIXEL_OFFSET( ntsc, scaled ) \ - (kernel_size / 2 + (ntsc) - (scaled)),\ - (1.0f - (((ntsc) + 100) & 2)) -#endif - -extern pixel_info_t const nes_ntsc_pixels [alignment_count]; - -/* Generate pixel at all burst phases and column alignments */ -static void gen_kernel( init_t* impl, float y, float i, float q, nes_ntsc_rgb_t* out ) -{ - /* generate for each scanline burst phase */ - float const* to_rgb = impl->to_rgb; - int burst_remain = burst_count; - y -= rgb_offset; - do - { - /* Encode yiq into *two* composite signals (to allow control over artifacting). - Convolve these with kernels which: filter respective components, apply - sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack - into integer. Based on algorithm by NewRisingSun. */ - pixel_info_t const* pixel = nes_ntsc_pixels; - int alignment_remain = alignment_count; - do - { - /* negate is -1 when composite starts at odd multiple of 2 */ - float const yy = y * impl->fringing * pixel->negate; - float const ic0 = (i + yy) * pixel->kernel [0]; - float const qc1 = (q + yy) * pixel->kernel [1]; - float const ic2 = (i - yy) * pixel->kernel [2]; - float const qc3 = (q - yy) * pixel->kernel [3]; - - float const factor = impl->artifacts * pixel->negate; - float const ii = i * factor; - float const yc0 = (y + ii) * pixel->kernel [0]; - float const yc2 = (y - ii) * pixel->kernel [2]; - - float const qq = q * factor; - float const yc1 = (y + qq) * pixel->kernel [1]; - float const yc3 = (y - qq) * pixel->kernel [3]; - - float const* k = &impl->kernel [pixel->offset]; - int n; - ++pixel; - for ( n = rgb_kernel_size; n; --n ) - { - float i = k[0]*ic0 + k[2]*ic2; - float q = k[1]*qc1 + k[3]*qc3; - float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + - k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; - if ( rescale_out <= 1 ) - k--; - else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) - k += kernel_size * 2 - 1; - else - k -= kernel_size * 2 * (rescale_out - 1) + 2; - { - int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); - *out++ = PACK_RGB( r, g, b ) - rgb_bias; - } - } - } - while ( alignment_count > 1 && --alignment_remain ); - - if ( burst_count <= 1 ) - break; - - to_rgb += 6; - - ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ - } - while ( --burst_remain ); -} - -static void correct_errors( nes_ntsc_rgb_t color, nes_ntsc_rgb_t* out ); - -#if DISABLE_CORRECTION - #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } - #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } -#else - #define CORRECT_ERROR( a ) { out [a] += error; } - #define DISTRIBUTE_ERROR( a, b, c ) {\ - nes_ntsc_rgb_t fourth = (error + 2 * nes_ntsc_rgb_builder) >> 2;\ - fourth &= (rgb_bias >> 1) - nes_ntsc_rgb_builder;\ - fourth -= rgb_bias >> 2;\ - out [a] += fourth;\ - out [b] += fourth;\ - out [c] += fourth;\ - out [i] += error - (fourth * 3);\ - } -#endif - -#define RGB_PALETTE_OUT( rgb, out_ )\ -{\ - unsigned char* out = (out_);\ - nes_ntsc_rgb_t clamped = (rgb);\ - NES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ - out [0] = (unsigned char) (clamped >> 21);\ - out [1] = (unsigned char) (clamped >> 11);\ - out [2] = (unsigned char) (clamped >> 1);\ -} - -/* blitter related */ - -#ifndef restrict - #if defined (__GNUC__) - #define restrict __restrict__ - #elif defined (_MSC_VER) && _MSC_VER > 1300 - #define restrict __restrict - #else - /* no support for restricted pointers */ - #define restrict - #endif -#endif - -#include - -#if NES_NTSC_OUT_DEPTH <= 16 - #if USHRT_MAX == 0xFFFF - typedef unsigned short nes_ntsc_out_t; - #else - #error "Need 16-bit int type" - #endif - -#else - #if UINT_MAX == 0xFFFFFFFF - typedef unsigned int nes_ntsc_out_t; - #elif ULONG_MAX == 0xFFFFFFFF - typedef unsigned long nes_ntsc_out_t; - #else - #error "Need 32-bit int type" - #endif - -#endif diff --git a/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c b/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c index f0827bc9..a1f4c79d 100644 --- a/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c +++ b/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c @@ -18,9 +18,11 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include "ntsc.h" + +#if 0 #include #include -#include "ntsc.h" #include "nes_ntsc/nes_ntsc.h" #include "misc/memutil.h" #include "system/video.h" @@ -91,6 +93,7 @@ void ntsc2x(void *d,u32 dst_slice,void *s,u32 src_slice,u32 width,u32 height) u32 *dst = (u32*)d; u8 *pal = nes->ppu.palette; + assert(0); for(h=0;h<240;h++) { u32 *dststart = dst; @@ -133,6 +136,12 @@ void ntsc2x(void *d,u32 dst_slice,void *s,u32 src_slice,u32 width,u32 height) dst = (u32*)((u8*)d + dst_slice * (h * 2)); } } +#endif + +int ntsc_init() { return 0; } +void ntsc_kill() { } +void ntsc_palette_changed() { } +void ntsc2x(void *d,u32 dst_slice,void *s,u32 src_slice,u32 width,u32 height) { } void ntsc3x(void *dst,u32 dst_slice,void *src,u32 src_slice,u32 width,u32 height) { diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h b/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h index 7a50bfc1..64069985 100644 --- a/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h +++ b/apps/nesemu2/src/system/common/filters/scale2x/scale2x.h @@ -21,9 +21,11 @@ #ifndef __SCALE2X_H #define __SCALE2X_H -typedef unsigned char scale2x_uint8; -typedef unsigned short scale2x_uint16; -typedef unsigned scale2x_uint32; +#include "types.h" + +typedef u8 scale2x_uint8; +typedef u16 scale2x_uint16; +typedef u32 scale2x_uint32; void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h b/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h index 14655d00..9ab872e3 100644 --- a/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h +++ b/apps/nesemu2/src/system/common/filters/scale2x/scale3x.h @@ -21,9 +21,11 @@ #ifndef __SCALE3X_H #define __SCALE3X_H -typedef unsigned char scale3x_uint8; -typedef unsigned short scale3x_uint16; -typedef unsigned scale3x_uint32; +#include "types.h" + +typedef u8 scale3x_uint8; +typedef u16 scale3x_uint16; +typedef u32 scale3x_uint32; void scale3x_8_def(scale3x_uint8* dst0, scale3x_uint8* dst1, scale3x_uint8* dst2, const scale3x_uint8* src0, const scale3x_uint8* src1, const scale3x_uint8* src2, unsigned count); void scale3x_16_def(scale3x_uint16* dst0, scale3x_uint16* dst1, scale3x_uint16* dst2, const scale3x_uint16* src0, const scale3x_uint16* src1, const scale3x_uint16* src2, unsigned count); diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c index 422738d6..a39050d3 100644 --- a/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c +++ b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.c @@ -33,6 +33,7 @@ * - derivative works of the program are allowed. */ +#if 0 #if HAVE_CONFIG_H #include #endif @@ -514,3 +515,12 @@ void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_ } } +#endif + +#include "types.h" + +void scale(unsigned scale, void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height) { } + +void scale2x(void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height) { } +void scale3x(void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height) { } +void scale4x(void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height) { } diff --git a/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h index 0c1479a5..09c622fb 100644 --- a/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h +++ b/apps/nesemu2/src/system/common/filters/scale2x/scalebit.h @@ -41,10 +41,11 @@ extern "C" { #endif int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height); -void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height); -void scale2x(void* void_dst, unsigned dst_slice, void* void_src, unsigned src_slice, unsigned width, unsigned height); -void scale3x(void* void_dst, unsigned dst_slice, void* void_src, unsigned src_slice, unsigned width, unsigned height); -void scale4x(void* void_dst, unsigned dst_slice, void* void_src, unsigned src_slice, unsigned width, unsigned height); +void scale(unsigned scale, void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height); + +void scale2x(void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height); +void scale3x(void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height); +void scale4x(void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height); #ifdef __cplusplus } diff --git a/apps/nesemu2/src/system/linux/stricmp.c b/apps/nesemu2/src/system/linux/stricmp.c index a71d58b8..7ce073ff 100644 --- a/apps/nesemu2/src/system/linux/stricmp.c +++ b/apps/nesemu2/src/system/linux/stricmp.c @@ -1,4 +1,6 @@ -#include +static inline char tolower(char c) { + return (c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c); +} int stricmp(char *s1,char *s2) { diff --git a/apps/nesemu2/src/system/sdl/main.c b/apps/nesemu2/src/system/sdl/main.c index a782362a..0a6230c1 100644 --- a/apps/nesemu2/src/system/sdl/main.c +++ b/apps/nesemu2/src/system/sdl/main.c @@ -18,7 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -//#include #include #include "version.h" #include "emu/emu.h" @@ -34,7 +33,7 @@ #include "system/video.h" #include "system/input.h" #include "palette/palette.h" -#include "palette/generator.h" +//#include "palette/generator.h" #include "system/sdl/console/console.h" #include @@ -44,87 +43,30 @@ char configfilename[1024] = CONFIG_FILENAME; char exepath[1024] = ""; -#if 0 -static void usage(char *argv0) -{ - printf("\nnesemu2 v%s - Copyright 2013 James Holodnak\n\n",VERSION); - printf("Usage: %s [options] filename\n\n",argv0); - printf("Supported ROM formats: iNES, NES 2.0, UNIF, FDS, NSF\n\n"); - printf("Options:\n\n"); - printf(" --help : Show this message and exit.\n"); - printf(" --mappers : Show supported mappers and exit.\n"); - printf(" --config : Use 'file' as configuration file.\n"); - printf(" --patch : Specify patch file for ROM.\n"); - printf(" --movie : Specify movie file.\n"); - printf(" --record : After loading rom, start recording movie (used with --movie).\n"); - printf(" --recordtest : After loading rom, start recording test (used with --movie).\n"); - printf(" --test : Specify automated testing script.\n"); - printf("\n"); -} -#endif - //todo: this is getting ugly -int main(int argc,char *argv[]) +int main(const char *romfilename) { //int i; int ret; - int recordmovie = 0; + //int recordmovie = 0; //char *p; - char romfilename[1024] = ""; - char patchfilename[1024] = ""; - char moviefilename[1024] = ""; - char testfilename[1024] = ""; + //char romfilename[1024] = ""; + //char patchfilename[1024] = ""; + //char moviefilename[1024] = ""; + //char testfilename[1024] = ""; _ioe_init(); //clear the tmp strings and configfile string - memset(romfilename,0,1024); - memset(patchfilename,0,1024); - memset(moviefilename,0,1024); - memset(configfilename,0,1024); - memset(testfilename,0,1024); + //memset(romfilename,0,1024); + //memset(patchfilename,0,1024); + //memset(moviefilename,0,1024); + //memset(configfilename,0,1024); + //memset(testfilename,0,1024); -#if 0 - //make the exe path variable - strcpy(exepath,argv[0]); - if((p = strrchr(exepath,PATH_SEPERATOR)) != 0) { - *p = 0; - } - - //process the command line - for(i=1;icart == 0) - log_printf("main: cannot record movie without rom loaded.\n"); - else { - emu_event(E_SAVEMOVIE,(void*)moviefilename); - emu_event(E_RECORDMOVIE,0); - nes->movie.mode |= (recordmovie > 1) ? MOVIE_TEST : 0; - } - } - else { - emu_event(E_LOADMOVIE,(void*)moviefilename); - emu_event(E_PLAYMOVIE,0); - } - } + //if(strcmp(moviefilename,"") != 0) { + // if(recordmovie) { + // if(nes->cart == 0) + // log_printf("main: cannot record movie without rom loaded.\n"); + // else { + // emu_event(E_SAVEMOVIE,(void*)moviefilename); + // emu_event(E_RECORDMOVIE,0); + // nes->movie.mode |= (recordmovie > 1) ? MOVIE_TEST : 0; + // } + // } + // else { + // emu_event(E_LOADMOVIE,(void*)moviefilename); + // emu_event(E_PLAYMOVIE,0); + // } + //} //begin automated tests - if(strcmp(testfilename,"") != 0) - ret = emu_mainloop_test(testfilename); + //if(strcmp(testfilename,"") != 0) + // ret = emu_mainloop_test(testfilename); //or begin the main loop - else - ret = emu_mainloop(); + //else + ret = emu_mainloop(); //destroy emulator emu_kill(); diff --git a/apps/nesemu2/src/system/sdl/system.c b/apps/nesemu2/src/system/sdl/system.c index 8f7dcf95..eaf43619 100644 --- a/apps/nesemu2/src/system/sdl/system.c +++ b/apps/nesemu2/src/system/sdl/system.c @@ -18,29 +18,11 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include -#include - -uint32_t uptime(); - -#ifndef _MAX_PATH - #define _MAX_PATH 4096 -#endif +#include int system_init() { return 0; } void system_kill() { } - void system_checkevents() { } - -char *system_getcwd() -{ - static char buf[_MAX_PATH]; - - if(getcwd(buf,_MAX_PATH) == NULL) - memset(buf,0,_MAX_PATH); - return(buf); -} - +char *system_getcwd() { return ""; } uint64_t system_gettick() { return uptime(); } uint64_t system_getfrequency() { return 1000; } diff --git a/apps/nesemu2/src/system/sdl/video.c b/apps/nesemu2/src/system/sdl/video.c index bcf3da8a..a2e4fb47 100644 --- a/apps/nesemu2/src/system/sdl/video.c +++ b/apps/nesemu2/src/system/sdl/video.c @@ -47,7 +47,7 @@ static u32 palettecache32[256]; static u8 palettecache[32]; //for frame limiting -static double interval = 0; +static int interval = 0; static u64 lasttime = 0; //pointer to scree and copy of the nes screen @@ -94,7 +94,7 @@ int video_init() nesscreen = (u8*)mem_alloc(256 * (240 + 16)); //setup timer to limit frames - interval = (double)system_getfrequency() / 60.0f; + interval = system_getfrequency() / 60; lasttime = system_gettick(); //clear palette caches @@ -158,13 +158,14 @@ void video_endframe() //draw everything draw_rect(screen, 0, 0, 256, 240); + draw_sync(); console_draw(screen, 256*4,screenh); //simple frame limiter if(config_get_bool("video.framelimit")) { do { t = system_gettick(); - } while((double)(t - lasttime) < interval); + } while(t - lasttime < interval); lasttime = t; } } @@ -220,20 +221,20 @@ void video_updatepalette(u8 addr,u8 data) void video_setpalette(palette_t *p) { int i,j; - palentry_t *e; + palentry_t e; - for(j=0;j<8;j++) { - for(i=0;i<64;i++) { - palette[j][(i * 3) + 0] = p->pal[j][i].r; - palette[j][(i * 3) + 1] = p->pal[j][i].g; - palette[j][(i * 3) + 2] = p->pal[j][i].b; - } - } + //for(j=0;j<8;j++) { + // for(i=0;i<64;i++) { + // palette[j][(i * 3) + 0] = p->pal[j][i].r; + // palette[j][(i * 3) + 1] = p->pal[j][i].g; + // palette[j][(i * 3) + 2] = p->pal[j][i].b; + // } + //} for(j=0;j<8;j++) { for(i=0;i<256;i++) { - e = &p->pal[j][i & 0x3F]; - palette32[j][i] = (e->r << rshift) | (e->g << gshift) | (e->b << bshift); + e.val = p->pal[j][i & 0x3F]; + palette32[j][i] = (e.r << rshift) | (e.g << gshift) | (e.b << bshift); } } @@ -254,6 +255,7 @@ int video_zapperhit(int x,int y) color = palettecache[nesscreen[x + y * 256]]; e = &palette[(color >> 5) & 7][(color & 0x3F) * 3]; + assert(0); ret += (int)(e[0] * 0.299f); ret += (int)(e[1] * 0.587f); ret += (int)(e[2] * 0.114f); diff --git a/apps/nesemu2/src/types.h b/apps/nesemu2/src/types.h index 2295e7da..12ac4d4b 100644 --- a/apps/nesemu2/src/types.h +++ b/apps/nesemu2/src/types.h @@ -21,29 +21,29 @@ #ifndef __types_h__ #define __types_h__ -#if defined(_MSC_VER) - #define int64 __int64 - #define INLINE __inline -#elif defined(__GNUC__) - #define int64 long long - #define INLINE inline -#else - #error unknown compiler. please #define int64. -#endif +#include +#define int64 long long +#define INLINE inline -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed int64 s64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned int64 u64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; typedef u8 (*readfunc_t)(u32); typedef void (*writefunc_t)(u32,u8); int stricmp(char *s1,char *s2); +// these are symbols conflicted with glibc +#define sync my_sync +#define read my_read +#define write my_write +#define select my_select + #endif diff --git a/libs/klib/include/klib.h b/libs/klib/include/klib.h index 2be246a1..21f858c5 100644 --- a/libs/klib/include/klib.h +++ b/libs/klib/include/klib.h @@ -24,6 +24,8 @@ #define vsprintf my_vsprintf #define sprintf my_sprintf #define snprintf my_snprintf +#define malloc my_malloc +#define free my_free #endif #ifdef __cplusplus @@ -53,7 +55,7 @@ int strcmp(const char* s1, const char* s2); int strncmp(const char* s1, const char* s2, size_t n); char* strtok(char* s,const char* delim); char *strstr(const char *, const char *); -const char *strchr(const char *s, int c); +char *strchr(const char *s, int c); // stdlib.h int atoi(const char* nptr); @@ -61,6 +63,8 @@ int abs(int x); unsigned long time(); void srand(unsigned int seed); int rand(); +void *malloc(size_t size); +void free(void *ptr); // stdio.h int printf(const char* fmt, ...); diff --git a/libs/klib/src/stdlib.c b/libs/klib/src/stdlib.c index d9142876..b64a0c72 100644 --- a/libs/klib/src/stdlib.c +++ b/libs/klib/src/stdlib.c @@ -1,3 +1,6 @@ +#include "klib.h" + +#if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) static unsigned long int next = 1; int rand(void) { @@ -9,3 +12,38 @@ int rand(void) { void srand(unsigned int seed) { next = seed; } + +int abs(int x) { + return (x < 0 ? -x : x); +} + +int atoi(const char* nptr) { + int x = 0; + while (*nptr != '\0') { + x = x * 10 + *nptr - '0'; + nptr ++; + } + return x; +} + +void *malloc(size_t size) { + static void *head = NULL; + if (head == NULL) { + head = _heap.start; + printf("heap start = %x\n", head); + } + + // aligning + size = (size + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); + + if (head + size >= _heap.end) return NULL; + printf("malloc: size = %d\n", size); + void *ret = head; + head += size; + return ret; +} + +void free(void *ptr) { +} + +#endif diff --git a/libs/klib/src/string.c b/libs/klib/src/string.c index 69d97db6..a5dad6b4 100644 --- a/libs/klib/src/string.c +++ b/libs/klib/src/string.c @@ -170,4 +170,14 @@ int memcmp(const void* s1, const void* s2, size_t n){ return (int)(*t1-*t2); } +char *strchr(const char *s, int c) { + assert(0); + return NULL; +} + +char *strrchr(const char *s, int c) { + assert(0); + return NULL; +} + #endif From 318a36a8ec28c9e9ff1fb8722a1618517c063d25 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Fri, 29 Nov 2019 18:49:54 +0800 Subject: [PATCH 007/234] apps,nesemu2: clean up --- apps/nesemu2/src/cartdb/cartdb.c | 508 ------ apps/nesemu2/src/cartdb/cartdb.h | 31 - apps/nesemu2/src/{types.h => common.h} | 10 +- apps/nesemu2/src/emu/commands.c | 113 -- apps/nesemu2/src/emu/commands.h | 62 - apps/nesemu2/src/emu/commands/general.c | 101 -- apps/nesemu2/src/emu/commands/nes.c | 184 --- apps/nesemu2/src/emu/emu.c | 89 - apps/nesemu2/src/emu/emu.h | 3 +- apps/nesemu2/src/emu/events.c | 193 --- apps/nesemu2/src/emu/events.h | 65 - apps/nesemu2/src/inputdev/inputdev.c | 4 +- apps/nesemu2/src/inputdev/inputdev.h | 2 - apps/nesemu2/src/inputdev/joypad0.c | 6 - apps/nesemu2/src/inputdev/joypad1.c | 6 - apps/nesemu2/src/inputdev/powerpad.c | 60 - apps/nesemu2/src/inputdev/zapper.c | 114 -- apps/nesemu2/src/mappers/chips/mmc1.h | 2 +- apps/nesemu2/src/mappers/chips/mmc2.h | 2 +- apps/nesemu2/src/mappers/chips/mmc3.h | 2 +- apps/nesemu2/src/mappers/chips/mmc4.h | 2 +- apps/nesemu2/src/mappers/chips/mmc5.h | 2 +- apps/nesemu2/src/mappers/chips/namcot-108.h | 2 +- apps/nesemu2/src/mappers/chips/namcot-163.h | 2 +- .../src/mappers/chips/taito-tc0190fmc.h | 2 +- apps/nesemu2/src/mappers/fds/calls.h | 1 - apps/nesemu2/src/mappers/fds/fds.c | 1 - apps/nesemu2/src/mappers/mapperinc.h | 1 - apps/nesemu2/src/mappers/mappers.h | 2 +- apps/nesemu2/src/mappers/nsf/nsf.c | 1 - apps/nesemu2/src/mappers/sound/s_DRIP.c | 2 +- apps/nesemu2/src/mappers/sound/s_FDS.c | 321 ---- apps/nesemu2/src/mappers/sound/s_FME7.c | 2 +- apps/nesemu2/src/mappers/sound/s_N106.c | 2 +- apps/nesemu2/src/mappers/sound/s_VRC6.c | 5 +- apps/nesemu2/src/mappers/sound/s_VRC7.c | 1435 ----------------- apps/nesemu2/src/misc/config.c | 166 +- apps/nesemu2/src/misc/crc32.h | 2 +- apps/nesemu2/src/misc/history.c | 90 -- apps/nesemu2/src/misc/history.h | 42 - apps/nesemu2/src/misc/log.c | 142 -- apps/nesemu2/src/misc/log.h | 30 - apps/nesemu2/src/misc/memfile.c | 1 - apps/nesemu2/src/misc/memfile.h | 2 +- apps/nesemu2/src/misc/memutil.c | 27 +- apps/nesemu2/src/misc/paths.c | 76 - apps/nesemu2/src/misc/paths.h | 33 - apps/nesemu2/src/misc/vars.c | 74 - apps/nesemu2/src/nes/apu/apu.c | 1 - apps/nesemu2/src/nes/cart/cart.c | 17 +- apps/nesemu2/src/nes/cart/cart.h | 2 +- apps/nesemu2/src/nes/cart/doctor.c | 35 - apps/nesemu2/src/nes/cart/fds.c | 2 - apps/nesemu2/src/nes/cart/ines.c | 1 - apps/nesemu2/src/nes/cart/ines20.c | 1 - apps/nesemu2/src/nes/cart/nsf.c | 58 - apps/nesemu2/src/nes/cart/patch/ips.c | 1 - apps/nesemu2/src/nes/cart/patch/patch.c | 1 - apps/nesemu2/src/nes/cart/patch/ups.c | 1 - apps/nesemu2/src/nes/cart/unif.c | 1 - apps/nesemu2/src/nes/cpu/cpu.c | 1 - apps/nesemu2/src/nes/cpu/cpu.h | 2 +- apps/nesemu2/src/nes/genie.c | 65 - apps/nesemu2/src/nes/io.c | 1 - apps/nesemu2/src/nes/io.h | 2 +- apps/nesemu2/src/nes/memory.c | 1 - apps/nesemu2/src/nes/memory.h | 2 +- apps/nesemu2/src/nes/movie.c | 292 ---- apps/nesemu2/src/nes/movie.h | 59 - apps/nesemu2/src/nes/nes.c | 13 - apps/nesemu2/src/nes/nes.h | 6 +- apps/nesemu2/src/nes/ppu/io.c | 1 - apps/nesemu2/src/nes/ppu/ppu.h | 2 +- apps/nesemu2/src/nes/ppu/step.c | 1 - apps/nesemu2/src/nes/ppu/tilecache.h | 2 +- apps/nesemu2/src/nes/region.c | 1 - apps/nesemu2/src/nes/state/block.c | 1 - apps/nesemu2/src/nes/state/state.c | 1 - apps/nesemu2/src/palette/palette.c | 122 +- apps/nesemu2/src/palette/palette.h | 2 +- apps/nesemu2/src/system/common/filters.h | 2 +- .../src/system/common/filters/draw/draw.h | 2 +- .../common/filters/interpolate/interpolate.h | 2 +- .../src/system/common/filters/ntsc/ntsc.c | 118 -- .../src/system/common/filters/ntsc/ntsc.h | 2 +- .../system/common/filters/scale2x/scale2x.h | 2 +- .../system/common/filters/scale2x/scale3x.h | 2 +- .../system/common/filters/scale2x/scalebit.c | 486 +----- apps/nesemu2/src/system/input.h | 2 +- apps/nesemu2/src/system/sdl/console/console.c | 189 --- apps/nesemu2/src/system/sdl/console/console.h | 32 - apps/nesemu2/src/system/sdl/console/font.c | 60 - apps/nesemu2/src/system/sdl/console/font.h | 32 - .../nesemu2/src/system/sdl/console/fontdata.c | 142 -- .../nesemu2/src/system/sdl/console/fontdata.h | 29 - .../src/system/sdl/console/linebuffer.c | 139 -- .../src/system/sdl/console/linebuffer.h | 32 - apps/nesemu2/src/system/sdl/main.c | 93 +- apps/nesemu2/src/system/sdl/video.c | 124 +- apps/nesemu2/src/system/system.h | 5 +- apps/nesemu2/src/system/video.h | 2 - 101 files changed, 70 insertions(+), 6150 deletions(-) delete mode 100644 apps/nesemu2/src/cartdb/cartdb.c delete mode 100644 apps/nesemu2/src/cartdb/cartdb.h rename apps/nesemu2/src/{types.h => common.h} (94%) delete mode 100644 apps/nesemu2/src/emu/commands.c delete mode 100644 apps/nesemu2/src/emu/commands.h delete mode 100644 apps/nesemu2/src/emu/commands/general.c delete mode 100644 apps/nesemu2/src/emu/commands/nes.c delete mode 100644 apps/nesemu2/src/emu/events.c delete mode 100644 apps/nesemu2/src/emu/events.h delete mode 100644 apps/nesemu2/src/inputdev/powerpad.c delete mode 100644 apps/nesemu2/src/inputdev/zapper.c delete mode 100644 apps/nesemu2/src/misc/history.c delete mode 100644 apps/nesemu2/src/misc/history.h delete mode 100644 apps/nesemu2/src/misc/log.c delete mode 100644 apps/nesemu2/src/misc/log.h delete mode 100644 apps/nesemu2/src/misc/paths.c delete mode 100644 apps/nesemu2/src/misc/paths.h delete mode 100644 apps/nesemu2/src/nes/movie.c delete mode 100644 apps/nesemu2/src/nes/movie.h delete mode 100644 apps/nesemu2/src/system/sdl/console/console.c delete mode 100644 apps/nesemu2/src/system/sdl/console/console.h delete mode 100644 apps/nesemu2/src/system/sdl/console/font.c delete mode 100644 apps/nesemu2/src/system/sdl/console/font.h delete mode 100644 apps/nesemu2/src/system/sdl/console/fontdata.c delete mode 100644 apps/nesemu2/src/system/sdl/console/fontdata.h delete mode 100644 apps/nesemu2/src/system/sdl/console/linebuffer.c delete mode 100644 apps/nesemu2/src/system/sdl/console/linebuffer.h diff --git a/apps/nesemu2/src/cartdb/cartdb.c b/apps/nesemu2/src/cartdb/cartdb.c deleted file mode 100644 index 288e96a2..00000000 --- a/apps/nesemu2/src/cartdb/cartdb.c +++ /dev/null @@ -1,508 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "cartdb/cartdb.h" - -#if 0 -#include -#include -#include "cartdb/parser.h" -#include "misc/memutil.h" -#include "misc/strutil.h" -#include "misc/config.h" -#include "misc/log.h" -#include "misc/crc32.h" -#include "mappers/mappers.h" -#include "mappers/mapperid.h" - -static xml_t *cartxml = 0; -int cartdb_init() -{ - char filename[1024]; - char *str,*str2,*p; - xml_t *xml; - - config_get_eval_string(filename,"cartdb.filename"); - if(cartxml) { - return(0); - } - - str = strtok(filename,";"); - while(str != 0) { - str2 = mem_strdup(str); - p = str_eatwhitespace(str2); - if((xml = parser_load(p)) == 0) { - log_printf("cartdb_init: error loading xml cart database '%s'\n",p); - } - else { - log_printf("cartdb_init: loaded xml cart database '%s'\n",p); - if(cartxml == 0) - cartxml = xml; - else - parser_merge(cartxml,&xml); - } - mem_free(str2); - str = strtok(0,";"); - } - - if(cartxml == 0) { - log_printf("cartdb_init: no xml databases loaded, continuing without using cartdb\n"); - return(0); - } - - return(0); -} - -void cartdb_kill() -{ - if(cartxml) { - parser_free(cartxml); - parser_verifymemory(); - } - cartxml = 0; -} - -static char *find_attrib(node_t *node,char *name) -{ - attribute_t *attrib; - - if(node == 0) - return(0); - attrib = node->attributes; - while(attrib) { - if(strcmp(attrib->name,name) == 0) { - return(attrib->data); - } - attrib = attrib->next; - } - return(0); -} - -static u32 hexstr2int(char *str) -{ - u32 ret = 0; - size_t len = strlen(str); - u32 i; - char ch; - - for(i=0;i= 'A' && ch <= 'F') - ch = 10 + ch - 'A'; - else if(ch >= '0' && ch <= '9') - ch = ch - '0'; - else { - log_printf("hexstr2int: invalid character '%c'\n",ch); - return((u32)-1); - } - ret |= ch; - } - return(ret); -} - -static node_t *find_cart(u32 crc32) -{ - node_t *gamenode,*cartnode; - char *str; - u32 n; - - for(gamenode = cartxml->root->child; gamenode; gamenode = gamenode->next) { - if(strcmp(gamenode->name,"game") != 0) - continue; - for(cartnode = gamenode->child; cartnode; cartnode = cartnode->next) { - if(strcmp(cartnode->name,"cartridge") != 0) - continue; - if((str = find_attrib(cartnode,"crc")) != 0) { - n = hexstr2int(str); - if(n == crc32) - return(cartnode); - } - } - } - return(0); -} - -static node_t *get_child(char *name,node_t *parentnode) -{ - node_t *node; - - for(node = parentnode->child; node; node = node->next) { - if(strcmp(node->name,name) == 0) - break; - } - return(node); -} - -static node_t *get_sibling(char *name,node_t *brothernode) -{ - node_t *node; - - for(node = brothernode->next; node; node = node->next) { - if(strcmp(node->name,name) == 0) - break; - } - return(node); -} - -enum pins_e { - P_ERROR = 0, - P_NC, - - P_PRG_A0, P_PRG_A1, P_PRG_A2, P_PRG_A3, P_PRG_A4, P_PRG_A5, P_PRG_A6, P_PRG_A7, - P_PRG_A8, P_PRG_A9, P_PRG_A10, P_PRG_A11, P_PRG_A12, P_PRG_A13, P_PRG_A14, P_PRG_A15, - P_PRG_A16, P_PRG_A17, P_PRG_A18, P_PRG_A19, P_PRG_A20, - - P_CHR_A0, P_CHR_A1, P_CHR_A2, P_CHR_A3, P_CHR_A4, P_CHR_A5, P_CHR_A6, P_CHR_A7, - P_CHR_A8, P_CHR_A9, P_CHR_A10, P_CHR_A11, P_CHR_A12, P_CHR_A13, P_CHR_A14, P_CHR_A15, - P_CHR_A16, P_CHR_A17, P_CHR_A18, P_CHR_A19, P_CHR_A20, - -}; - -static u8 get_pin_func(char *str) -{ - char *p,*tmp = strdup(str); - u8 ret = P_ERROR; - - //make lowercase - for(p = tmp;*p;p++) - *p = tolower(*p); - p = str_eatwhitespace(tmp); - - //not connected - if(strncmp(p,"nc",2) == 0) - ret = P_NC; - - //prg pin - else if(strncmp(p,"prg",3) == 0) { - p += 3; - p = str_eatwhitespace(p); - if(*p == 'a') { - p++; - ret = P_PRG_A0 + atoi(p); - } - } - - //chr pin - else if(strncmp(p,"chr",3) == 0) { - p += 3; - p = str_eatwhitespace(p); - if(*p == 'a') { - p++; - ret = P_CHR_A0 + atoi(p); - } - } - - //unknown function - if(ret == P_ERROR) { - log_printf("get_pin_func: unknown pin function '%s'\n",str); - } - - free(tmp); - return(ret); -} - -//get pin map from the xml nodes -static int get_pin_map(node_t *node,u8 *pins,int maxpins) -{ - node_t *pin; - char *num,*func; - u8 pinnum,pinfunc; - - //clear pins array - memset(pins,P_ERROR,maxpins); - - //get first pin node - pin = get_child("pin",node); - while(pin) { - - //get pin number and function attributes - num = find_attrib(pin,"number"); - func = find_attrib(pin,"function"); - - //get and verify the pin number - pinnum = (u8)atoi(num); - if(pinnum >= maxpins) { - log_printf("get_pin_map: pinnum exceeds maxpins\n"); - continue; - } - - //get and verify the pin function - pinfunc = get_pin_func(func); - if(pinfunc != P_ERROR) - pins[pinnum] = pinfunc; - - //get node sibling for continued processing - pin = get_sibling("pin",pin); - } - return(0); -} - -//process chip nodes -static int process_chip(int mapperid,char *board,int mapper,node_t *node) -{ - char *chip; - u8 pins[64]; - - if((chip = find_attrib(node,"type")) == 0) - return(mapperid); - - log_printf("processing child chip node: %s (mapperid = %d)\n", chip,mapperid); - - //SxROM boards, process mmc1 type - if(mapperid == B_NINTENDO_SxROM) { - if(strncmp(chip,"MMC1A",5) == 0) mapperid = B_NINTENDO_SxROM_MMC1A; - else if(strncmp(chip,"MMC1B",5) == 0) mapperid = B_NINTENDO_SxROM_MMC1B; - else if(strncmp(chip,"MMC1C",5) == 0) mapperid = B_NINTENDO_SxROM_MMC1C; - log_printf("process_chip: mmc1 chip type: %s\n",chip); - } - - //vrc2 boards, find wiring configuration - if (mapperid == B_KONAMI_VRC2) { - if (strncmp(chip, "VRC2", 4) == 0 && get_pin_map(node, pins, 64) == 0) { - if (pins[3] == P_PRG_A0 && pins[4] == P_PRG_A1) { - if (pins[21] == P_NC) { - mapperid = B_KONAMI_VRC2A; - log_printf("process_chip: vrc2a detected.\n"); - } - else { - mapperid = B_KONAMI_VRC4B; - log_printf("process_chip: vrc2c detected, using vrc4b.\n"); - } - } - else if (pins[3] == P_PRG_A1 && pins[4] == P_PRG_A0) { - mapperid = B_KONAMI_VRC2B; - log_printf("process_chip: vrc2b detected.\n"); - } - else - log_printf("process_chip: unknown vrc2 pin configuration (pin3 = %d, pin4 = %d)\n", pins[3], pins[4]); - - } - } - - //vrc4 boards - if (mapperid == B_KONAMI_VRC4) { - if (strncmp(chip, "VRC4", 4) == 0 && get_pin_map(node, pins, 64) == 0) { - if (pins[3] == P_PRG_A7 && pins[4] == P_PRG_A6) { - mapperid = B_KONAMI_VRC4C; - log_printf("process_chip: vrc4c detected.\n"); - } - else if (pins[3] == P_PRG_A0 && pins[4] == P_PRG_A1) { - mapperid = B_KONAMI_VRC4B; - log_printf("process_chip: vrc4b detected.\n"); - } - else if (pins[3] == P_PRG_A2 && pins[4] == P_PRG_A1) { - mapperid = B_KONAMI_VRC4A; - log_printf("process_chip: vrc4a detected.\n"); - } - else if (pins[3] == P_PRG_A2 && pins[4] == P_PRG_A3) { - mapperid = B_KONAMI_VRC4B; - log_printf("process_chip: vrc4d detected.\n"); - } - else if (pins[3] == P_PRG_A3 && pins[4] == P_PRG_A2) { - mapperid = B_KONAMI_VRC4E; - log_printf("process_chip: vrc4e detected.\n"); - } - else { - log_printf("process_chip: unknown vrc4 pin configuration (pin3 = %d, pin4 = %d)\n", pins[3], pins[4]); - } - } - } - - //leave these alone! - if(mapper == 93) { - mapperid = B_SUNSOFT_2; - log_printf("process_chip: sunsoft-3R board. using sunsoft-2 mapper.\n"); - } - - //return new mapperid - return(mapperid); -} - -//finds the correct mapper id using the information given -static int determine_mapperid(cart_t *cart,char *type,char *mapper,node_t *boardnode) -{ - int n,ret = B_UNSUPPORTED; - node_t *node; - - //turn mapper string into mapper integer - n = mapper ? atoi(mapper) : -1; - - //see if this board is supported by the unif mappers - if((ret = mapper_get_mapperid_unif(type ? type : "")) == B_UNSUPPORTED) { - - //check if this ines mapper is supported - if((ret = mapper_get_mapperid_ines(n)) == B_UNSUPPORTED) { - - //not supported, use mapperid from the rom loader maybe it is different - ret = cart->mapperid; - } - else { - log_printf("determine_mapperid: ines mapper %d supported. (board '%s')\n",n,type ? type : ""); - - //save ines -> unif conversions to add to the unif section - {{{{{{{ - FILE *fp = fopen("c:\\mingw\\home\\ines2unf.txt","at"); - - if(fp) { - fprintf(fp,"%d = %s\n",n,type ? type : ""); - fclose(fp); - } - }}}}}}} - - } - } - else - log_printf("determine_mapperid: unif board '%s' supported.\n",type); - - //find the chips used and process them - node = get_child("chip",boardnode); - while(node) { - ret = process_chip(ret,type,n,node); - node = get_sibling("chip",node); - } - - return(ret); -} - -static int sizestr2int(char *str) -{ - char *tmp = strdup(str); - char *p = tmp; - int ret = -1; - - //convert to lowercase - while(*p) { - *p = tolower(*p); - p++; - } - - //search for the k for kilobytes - if((p = strchr(tmp,'k')) != 0) { - *p = 0; - ret = atoi(tmp) * 1024; - } - - //search for the m for megabytes - else if((p = strchr(tmp,'m')) != 0) { - *p = 0; - ret = atoi(tmp) * 1024 * 1024; - } - - //must be just bytes (or gigabytes...) - else { - ret = atoi(tmp); - } - - free(tmp); - return(ret); -} - -static int hasbattery(node_t *node) -{ - char *tmp = find_attrib(node,"battery"); - - if(tmp && strcmp(tmp,"1") == 0) - return(1); - return(0); -} - -int cartdb_find(cart_t *cart) -{ - node_t *cartnode,*boardnode,*node; - u32 crc32,wramsize,vramsize,battery; - char *str,*str2,*tmp; - - if(cartxml == 0) - return(1); - - //calculate crc32 of entire image - crc32 = crc32_block(cart->prg.data,cart->prg.size,0); - crc32 = crc32_block(cart->chr.data,cart->chr.size,crc32); - - //initialize the size and battery vars - wramsize = vramsize = 0; - battery = 0; - - //try to find cart node with same crc32 - cartnode = find_cart(crc32); - if(cartnode) { - - log_printf("cartdb_find: cart found in database.\n"); - - //copy game title - if((str = find_attrib(cartnode->parent,"name"))) - strcpy(cart->title,str); - - //see if board node exists - if((boardnode = get_child("board",cartnode))) { - - //get unif board name and ines mapper number - str = find_attrib(boardnode,"type"); - str2 = find_attrib(boardnode,"mapper"); - - //set mapperid with information discovered - cart->mapperid = determine_mapperid(cart,str,str2,boardnode); - - //find the vram size - node = get_child("vram",boardnode); - while(node) { - tmp = find_attrib(node,"size"); - if(tmp) { - battery |= hasbattery(node) << 1; - vramsize += sizestr2int(tmp); - } - node = get_sibling("vram",node); - } - - //find the wram size and battery status - node = get_child("wram",boardnode); - while(node) { - tmp = find_attrib(node,"size"); - if(tmp) { - battery |= hasbattery(node); - wramsize += sizestr2int(tmp); - } - node = get_sibling("wram",node); - } - - //debug messages - if(wramsize) { - cart_setwramsize(cart,wramsize / 1024); - if(battery & 1) - cart->battery |= 1; - } - if(vramsize) { - cart_setvramsize(cart,vramsize / 1024); - if(battery & 2) - cart->battery |= 2; - } - return(0); - } - } - - log_printf("cartdb_find: cart not found in database.\n"); - return(1); -} -#endif - -int cartdb_init() { return 0; } -void cartdb_kill() { } -int cartdb_find(cart_t *cart) { return 1; } diff --git a/apps/nesemu2/src/cartdb/cartdb.h b/apps/nesemu2/src/cartdb/cartdb.h deleted file mode 100644 index eed0b0b1..00000000 --- a/apps/nesemu2/src/cartdb/cartdb.h +++ /dev/null @@ -1,31 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __cartdb_h__ -#define __cartdb_h__ - -#include "types.h" -#include "nes/cart/cart.h" - -int cartdb_init(); -void cartdb_kill(); -int cartdb_find(cart_t *cart); - -#endif diff --git a/apps/nesemu2/src/types.h b/apps/nesemu2/src/common.h similarity index 94% rename from apps/nesemu2/src/types.h rename to apps/nesemu2/src/common.h index 12ac4d4b..c205e17d 100644 --- a/apps/nesemu2/src/types.h +++ b/apps/nesemu2/src/common.h @@ -18,11 +18,12 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#ifndef __types_h__ -#define __types_h__ +#ifndef __common_h__ +#define __common_h__ #include -#define int64 long long +#include + #define INLINE inline typedef int8_t s8; @@ -45,5 +46,8 @@ int stricmp(char *s1,char *s2); #define read my_read #define write my_write #define select my_select +#define log_printf printf + +#define PATH_SEPERATOR '/' #endif diff --git a/apps/nesemu2/src/emu/commands.c b/apps/nesemu2/src/emu/commands.c deleted file mode 100644 index 129e52f0..00000000 --- a/apps/nesemu2/src/emu/commands.c +++ /dev/null @@ -1,113 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include "emu/commands.h" -#include "misc/log.h" -#include "misc/memutil.h" - -#define COMMAND_START static command_t commands[] = { -#define COMMAND(n) {"" #n "", command_ ## n}, -#define COMMAND_END {0,0}}; - -typedef int (*cmdfunc_t)(int argc,char **argv); - -typedef struct command_s { - char *name; - cmdfunc_t func; -} command_t; - -int command_help(int,char**); - -COMMAND_START - COMMAND(help) - COMMAND(mappers) - COMMAND(set) - COMMAND(unset) - COMMAND(quit) - COMMAND(load) - COMMAND(unload) - COMMAND(reset) - COMMAND(hardreset) - COMMAND(readcpu) - COMMAND(writecpu) - COMMAND(readppu) - COMMAND(dump) -COMMAND_END - -COMMAND_FUNC(help) -{ - command_t *c = commands; - - log_printf("available commands:\n "); - for(c=commands;c->name;c++) { - log_printf("%s ",c->name); - } - return(0); -} - -int command_init() -{ - return(0); -} - -void command_kill() -{ - -} - -int command_execute(char *s) -{ - int i,argc = 0; - char *argv[64]; //not more than 64 args! - char *str; - - //eat whitespace in front - while(*s == ' ') { - s++; - } - - //empty strings do nothing - if(strcmp(s,"") == 0) - return(1); - - //make a copy of the string - str = mem_strdup(s); - - //split up the command line (needs improvement) - argv[argc] = strtok(str," \t"); - while(argv[argc] != 0) { - argv[++argc] = strtok(0," \t"); - } - - //try to execute the command - for(i=0;commands[i].name;i++) { - if(strcmp(argv[0],commands[i].name) == 0) { - commands[i].func(argc,argv); - mem_free(str); - return(0); - } - } - - //bad command or file name! - log_printf("invalid command '%s'\n",argv[0]); - mem_free(str); - return(1); -} diff --git a/apps/nesemu2/src/emu/commands.h b/apps/nesemu2/src/emu/commands.h deleted file mode 100644 index d92e63bf..00000000 --- a/apps/nesemu2/src/emu/commands.h +++ /dev/null @@ -1,62 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __commands_h__ -#define __commands_h__ - -#include "types.h" - -#define CHECK_ARGS(num,msg) \ - if(argc < (num)) { \ - log_printf(msg); \ - return(1); \ - } - -#define CHECK_CART() \ - if(nes->cart == 0) { \ - log_printf("no rom loaded!\n"); \ - return(1); \ - } - -#define COMMAND_FUNC(n) int command_ ## n (int argc,char **argv) -#define COMMAND_DECL(n) COMMAND_FUNC(n); - -//general commands -COMMAND_DECL(mappers); -COMMAND_DECL(set); -COMMAND_DECL(unset); -COMMAND_DECL(quit); - -//nes commands -COMMAND_DECL(load); -COMMAND_DECL(unload); -COMMAND_DECL(reset); -COMMAND_DECL(hardreset); -COMMAND_DECL(readcpu); -COMMAND_DECL(writecpu); -COMMAND_DECL(readppu); -COMMAND_DECL(loadstate); -COMMAND_DECL(savestate); - -COMMAND_DECL(dump); - -int command_execute(char *str); - -#endif diff --git a/apps/nesemu2/src/emu/commands/general.c b/apps/nesemu2/src/emu/commands/general.c deleted file mode 100644 index a8a58a3c..00000000 --- a/apps/nesemu2/src/emu/commands/general.c +++ /dev/null @@ -1,101 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "emu/commands.h" -#include "misc/log.h" -#include "misc/config.h" -#include "mappers/mappers.h" -#include "mappers/mapperid.h" - -//just use include file, ditch this -extern int quit; - -COMMAND_FUNC(mappers) -{ - int i,j,n,n2; - const char *str; - - log_printf("supported ines mappers: "); - for(n=0,i=0;i<256;i++) { - if(mapper_get_mapperid_ines(i) >= 0) { - if(n) - log_printf(", "); - log_printf("%d",i); - n++; - } - } - - log_printf("\n\nsupported ines20 mappers: "); - for(n2=0,i=0;i<(256 * 1);i++) { - for(j=1;j<16;j++) { - if(mapper_get_mapperid_ines20(i,j) >= 0) { - if(n2) - log_printf(", "); - log_printf("%d.%d",i,j); - n2++; - } - } - } - - log_printf("\n\nsupported unif mappers:\n"); - for(i=0;;i++) { - if((str = mapper_get_unif_boardname(i)) == 0) - break; - log_printf(" %s\n",str); - } - log_printf("\n%d ines, %d ines20, %d unif, %d internal boards supported\n",n,n2,i,B_BOARDEND); - return(0); -} - -COMMAND_FUNC(set) -{ - var_t *var; - - //if no arguments was passed, just show the variables - if(argc < 2) { - var = config_get_head(); - while(var) { - log_printf(" %s = %s\n",var->name,var->data); - var = var->next; - } - } - - //we have arguments, validate them and set var - else { - char *name = argv[1]; - char *data = argv[2]; - - var_set_string(name,data); - } - return(0); -} - -COMMAND_FUNC(unset) -{ - CHECK_ARGS(2,"usage: unset \n"); - var_unset(argv[1]); - return(0); -} - -COMMAND_FUNC(quit) -{ - quit++; - return(0); -} diff --git a/apps/nesemu2/src/emu/commands/nes.c b/apps/nesemu2/src/emu/commands/nes.c deleted file mode 100644 index 3d0ff90f..00000000 --- a/apps/nesemu2/src/emu/commands/nes.c +++ /dev/null @@ -1,184 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include -#include -#include "emu/commands.h" -#include "misc/log.h" -#include "misc/config.h" -#include "nes/nes.h" - -//!!!!!! kludge alert !!!!!! -extern int running; - -static u32 str2int(char *str) -{ - u32 i,ret = 0; - size_t len; - char ch; - int base = 10; - - if(*str == '$') { - base = 16; - str++; - } - else if(str[0] == '0' && tolower(str[1]) == 'x') { - str += 2; - base = 16; - } - - len = strlen(str); - - for(i=0;i= 'A' && ch <= 'F') - ch = 10 + ch - 'A'; - else if(ch >= '0' && ch <= '9') - ch = ch - '0'; - else { - return((u32)-1); - } - ret += ch; - } - return(ret); -} - -COMMAND_FUNC(load) -{ - //need a way to load the functions common to all systems/targets - if(nes_load(argv[1]) == 0) { - nes_reset(1); - running = config_get_bool("nes.pause_on_load") ? 0 : 1; - } - return(0); -} - -COMMAND_FUNC(unload) -{ - CHECK_CART(); - nes_unload(); - running = 0; - return(0); -} - -COMMAND_FUNC(reset) -{ - CHECK_CART(); - nes_reset(0); - return(0); -} - -COMMAND_FUNC(hardreset) -{ - CHECK_CART(); - nes_reset(1); - return(0); -} - -COMMAND_FUNC(readcpu) -{ - u32 addr; - - CHECK_ARGS(2,"usage: readcpu \n"); - CHECK_CART(); - addr = str2int(argv[1]); - if(addr == (u32)-1) { - log_printf("invalid address\n"); - } - else { - log_printf("$%04X = $%02X\n",addr,cpu_read(addr)); - } - return(0); -} - -COMMAND_FUNC(writecpu) -{ - u32 addr,data; - - CHECK_ARGS(3,"usage: writecpu \n"); - CHECK_CART(); - addr = str2int(argv[1]); - data = str2int(argv[2]); - if(addr == (u32)-1) { - log_printf("invalid address\n"); - } - else if(data == (u32)-1) { - log_printf("invalid data\n"); - } - else { - log_printf("$%04X = $%02X\n",addr,data); - cpu_write(addr,(u8)data); - } - return(0); -} - -COMMAND_FUNC(readppu) -{ - u32 addr, size, n; - - CHECK_ARGS(2, "usage: readppu [size]\n"); - CHECK_CART(); - addr = str2int(argv[1]); - if (addr == (u32)-1) { - log_printf("invalid address\n"); - } - else { - if (argc >= 3) { - size = str2int(argv[2]); - for (n = 0; n \n"); - CHECK_CART(); - - //dumping prg - if (stricmp("prg", argv[1]) == 0) { - size = nes->cart->prg.size; - memfile_t *mf = memfile_open(argv[2], "wb"); - - if (mf == 0) { - log_printf("error creating file %s\n", argv[2]); - return(0); - } - memfile_write(nes->cart->prg.data, size, 1, mf); - memfile_close(mf); - log_printf("dumped %d bytes of PRG to file '%s'\n", size, argv[2]); - } - - else - log_printf("please choose valid area and filename to save for dump\n"); - return(0); -} diff --git a/apps/nesemu2/src/emu/emu.c b/apps/nesemu2/src/emu/emu.c index c98bc91c..5984b191 100644 --- a/apps/nesemu2/src/emu/emu.c +++ b/apps/nesemu2/src/emu/emu.c @@ -20,13 +20,10 @@ #include #include "emu/emu.h" -#include "emu/events.h" -#include "misc/log.h" #include "misc/memutil.h" #include "misc/strutil.h" #include "misc/config.h" #include "misc/crc32.h" -#include "cartdb/cartdb.h" #include "system/system.h" #include "system/video.h" #include "system/input.h" @@ -50,8 +47,6 @@ typedef struct subsystem_s { SUBSYSTEM_START SUBSYSTEM(memutil) SUBSYSTEM(config) - SUBSYSTEM_NOKILL(log) - SUBSYSTEM(cartdb) SUBSYSTEM(system) SUBSYSTEM(video) SUBSYSTEM(input) @@ -104,28 +99,9 @@ void emu_kill() int emu_exit(int ret) { - //kludge alert! need list here or something atexit() style... - log_kill(); return(ret); } -int emu_addsubsystem(char *name,initfunc_t init,killfunc_t kill) -{ - int i; - - log_printf("emu_addsubsystem: adding subsystem '%s'\n",name); - for(i=0;subsystems[i].id != -1;i++); - subsystems[i].id = 50; - strcpy(subsystems[i].name,name); - subsystems[i].init = init; - subsystems[i].kill = kill; - subsystems[i+1].id = -1; - strcpy(subsystems[i+1].name,""); - subsystems[i+1].init = 0; - subsystems[i+1].kill = 0; - return(0); -} - int emu_mainloop() { u8 *line = (u8*)mem_alloc(512); @@ -171,68 +147,3 @@ int emu_mainloop() mem_free(line); return(0); } - -#if 0 -int emu_mainloop_test(char *script) -{ - u64 t,total,frames; - char line[1024],*rom = "",*test,*p; - memfile_t *file; - int testrunning = 0; - - log_printf("emu_mainloop_test: starting automated tests from '%s'\n",script); - - if((file = memfile_open(script,"rb")) == 0) { - log_printf("emu_mainloop_test: error opening test script '%s'\n",script); - return(0); - } - - //begin the main loop - total = 0; - frames = 0; - while(quit == 0) { - if(nes->movie.mode & (MOVIE_CRCFAIL | MOVIE_CRCPASS)) { - if(nes->movie.mode & MOVIE_CRCFAIL) - log_printf("emu_mainloop_test: test over. failed. (rom = '%s')\n",rom); - if(nes->movie.mode & MOVIE_CRCPASS) - log_printf("emu_mainloop_test: test over. passed. (rom = '%s')\n",rom); - testrunning = 0; - } - if(testrunning == 0) { - do { - if(memfile_gets(line,1024,file) == 0) { - quit++; - p = 0; - break; - } - p = str_eatwhitespace(line); - log_printf("line: %s",p); - } while(*p == '#'); - if(p == 0) - break; - rom = str_eatwhitespace(strtok(p,";\r\n")); - test = str_eatwhitespace(strtok(0,";\r\n")); - log_printf("rom, test = '%s', '%s'\n",rom,test); - if(emu_event(E_LOADROM,(void*)rom) == 0) { - if(emu_event(E_LOADMOVIE,(void*)test) == 0) { - emu_event(E_PLAYMOVIE,0); - testrunning = 1; - } - } - } - t = system_gettick(); - system_checkevents(); - input_poll(); - video_startframe(); - if(running && nes->cart) { - nes_frame(); - } - video_endframe(); - total += system_gettick() - t; - frames++; - } - log_printf("fps: %f (%d frames)\n",(double)frames / (double)total * system_getfrequency(),frames); - memfile_close(file); - return(0); -} -#endif diff --git a/apps/nesemu2/src/emu/emu.h b/apps/nesemu2/src/emu/emu.h index c6037e1f..476e31af 100644 --- a/apps/nesemu2/src/emu/emu.h +++ b/apps/nesemu2/src/emu/emu.h @@ -21,7 +21,7 @@ #ifndef __emu_h__ #define __emu_h__ -#include "types.h" +#include "common.h" typedef int (*initfunc_t)(); typedef void (*killfunc_t)(); @@ -32,7 +32,6 @@ extern int quit,running; int emu_init(); void emu_kill(); int emu_exit(int ret); -int emu_addsubsystem(char *name,initfunc_t init,killfunc_t kill); int emu_mainloop(); int emu_mainloop_test(char *script); diff --git a/apps/nesemu2/src/emu/events.c b/apps/nesemu2/src/emu/events.c deleted file mode 100644 index d9c25de6..00000000 --- a/apps/nesemu2/src/emu/events.c +++ /dev/null @@ -1,193 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "emu/events.h" -#include "emu/emu.h" -#include "misc/log.h" -#include "misc/config.h" -#include "misc/paths.h" -#include "misc/memutil.h" -#include "system/video.h" -#include "system/sound.h" -#include "nes/nes.h" -#include "nes/nes.h" -#include "nes/state/state.h" -#include "mappers/mapperid.h" - -static void setfullscreen(int fs) -{ - video_kill(); - sound_pause(); - config_set_bool("video.fullscreen",fs); - sound_play(); - video_init(); - ppu_sync(); -} - -int emu_event(int id,void *data) -{ - int ret = 0; - char dest[1024]; - char *str; - - switch(id) { - - case E_QUIT: - quit++; - break; - - case E_LOADROM: - if((ret = nes_load((char*)data)) == 0) { - nes_reset(1); - running = config_get_bool("video.pause_on_load") ? 0 : 1; - } - else - ret = 1; - break; - - case E_LOADPATCH: - if(nes->cart == 0) - break; - str = mem_strdup(nes->cart->filename); - if((ret = nes_load_patched(str,(char*)data)) == 0) { - nes_reset(1); - } - mem_free(str); - break; - - case E_UNLOAD: - nes_unload(); - break; - - case E_SOFTRESET: - nes_reset(0); - break; - - case E_HARDRESET: - nes_reset(1); - break; - - case E_LOADSTATE: - if(nes->cart == 0) - break; - paths_makestatefilename(nes->cart->filename,dest,1024); - nes_loadstate(dest); - break; - - case E_SAVESTATE: - if (nes == 0 || nes->cart == 0) { - log_printf("emu_event: cannot load state, no rom loaded\n"); - break; - } - paths_makestatefilename(nes->cart->filename,dest,1024); - nes_savestate(dest); - break; - - case E_RECORDMOVIE: - log_printf("emu_event: recording movie from frame %d\n",nes->ppu.frames); - movie_record(); - break; - - case E_PLAYMOVIE: - movie_play(); - log_printf("emu_event: playing movie from frame %d\n",nes->ppu.frames); - break; - - case E_STOPMOVIE: - movie_stop(); - log_printf("emu_event: stopping movie at frame %d\n",nes->ppu.frames); - break; - - case E_LOADMOVIE: - movie_load((char*)data); - break; - - case E_SAVEMOVIE: - movie_save((char*)data); - break; - - case E_FLIPDISK: - if(nes->cart == 0) - break; - if((nes->cart->mapperid & B_TYPEMASK) == B_FDS) { - u8 d[4] = {0,0,0,0}; - - nes->mapper->state(CFG_SAVE,d); - if(d[0] == 0xFF) - d[0] = 0; - else { -// d[0] ^= 1; - d[0] = (d[0] + 1) & 3; - } - nes->mapper->state(CFG_LOAD,d); - log_printf("disk inserted! side = %d\n",d[0]); - } - else - log_printf("cannot flip disk. not fds.\n"); - break; - - case E_DUMPDISK: - if(nes->cart == 0) - break; -#if 0 - if((nes->cart->mapperid & B_TYPEMASK) == B_FDS) { - FILE *fp; - - log_printf("dumping disk as dump.fds\n"); - if((fp = fopen("dump.fds","wb")) != 0) { - fwrite(nes->cart->disk.data,1,nes->cart->disk.size,fp); - fclose(fp); - } - } -#endif - break; - - case E_TOGGLERUNNING: - running ^= 1; - break; - - case E_PAUSE: - running = 0; - break; - - case E_UNPAUSE: - running = 1; - break; - - case E_TOGGLEFULLSCREEN: - setfullscreen(config_get_bool("video.fullscreen") ^ 1); - break; - - case E_FULLSCREEN: - setfullscreen(1); - break; - - case E_WINDOWED: - setfullscreen(0); - break; - - //unhandled event - default: - log_printf("emu_event: unhandled event id %d\n",id); - ret = -1; - break; - } - return(ret); -} diff --git a/apps/nesemu2/src/emu/events.h b/apps/nesemu2/src/emu/events.h deleted file mode 100644 index af8e7da8..00000000 --- a/apps/nesemu2/src/emu/events.h +++ /dev/null @@ -1,65 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __events_h__ -#define __events_h__ - -#include "types.h" - -//event id's -enum events_e { - E_UNKNOWN = 0, - E_QUIT, - E_LOADROM, - E_LOADPATCH, - E_UNLOAD, - E_SOFTRESET, - E_HARDRESET, - E_SAVESTATE, - E_LOADSTATE, - E_RECORDMOVIE, - E_PLAYMOVIE, - E_STOPMOVIE, - E_LOADMOVIE, - E_SAVEMOVIE, - E_FLIPDISK, - E_DUMPDISK, - E_AUTOTEST, - - //change running state - E_TOGGLERUNNING, - E_PAUSE, - E_UNPAUSE, - - //change from fullscreen/windowed - E_TOGGLEFULLSCREEN, - E_FULLSCREEN, - E_WINDOWED, - - //last valid event id - E_LASTEVENT, - - //clones! - E_RESET = E_SOFTRESET, -}; - -int emu_event(int id,void *data); - -#endif diff --git a/apps/nesemu2/src/inputdev/inputdev.c b/apps/nesemu2/src/inputdev/inputdev.c index 735b8ef3..bad4637b 100644 --- a/apps/nesemu2/src/inputdev/inputdev.c +++ b/apps/nesemu2/src/inputdev/inputdev.c @@ -38,8 +38,8 @@ static inputdev_t *get_inputdev(int inputdevid) //standard input devices INPUTDEV(I_JOYPAD0); INPUTDEV(I_JOYPAD1); - INPUTDEV(I_ZAPPER); - INPUTDEV(I_POWERPAD); + //INPUTDEV(I_ZAPPER); + //INPUTDEV(I_POWERPAD); return(0); } diff --git a/apps/nesemu2/src/inputdev/inputdev.h b/apps/nesemu2/src/inputdev/inputdev.h index 111874f4..c52c9dda 100644 --- a/apps/nesemu2/src/inputdev/inputdev.h +++ b/apps/nesemu2/src/inputdev/inputdev.h @@ -60,8 +60,6 @@ enum inputdevid_e { I_NULL, I_JOYPAD0, I_JOYPAD1, - I_ZAPPER, - I_POWERPAD, }; inputdev_t *inputdev_get(int id); diff --git a/apps/nesemu2/src/inputdev/joypad0.c b/apps/nesemu2/src/inputdev/joypad0.c index 53af6129..fcb1f25e 100644 --- a/apps/nesemu2/src/inputdev/joypad0.c +++ b/apps/nesemu2/src/inputdev/joypad0.c @@ -59,12 +59,6 @@ static void update() static int movie(int mode) { - if(mode & MOVIE_PLAY) { - buttons = movie_read_u8(); - } - else if(mode & MOVIE_RECORD) { - movie_write_u8(buttons); - } return(1); } diff --git a/apps/nesemu2/src/inputdev/joypad1.c b/apps/nesemu2/src/inputdev/joypad1.c index e95132a8..075b0c46 100644 --- a/apps/nesemu2/src/inputdev/joypad1.c +++ b/apps/nesemu2/src/inputdev/joypad1.c @@ -59,12 +59,6 @@ static void update() static int movie(int mode) { - if(mode & MOVIE_PLAY) { - buttons = movie_read_u8(); - } - else if(mode & MOVIE_RECORD) { - movie_write_u8(buttons); - } return(1); } diff --git a/apps/nesemu2/src/inputdev/powerpad.c b/apps/nesemu2/src/inputdev/powerpad.c deleted file mode 100644 index 5e81fe4b..00000000 --- a/apps/nesemu2/src/inputdev/powerpad.c +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "inputdev.h" -#include "system/input.h" - -static u32 portdata; -static u16 buttons; -static u8 counter,strobe; - -static u8 read() -{ - u8 ret = 0; - - ret |= ((portdata >> (counter + 0)) & 1) << 3; - ret |= ((portdata >> (counter + 8)) & 1) << 4; - counter++; - return(ret | 0x40); -} - -static void write(u8 data) -{ - if(((data & 1) == 0) && (strobe & 1)) { - portdata = 0xFFFF0000 | buttons; - counter = 0; - } - strobe = data; -} - -static void update() -{ - buttons = 0; -} - -static void state(int mode,u8 *data) -{ - STATE_U32(portdata); - STATE_U16(buttons); - STATE_U8(counter); - STATE_U8(strobe); -} - -INPUTDEV(I_POWERPAD,read,write,update,0,state); diff --git a/apps/nesemu2/src/inputdev/zapper.c b/apps/nesemu2/src/inputdev/zapper.c deleted file mode 100644 index f9807e6e..00000000 --- a/apps/nesemu2/src/inputdev/zapper.c +++ /dev/null @@ -1,114 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "inputdev.h" -#include "system/video.h" -#include "system/input.h" -#include - -static u8 portdata; -static u8 counter,buttons,strobe; -static int xpos,ypos; - -extern int video_zapperhit(int x,int y); - -static u8 read() -{ - int x = xpos,y = ypos; - int X,Y; - int hits = 0; - u8 ret = 0; - - //check for button press - if(buttons) - ret = 0x10; - - //see if zapper is even in range - if((x < 0) || (x >= 256) || (y < 0) || (y >= 240)) - ret |= 8; - - //check for zapper light data - else { - for(Y = y - 8; Y < y + 8; Y++) { - if(Y < 0) - Y = 0; - if(Y < (int)nes->ppu.scanline - 32) - continue; - if(Y >(int)nes->ppu.scanline) - break; - for(X = x - 8; X < x + 8; X++) { - if(X < 0) - X = 0; - if(X > 255) - break; - if((Y == (int)nes->ppu.scanline) && (X >= (int)nes->ppu.linecycles)) - break; - assert(0); - //if(video_zapperhit(X,Y)) - // hits++; - } - } - } - - //if valid hit data, then set bit state - if(hits < 64) - ret |= 8; - - return(ret); -} - -static void write(u8 data) -{ - if(((data & 1) == 0) && (strobe & 1)) { - portdata = buttons; - counter = 0; - } - strobe = data; -} - -static void update() -{ - buttons = input_poll_mouse(&xpos,&ypos); -} - -static int movie(int mode) -{ - if(mode & MOVIE_PLAY) { - buttons = movie_read_u8(); - xpos = (int)movie_read_u8(); - ypos = (int)movie_read_u8(); - } - else if(mode & MOVIE_RECORD) { - movie_write_u8(buttons); - movie_write_u8((u8)xpos); - movie_write_u8((u8)ypos); - } - return(1); -} - -static void state(int mode,u8 *data) -{ - STATE_U8(portdata); - STATE_U8(counter); - STATE_U8(strobe); - STATE_U8(buttons); -} - -INPUTDEV(I_ZAPPER,read,write,update,movie,state); diff --git a/apps/nesemu2/src/mappers/chips/mmc1.h b/apps/nesemu2/src/mappers/chips/mmc1.h index 199a7061..17f028e2 100644 --- a/apps/nesemu2/src/mappers/chips/mmc1.h +++ b/apps/nesemu2/src/mappers/chips/mmc1.h @@ -21,7 +21,7 @@ #ifndef __mmc1_h__ #define __mmc1_h__ -#include "types.h" +#include "common.h" #define C_MMC1 0x10 diff --git a/apps/nesemu2/src/mappers/chips/mmc2.h b/apps/nesemu2/src/mappers/chips/mmc2.h index 231249a3..e52f87a6 100644 --- a/apps/nesemu2/src/mappers/chips/mmc2.h +++ b/apps/nesemu2/src/mappers/chips/mmc2.h @@ -21,7 +21,7 @@ #ifndef __mmc2_h__ #define __mmc2_h__ -#include "types.h" +#include "common.h" #define C_MMC2 0x20 #define C_MMC4 0x40 diff --git a/apps/nesemu2/src/mappers/chips/mmc3.h b/apps/nesemu2/src/mappers/chips/mmc3.h index 12d65cae..84ada28b 100644 --- a/apps/nesemu2/src/mappers/chips/mmc3.h +++ b/apps/nesemu2/src/mappers/chips/mmc3.h @@ -21,7 +21,7 @@ #ifndef __mmc3_h__ #define __mmc3_h__ -#include "types.h" +#include "common.h" #define C_MMC3 0x30 #define C_MMC6 0x60 diff --git a/apps/nesemu2/src/mappers/chips/mmc4.h b/apps/nesemu2/src/mappers/chips/mmc4.h index 5f5f366a..da669fb7 100644 --- a/apps/nesemu2/src/mappers/chips/mmc4.h +++ b/apps/nesemu2/src/mappers/chips/mmc4.h @@ -21,7 +21,7 @@ #ifndef __mmc4_h__ #define __mmc4_h__ -#include "types.h" +#include "common.h" void mmc4_init(int hard); void mmc4_write(u32 addr,u8 value); diff --git a/apps/nesemu2/src/mappers/chips/mmc5.h b/apps/nesemu2/src/mappers/chips/mmc5.h index 3be0b38d..e9107bad 100644 --- a/apps/nesemu2/src/mappers/chips/mmc5.h +++ b/apps/nesemu2/src/mappers/chips/mmc5.h @@ -21,7 +21,7 @@ #ifndef __mmc5_h__ #define __mmc5_h__ -#include "types.h" +#include "common.h" void mmc5_sync(); u8 mmc5_read(u32 addr); diff --git a/apps/nesemu2/src/mappers/chips/namcot-108.h b/apps/nesemu2/src/mappers/chips/namcot-108.h index 87c27d6f..88eb8c94 100644 --- a/apps/nesemu2/src/mappers/chips/namcot-108.h +++ b/apps/nesemu2/src/mappers/chips/namcot-108.h @@ -21,7 +21,7 @@ #ifndef __namcot_108_h__ #define __namcot_108_h__ -#include "types.h" +#include "common.h" u8 namcot108_getindex(); u8 *namcot108_getregs(); diff --git a/apps/nesemu2/src/mappers/chips/namcot-163.h b/apps/nesemu2/src/mappers/chips/namcot-163.h index 1a9f54e0..39afe99a 100644 --- a/apps/nesemu2/src/mappers/chips/namcot-163.h +++ b/apps/nesemu2/src/mappers/chips/namcot-163.h @@ -21,7 +21,7 @@ #ifndef __namcot_163_h__ #define __namcot_163_h__ -#include "types.h" +#include "common.h" #define NAMCOT_HAS_SOUND 0x01 //has sound registers #define NAMCOT_HAS_IRQ 0x02 //has irq registers diff --git a/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h b/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h index 5e242071..7a6968c6 100644 --- a/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h +++ b/apps/nesemu2/src/mappers/chips/taito-tc0190fmc.h @@ -21,7 +21,7 @@ #ifndef __taito_tc0190fmc_h__ #define __taito_tc0190fmc_h__ -#include "types.h" +#include "common.h" void tc0190fmc_sync(); void tc0190fmc_reset(int hard); diff --git a/apps/nesemu2/src/mappers/fds/calls.h b/apps/nesemu2/src/mappers/fds/calls.h index d6891b34..69f8a5fb 100644 --- a/apps/nesemu2/src/mappers/fds/calls.h +++ b/apps/nesemu2/src/mappers/fds/calls.h @@ -22,7 +22,6 @@ #define __fds__calls_h__ #include "nes/nes.h" -#include "misc/log.h" #define HLECALL(name) void hle_ ## name () diff --git a/apps/nesemu2/src/mappers/fds/fds.c b/apps/nesemu2/src/mappers/fds/fds.c index 3eff2251..02692251 100644 --- a/apps/nesemu2/src/mappers/fds/fds.c +++ b/apps/nesemu2/src/mappers/fds/fds.c @@ -22,7 +22,6 @@ #include "mappers/mapperinc.h" #include "mappers/sound/s_FDS.h" #include "mappers/fds/hle.h" -#include "misc/log.h" #include "misc/config.h" #define SHORTIRQ 100 diff --git a/apps/nesemu2/src/mappers/mapperinc.h b/apps/nesemu2/src/mappers/mapperinc.h index 1a19b88a..5258eda6 100644 --- a/apps/nesemu2/src/mappers/mapperinc.h +++ b/apps/nesemu2/src/mappers/mapperinc.h @@ -32,6 +32,5 @@ #include "nes/nes.h" #include "nes/memory.h" #include "nes/state/state.h" -#include "misc/log.h" #endif diff --git a/apps/nesemu2/src/mappers/mappers.h b/apps/nesemu2/src/mappers/mappers.h index 9abbcf49..99eae7c9 100644 --- a/apps/nesemu2/src/mappers/mappers.h +++ b/apps/nesemu2/src/mappers/mappers.h @@ -21,7 +21,7 @@ #ifndef __mappers_h__ #define __mappers_h__ -#include "types.h" +#include "common.h" typedef struct mapper_s { int boardid; //internal board id diff --git a/apps/nesemu2/src/mappers/nsf/nsf.c b/apps/nesemu2/src/mappers/nsf/nsf.c index 2231a82e..fd12df78 100644 --- a/apps/nesemu2/src/mappers/nsf/nsf.c +++ b/apps/nesemu2/src/mappers/nsf/nsf.c @@ -25,7 +25,6 @@ #include "mappers/sound/s_N106.h" #include "mappers/sound/s_VRC6.h" #include "mappers/sound/s_VRC7.h" -#include "misc/log.h" static readfunc_t read4; static writefunc_t write4; diff --git a/apps/nesemu2/src/mappers/sound/s_DRIP.c b/apps/nesemu2/src/mappers/sound/s_DRIP.c index c8e667e4..c5f52673 100644 --- a/apps/nesemu2/src/mappers/sound/s_DRIP.c +++ b/apps/nesemu2/src/mappers/sound/s_DRIP.c @@ -1,6 +1,6 @@ #include #include "mappers/sound/s_DRIP.h" -#include "types.h" +#include "common.h" typedef struct dripsound_s { u8 FIFO[256], ReadPos, WritePos; diff --git a/apps/nesemu2/src/mappers/sound/s_FDS.c b/apps/nesemu2/src/mappers/sound/s_FDS.c index 16633dce..8f2ba5dd 100644 --- a/apps/nesemu2/src/mappers/sound/s_FDS.c +++ b/apps/nesemu2/src/mappers/sound/s_FDS.c @@ -5,327 +5,6 @@ * $Id: s_FDS.c 376 2008-06-29 20:58:13Z Quietust $ */ -#if 0 -//#include "..\..\interface.h" -#include -#include -#include "types.h" -#include "s_FDS.h" - -// Sound code borrowed from NEZplug 0.9.4.8 - -#define FM_DEPTH 0 /* 0,1,2 */ -#define NES_BASECYCLES (21477270) -#define PGCPS_BITS (32-16-6) -#define EGCPS_BITS (12) -#define VOL_BITS 12 - -#define LOG_BITS 12 -#define LIN_BITS 7 -#define LOG_LIN_BITS 30 - -struct FDS_EG { - u8 spd; - u8 cnt; - u8 mode; - u8 volume; -} eg; -struct FDS_PG { - u32 spdbase; - u32 spd; - u32 freq; -} pg; -struct FDS_WG { - u32 phase; - s8 wave[0x40]; - u8 wavptr; - s8 output; - u8 disable; - u8 disable2; -} wg; - -struct FDS_OP { - struct FDS_EG eg; - struct FDS_PG pg; - struct FDS_WG wg; - u8 bias; - u8 wavebase; - u8 d[2]; -} op[2]; - -typedef struct FDSsound { - struct FDS_OP op[2]; - u32 phasecps; - u32 envcnt; - u32 envspd; - u32 envcps; - u8 envdisable; - u8 d[3]; - u32 lvl; - s32 mastervolumel[4]; - u32 mastervolume; - u32 srate; - u8 reg[0x10]; -} TFDSsound, *PFDSsound; - -static TFDSsound FDSsound; - -static u32 lineartbl[(1 << LIN_BITS) + 1]; -static u32 logtbl[1 << LOG_BITS]; - -/*static u32 LinearToLog(s32 l) -{ - return (l < 0) ? (lineartbl[-l] + 1) : lineartbl[l]; -}*/ - -static s32 LogToLinear(u32 l, u32 sft) -{ - s32 ret; - u32 ofs; - l += sft << (LOG_BITS + 1); - sft = l >> (LOG_BITS + 1); - if (sft >= LOG_LIN_BITS) return 0; - ofs = (l >> 1) & ((1 << LOG_BITS) - 1); - ret = logtbl[ofs] >> sft; - return (l & 1) ? -ret : ret; -} - -static void LogTableInitialize(void) -{ - static volatile u32 initialized = 0; - u32 i; - double a; - if (initialized) return; - initialized = 1; - for (i = 0; i < (1 << LOG_BITS); i++) - { - a = (1 << LOG_LIN_BITS) / pow(2, i / (double)(1 << LOG_BITS)); - logtbl[i] = (u32)a; - } - lineartbl[0] = LOG_LIN_BITS << LOG_BITS; - for (i = 1; i < (1 << LIN_BITS) + 1; i++) - { - u32 ua; - a = (double)((u64)i << (LOG_LIN_BITS - LIN_BITS)); - ua = (u32)((LOG_LIN_BITS - (log(a) / log(2))) * (1 << LOG_BITS)); - lineartbl[i] = ua << 1; - } -} - -static void FDSSoundWGStep(struct FDS_WG *pwg) -{ - if (pwg->disable || pwg->disable2) return; - pwg->output = pwg->wave[(pwg->phase >> (PGCPS_BITS+16)) & 0x3f]; -} - -static void FDSSoundEGStep(struct FDS_EG *peg) -{ - if (peg->mode & 0x80) return; - if (++peg->cnt <= peg->spd) return; - peg->cnt = 0; - if (peg->mode & 0x40) - peg->volume += (peg->volume < 0x1f); - else - peg->volume -= (peg->volume > 0); -} - - -static s32 FDSSoundRender(void) -{ - s32 output; - /* Wave Generator */ - FDSSoundWGStep(&FDSsound.op[1].wg); - FDSSoundWGStep(&FDSsound.op[0].wg); - - /* Frequency Modulator */ - FDSsound.op[1].pg.spd = FDSsound.op[1].pg.spdbase; - if (FDSsound.op[1].wg.disable) - FDSsound.op[0].pg.spd = FDSsound.op[0].pg.spdbase; - else - { - u32 v1; - v1 = 0x10000 + ((s32)FDSsound.op[1].eg.volume) * (((s32)(((u8)FDSsound.op[1].wg.output) & 255)) - 64); - v1 = ((1 << 10) + v1) & 0xfff; - v1 = (FDSsound.op[0].pg.freq * v1) >> 10; - FDSsound.op[0].pg.spd = v1 * FDSsound.phasecps; - } - - /* Accumulator */ - output = FDSsound.op[0].eg.volume; - if (output > 0x20) output = 0x20; - output = (FDSsound.op[0].wg.output * output * FDSsound.mastervolumel[FDSsound.lvl]) >> (VOL_BITS - 4); - - /* Envelope Generator */ - if (!FDSsound.envdisable && FDSsound.envspd) - { - FDSsound.envcnt += FDSsound.envcps; - while (FDSsound.envcnt >= FDSsound.envspd) - { - FDSsound.envcnt -= FDSsound.envspd; - FDSSoundEGStep(&FDSsound.op[1].eg); - FDSSoundEGStep(&FDSsound.op[0].eg); - } - } - - /* Phase Generator */ - FDSsound.op[1].wg.phase += FDSsound.op[1].pg.spd; - FDSsound.op[0].wg.phase += FDSsound.op[0].pg.spd; - return (FDSsound.op[0].pg.freq != 0) ? output : 0; -} - -static void FDSSoundVolume(unsigned int volume) -{ - volume += 196; - FDSsound.mastervolume = (volume << (LOG_BITS - 8)) << 1; - FDSsound.mastervolumel[0] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 2; - FDSsound.mastervolumel[1] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 4 / 3; - FDSsound.mastervolumel[2] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 2 / 2; - FDSsound.mastervolumel[3] = LogToLinear(FDSsound.mastervolume, LOG_LIN_BITS - LIN_BITS - VOL_BITS) * 8 / 10; -} - -static const u8 wave_delta_table[8] = { - 0,(1 << FM_DEPTH),(2 << FM_DEPTH),(4 << FM_DEPTH), - 0,256 - (4 << FM_DEPTH),256 - (2 << FM_DEPTH),256 - (1 << FM_DEPTH), -}; - -static u32 DivFix(u32 p1, u32 p2, u32 fix) -{ - u32 ret; - ret = p1 / p2; - p1 = p1 % p2; - while (fix--) - { - p1 += p1; - ret += ret; - if (p1 >= p2) - { - p1 -= p2; - ret++; - } - } - return ret; -} - -void FDSsound_Load (void) -{ - memset(&FDSsound, 0, sizeof(TFDSsound)); - FDSsound.srate = 44100; - FDSsound.envcps = DivFix(NES_BASECYCLES, 12 * FDSsound.srate, EGCPS_BITS + 5 - 9 + 1); - FDSsound.envspd = 0xe8 << EGCPS_BITS; - FDSsound.envdisable = 1; - FDSsound.phasecps = DivFix(NES_BASECYCLES, 12 * FDSsound.srate, PGCPS_BITS); - LogTableInitialize(); - FDSSoundVolume(0); -} - -void FDSsound_Reset (void) -{ - int i; - for (i = 0; i < 0x40; i++) - { - FDSsound.op[0].wg.wave[i] = (i < 0x20) ? 0x1F : -0x20; - FDSsound.op[1].wg.wave[i] = 64; - } -} - -void FDSsound_Unload (void) -{ -} - -int FDSsound_Read (int Addr) -{ - if ((0x4040 <= Addr) && (Addr <= 0x407F)) - return FDSsound.op[0].wg.wave[Addr & 0x3F] + 0x20; - if (0x4090 == Addr) - return FDSsound.op[0].eg.volume | 0x40; - if (0x4092 == Addr) /* 4094? */ - return FDSsound.op[1].eg.volume | 0x40; - return -1; -} - -void FDSsound_Write (int Addr, int Val) -{ - if (0x4040 <= Addr && Addr <= 0x407F) - { - FDSsound.op[0].wg.wave[Addr - 0x4040] = ((int)(Val & 0x3F)) - 0x20; - } - else if (0x4080 <= Addr && Addr <= 0x408F) - { - struct FDS_OP *pop = &FDSsound.op[(Addr & 4) >> 2]; - FDSsound.reg[Addr - 0x4080] = Val; - switch (Addr & 0xf) - { - case 0: - case 4: - pop->eg.mode = Val & 0xc0; - if (pop->eg.mode & 0x80) - { - pop->eg.volume = (Val & 0x3f); - } - else - { - pop->eg.spd = Val & 0x3f; - } - break; - case 5: - FDSsound.op[1].bias = Val & 255; - break; - case 2: case 6: - pop->pg.freq &= 0x00000F00; - pop->pg.freq |= (Val & 0xFF) << 0; - pop->pg.spdbase = pop->pg.freq * FDSsound.phasecps; - break; - case 3: - FDSsound.envdisable = Val & 0x40; - case 7: - pop->pg.freq &= 0x000000FF; - pop->pg.freq |= (Val & 0x0F) << 8; - pop->pg.spdbase = pop->pg.freq * FDSsound.phasecps; - pop->wg.disable = Val & 0x80; - if (pop->wg.disable) - { - pop->wg.phase = 0; - pop->wg.wavptr = 0; - pop->wavebase = 0; - } - break; - case 8: - if (FDSsound.op[1].wg.disable) - { - s32 idx = Val & 7; - if (idx == 4) - { - FDSsound.op[1].wavebase = 0; - } - FDSsound.op[1].wavebase += wave_delta_table[idx]; - FDSsound.op[1].wg.wave[FDSsound.op[1].wg.wavptr + 0] = (FDSsound.op[1].wavebase + FDSsound.op[1].bias + 64) & 255; - FDSsound.op[1].wavebase += wave_delta_table[idx]; - FDSsound.op[1].wg.wave[FDSsound.op[1].wg.wavptr + 1] = (FDSsound.op[1].wavebase + FDSsound.op[1].bias + 64) & 255; - FDSsound.op[1].wg.wavptr = (FDSsound.op[1].wg.wavptr + 2) & 0x3f; - } - break; - case 9: - FDSsound.lvl = (Val & 3); - FDSsound.op[0].wg.disable2 = Val & 0x80; - break; - case 10: - FDSsound.envspd = Val << EGCPS_BITS; - break; - } - } -} - -int FDSsound_Get (int numCycles) -{ - return FDSSoundRender() >> 9; // current code does not run per-cycle -} - -int FDSsound_SaveLoad (int mode, int x, unsigned char *data) -{ - return x; -} -#endif - void FDSsound_Load (void) { } void FDSsound_Reset (void) { } void FDSsound_Unload (void) { } diff --git a/apps/nesemu2/src/mappers/sound/s_FME7.c b/apps/nesemu2/src/mappers/sound/s_FME7.c index bff49ef9..aba57838 100644 --- a/apps/nesemu2/src/mappers/sound/s_FME7.c +++ b/apps/nesemu2/src/mappers/sound/s_FME7.c @@ -8,7 +8,7 @@ //#include "..\..\interface.h" #include #include "s_FME7.h" -#include "types.h" +#include "common.h" // Sunsoft FME-7, based on the AY-8910 diff --git a/apps/nesemu2/src/mappers/sound/s_N106.c b/apps/nesemu2/src/mappers/sound/s_N106.c index 8e523bde..b9df3bb9 100644 --- a/apps/nesemu2/src/mappers/sound/s_N106.c +++ b/apps/nesemu2/src/mappers/sound/s_N106.c @@ -7,7 +7,7 @@ //#include "..\..\interface.h" #include -#include "types.h" +#include "common.h" #include "s_N106.h" // Namco 106 diff --git a/apps/nesemu2/src/mappers/sound/s_VRC6.c b/apps/nesemu2/src/mappers/sound/s_VRC6.c index 23f1408a..6755908a 100644 --- a/apps/nesemu2/src/mappers/sound/s_VRC6.c +++ b/apps/nesemu2/src/mappers/sound/s_VRC6.c @@ -5,11 +5,8 @@ * $Id: s_VRC6.c 376 2008-06-29 20:58:13Z Quietust $ */ -#include - -//#include "..\..\interface.h" #include "s_VRC6.h" -#include "types.h" +#include "common.h" // Konami VRC6 diff --git a/apps/nesemu2/src/mappers/sound/s_VRC7.c b/apps/nesemu2/src/mappers/sound/s_VRC7.c index 0666c4f7..3056cd9f 100644 --- a/apps/nesemu2/src/mappers/sound/s_VRC7.c +++ b/apps/nesemu2/src/mappers/sound/s_VRC7.c @@ -53,1441 +53,6 @@ YM2143 data sheet **************************************************************************************/ -#if 0 -#include "misc/log.h" -#include "s_VRC7.h" -#include -#include -#include -#include -#include "types.h" - -#define PI 3.14159265358979323846 - -enum {OPLL_VRC7_TONE=0}; - -/* voice data */ -typedef struct { - u32 TL,FB,EG,ML,AR,DR,SL,RR,KR,KL,AM,PM,WF; -} OPLL_PATCH; - -/* slot */ -typedef struct { - OPLL_PATCH patch; - - s32 type; /* 0 : modulator 1 : carrier */ - - /* OUTPUT */ - s32 feedback; - s32 output[2]; /* Output value of slot */ - - /* for Phase Generator (PG) */ - u16 *sintbl; /* Wavetable */ - u32 phase; /* Phase */ - u32 dphase; /* Phase increment amount */ - u32 pgout; /* output */ - - /* for Envelope Generator (EG) */ - s32 fnum; /* F-Number */ - s32 block; /* Block */ - s32 volume; /* Current volume */ - s32 sustine; /* Sustine 1 = ON, 0 = OFF */ - u32 tll; /* Total Level + Key scale level*/ - u32 rks; /* Key scale offset (Rks) */ - s32 eg_mode; /* Current state */ - u32 eg_phase; /* Phase */ - u32 eg_dphase; /* Phase increment amount */ - u32 egout; /* output */ - -} OPLL_SLOT; - -/* Mask */ -#define OPLL_MASK_CH(x) (1<<(x)) - -/* opll */ -typedef struct { - - u32 adr; - s32 out; - -#ifndef EMU2413_COMPACTION - u32 realstep; - u32 oplltime; - u32 opllstep; - s32 prev, next; -#endif - - /* Register */ - u8 LowFreq[6]; - u8 HiFreq[6]; - u8 InstVol[6]; - - u8 CustInst[8]; - - s32 slot_on_flag[6 * 2]; - - /* Pitch Modulator */ - u32 pm_phase; - s32 lfo_pm; - - /* Amp Modulator */ - s32 am_phase; - s32 lfo_am; - - u32 quality; - - /* Channel Data */ - s32 patch_number[6]; - s32 key_status[6]; - - /* Slot */ - OPLL_SLOT slot[6 * 2]; - - u32 mask; - -} OPLL; - -/* Create Object */ -OPLL *OPLL_new(u32 clk, u32 rate); -void OPLL_delete(OPLL *); - -/* Setup */ -void OPLL_reset(OPLL *); -void OPLL_set_rate(OPLL *opll, u32 r); -void OPLL_set_quality(OPLL *opll, u32 q); - -/* Port/Register access */ -void OPLL_writeIO(OPLL *, u32 reg, u32 val); -void OPLL_writeReg(OPLL *, u32 reg, u32 val); - -/* Synthsize */ -s16 OPLL_calc(OPLL *); - -/* Misc */ -void OPLL_forceRefresh(OPLL *); - -/* Channel Mask */ -u32 OPLL_setMask(OPLL *, u32 mask); -u32 OPLL_toggleMask(OPLL *, u32 mask); - - -static const unsigned char default_inst[15][8] = { - {0x03,0x21,0x04,0x06,0x8D,0xF2,0x42,0x17}, - {0x13,0x41,0x05,0x0E,0x99,0x96,0x63,0x12}, - {0x31,0x11,0x10,0x0A,0xF0,0x9C,0x32,0x02}, - {0x21,0x61,0x1D,0x07,0x9F,0x64,0x20,0x27}, - {0x22,0x21,0x1E,0x06,0xF0,0x76,0x08,0x28}, - {0x02,0x01,0x06,0x00,0xF0,0xF2,0x03,0x95}, - {0x21,0x61,0x1C,0x07,0x82,0x81,0x16,0x07}, - {0x23,0x21,0x1A,0x17,0xEF,0x82,0x25,0x15}, - {0x25,0x11,0x1F,0x00,0x86,0x41,0x20,0x11}, - {0x85,0x01,0x1F,0x0F,0xE4,0xA2,0x11,0x12}, - {0x07,0xC1,0x2B,0x45,0xB4,0xF1,0x24,0xF4}, - {0x61,0x23,0x11,0x06,0x96,0x96,0x13,0x16}, - {0x01,0x02,0xD3,0x05,0x82,0xA2,0x31,0x51}, - {0x61,0x22,0x0D,0x02,0xC3,0x7F,0x24,0x05}, - {0x21,0x62,0x0E,0x00,0xA1,0xA0,0x44,0x17} -}; - -/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.)*/ -#define PG_BITS 9 -#define PG_WIDTH (1<>(b)) - -/* Leave the lower b bit(s). */ -#define LOWBITS(c,b) ((c)&((1<<(b))-1)) - -/* Expand x which is s bits to d bits. */ -#define EXPAND_BITS(x,s,d) ((x)<<((d)-(s))) - -/* Expand x which is s bits to d bits and fill expanded bits '1' */ -#define EXPAND_BITS_X(x,s,d) (((x)<<((d)-(s)))|((1<<((d)-(s)))-1)) - -/* Adjust envelope speed which depends on sampling rate. */ -#define rate_adjust(x) (rate==49716?x:(u32)((double)(x)*clk/72/rate + 0.5)) /* added 0.5 to round the value*/ - -#define MOD(o,x) (&(o)->slot[(x)<<1]) -#define CAR(o,x) (&(o)->slot[((x)<<1)|1]) - -#define BIT(s,b) (((s)>>(b))&1) - -/* Input clock */ -static u32 clk = 844451141; -/* Sampling rate */ -static u32 rate = 3354932; - -/* WaveTable for each envelope amp */ -static u16 fullsintable[PG_WIDTH]; -static u16 halfsintable[PG_WIDTH]; - -static u16 *waveform[2] = { fullsintable, halfsintable }; - -/* LFO Table */ -static s32 pmtable[PM_PG_WIDTH]; -static s32 amtable[AM_PG_WIDTH]; - -/* Phase delta for LFO */ -static u32 pm_dphase; -static u32 am_dphase; - -/* dB to Liner table */ -static s16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2]; - -/* Liner to Log curve conversion table (for Attack rate). */ -static u16 AR_ADJUST_TABLE[1 << EG_BITS]; - -/* Definition of envelope mode */ -enum -{ SETTLE, ATTACK, DECAY, SUSHOLD, SUSTINE, ERELEASE, FINISH }; - -/* Phase incr table for Attack */ -static u32 dphaseARTable[16][16]; -/* Phase incr table for Decay and ERELEASE */ -static u32 dphaseDRTable[16][16]; - -/* KSL + TL Table */ -static u32 tllTable[16][8][1 << TL_BITS][4]; -static s32 rksTable[2][8][2]; - -/* Phase incr table for PG */ -static u32 dphaseTable[512][8][16]; - -/*************************************************** - - Create tables - -****************************************************/ -__inline static s32 -Min (s32 i, s32 j) -{ - if (i < j) - return i; - else - return j; -} - -/* Table for AR to LogCurve. */ -static void -makeAdjustTable (void) -{ - s32 i; - - AR_ADJUST_TABLE[0] = (1 << EG_BITS); - for (i = 1; i < 128; i++) - AR_ADJUST_TABLE[i] = (u16) ((double) (1 << EG_BITS) - 1 - (1 << EG_BITS) * log (i) / log (128)); -} - - -/* Table for dB(0 -- (1<= DB_MUTE) DB2LIN_TABLE[i] = 0; - DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (s16) (-DB2LIN_TABLE[i]); - } -} - -/* Liner(+0.0 - +1.0) to dB((1<> (20 - DP_BITS)); -} - -static void -makeTllTable (void) -{ -#define dB2(x) ((x)*2) - - static double kltable[16] = { - dB2 (0.000), dB2 (9.000), dB2 (12.000), dB2 (13.875), dB2 (15.000), dB2 (16.125), dB2 (16.875), dB2 (17.625), - dB2 (18.000), dB2 (18.750), dB2 (19.125), dB2 (19.500), dB2 (19.875), dB2 (20.250), dB2 (20.625), dB2 (21.000) - }; - - s32 tmp; - s32 fnum, block, TL, KL; - - for (fnum = 0; fnum < 16; fnum++) - for (block = 0; block < 8; block++) - for (TL = 0; TL < 64; TL++) - for (KL = 0; KL < 4; KL++) - { - if (KL == 0) - { - tllTable[fnum][block][TL][KL] = TL2EG (TL); - } - else - { - tmp = (s32) (kltable[fnum] - dB2 (3.000) * (7 - block)); - if (tmp <= 0) - tllTable[fnum][block][TL][KL] = TL2EG (TL); - else - tllTable[fnum][block][TL][KL] = (u32) ((tmp >> (3 - KL)) / EG_STEP) + TL2EG (TL); - } - } -} - -#ifdef USE_SPEC_ENV_SPEED -static double attacktime[16][4] = { - {0, 0, 0, 0}, - {1730.15, 1400.60, 1153.43, 988.66}, - {865.08, 700.30, 576.72, 494.33}, - {432.54, 350.15, 288.36, 247.16}, - {216.27, 175.07, 144.18, 123.58}, - {108.13, 87.54, 72.09, 61.79}, - {54.07, 43.77, 36.04, 30.90}, - {27.03, 21.88, 18.02, 15.45}, - {13.52, 10.94, 9.01, 7.72}, - {6.76, 5.47, 4.51, 3.86}, - {3.38, 2.74, 2.25, 1.93}, - {1.69, 1.37, 1.13, 0.97}, - {0.84, 0.70, 0.60, 0.54}, - {0.50, 0.42, 0.34, 0.30}, - {0.28, 0.22, 0.18, 0.14}, - {0.00, 0.00, 0.00, 0.00} -}; - -static double decaytime[16][4] = { - {0, 0, 0, 0}, - {20926.60, 16807.20, 14006.00, 12028.60}, - {10463.30, 8403.58, 7002.98, 6014.32}, - {5231.64, 4201.79, 3501.49, 3007.16}, - {2615.82, 2100.89, 1750.75, 1503.58}, - {1307.91, 1050.45, 875.37, 751.79}, - {653.95, 525.22, 437.69, 375.90}, - {326.98, 262.61, 218.84, 187.95}, - {163.49, 131.31, 109.42, 93.97}, - {81.74, 65.65, 54.71, 46.99}, - {40.87, 32.83, 27.36, 23.49}, - {20.44, 16.41, 13.68, 11.75}, - {10.22, 8.21, 6.84, 5.87}, - {5.11, 4.10, 3.42, 2.94}, - {2.55, 2.05, 1.71, 1.47}, - {1.27, 1.27, 1.27, 1.27} -}; -#endif - -/* Rate Table for Attack */ -static void -makeDphaseARTable (void) -{ - s32 AR, Rks, RM, RL; -#ifdef USE_SPEC_ENV_SPEED - u32 attacktable[16][4]; - - for (RM = 0; RM < 16; RM++) - for (RL = 0; RL < 4; RL++) - { - if (RM == 0) - attacktable[RM][RL] = 0; - else if (RM == 15) - attacktable[RM][RL] = EG_DP_WIDTH; - else - attacktable[RM][RL] = (u32) ((double) (1 << EG_DP_BITS) / (attacktime[RM][RL] * 3579545 / 72000)); - - } -#endif - - for (AR = 0; AR < 16; AR++) - for (Rks = 0; Rks < 16; Rks++) - { - RM = AR + (Rks >> 2); - RL = Rks & 3; - if (RM > 15) - RM = 15; - switch (AR) - { - case 0: - dphaseARTable[AR][Rks] = 0; - break; - case 15: - dphaseARTable[AR][Rks] = 0;/*EG_DP_WIDTH;*/ - break; - default: -#ifdef USE_SPEC_ENV_SPEED - dphaseARTable[AR][Rks] = rate_adjust (attacktable[RM][RL]); -#else - dphaseARTable[AR][Rks] = (u32)rate_adjust ((3 * (RL + 4) << (RM + 1))); -#endif - break; - } - } -} - -/* Rate Table for Decay and ERELEASE */ -static void -makeDphaseDRTable (void) -{ - s32 DR, Rks, RM, RL; - -#ifdef USE_SPEC_ENV_SPEED - u32 decaytable[16][4]; - - for (RM = 0; RM < 16; RM++) - for (RL = 0; RL < 4; RL++) - if (RM == 0) - decaytable[RM][RL] = 0; - else - decaytable[RM][RL] = (u32) ((double) (1 << EG_DP_BITS) / (decaytime[RM][RL] * 3579545 / 72000)); -#endif - - for (DR = 0; DR < 16; DR++) - for (Rks = 0; Rks < 16; Rks++) - { - RM = DR + (Rks >> 2); - RL = Rks & 3; - if (RM > 15) - RM = 15; - switch (DR) - { - case 0: - dphaseDRTable[DR][Rks] = 0; - break; - default: -#ifdef USE_SPEC_ENV_SPEED - dphaseDRTable[DR][Rks] = rate_adjust (decaytable[RM][RL]); -#else - dphaseDRTable[DR][Rks] = (u32)rate_adjust ((RL + 4) << (RM - 1)); -#endif - break; - } - } -} - -static void -makeRksTable (void) -{ - - s32 fnum8, block, KR; - - for (fnum8 = 0; fnum8 < 2; fnum8++) - for (block = 0; block < 8; block++) - for (KR = 0; KR < 2; KR++) - { - if (KR != 0) - rksTable[fnum8][block][KR] = (block << 1) + fnum8; - else - rksTable[fnum8][block][KR] = block >> 1; - } -} - -/************************************************************ - - Calc Parameters - -************************************************************/ - -__inline static u32 -calc_eg_dphase (OPLL_SLOT * slot) -{ - - switch (slot->eg_mode) - { - case ATTACK: - return dphaseARTable[slot->patch.AR][slot->rks]; - - case DECAY: - return dphaseDRTable[slot->patch.DR][slot->rks]; - - case SUSHOLD: - return 0; - - case SUSTINE: - return dphaseDRTable[slot->patch.RR][slot->rks]; - - case ERELEASE: - if (slot->sustine) - return dphaseDRTable[5][slot->rks]; - else if (slot->patch.EG) - return dphaseDRTable[slot->patch.RR][slot->rks]; - else - return dphaseDRTable[7][slot->rks]; - - case FINISH: - return 0; - - default: - return 0; - } -} - -/************************************************************* - - OPLL internal interfaces - -*************************************************************/ - -#define UPDATE_PG(S) (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch.ML] -#define UPDATE_TLL(S)\ -(((S)->type==0)?\ -((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->patch.TL][(S)->patch.KL]):\ -((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->volume][(S)->patch.KL])) -#define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum)>>8][(S)->block][(S)->patch.KR] -#define UPDATE_WF(S) (S)->sintbl = waveform[(S)->patch.WF] -#define UPDATE_EG(S) (S)->eg_dphase = calc_eg_dphase(S) -#define UPDATE_ALL(S)\ - UPDATE_PG(S);\ - UPDATE_TLL(S);\ - UPDATE_RKS(S);\ - UPDATE_WF(S); \ - UPDATE_EG(S) /* EG should be updated last. */ - - -/* Slot key on */ -__inline static void -slotOn (OPLL_SLOT * slot) -{ - slot->eg_mode = ATTACK; - slot->eg_phase = 0; - slot->phase = 0; -} - -/* Slot key on without reseting the phase */ -__inline static void -slotOn2 (OPLL_SLOT * slot) -{ - slot->eg_mode = ATTACK; - slot->eg_phase = 0; -} - -/* Slot key off */ -__inline static void -slotOff (OPLL_SLOT * slot) -{ - if (slot->eg_mode == ATTACK) - slot->eg_phase = EXPAND_BITS (AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS); - slot->eg_mode = ERELEASE; -} - -/* Channel key on */ -__inline static void -keyOn (OPLL * opll, s32 i) -{ - if (!opll->slot_on_flag[i * 2]) - slotOn (MOD(opll,i)); - if (!opll->slot_on_flag[i * 2 + 1]) - slotOn (CAR(opll,i)); - opll->key_status[i] = 1; -} - -/* Channel key off */ -__inline static void -keyOff (OPLL * opll, s32 i) -{ - if (opll->slot_on_flag[i * 2 + 1]) - slotOff (CAR(opll,i)); - opll->key_status[i] = 0; -} - -/* Set sustine parameter */ -__inline static void -setSustine (OPLL * opll, s32 c, s32 sustine) -{ - CAR(opll,c)->sustine = sustine; - if (MOD(opll,c)->type) - MOD(opll,c)->sustine = sustine; -} - -/* Volume : 6bit ( Volume register << 2 ) */ -__inline static void -setVolume (OPLL * opll, s32 c, s32 volume) -{ - CAR(opll,c)->volume = volume; -} - -__inline static void -setSlotVolume (OPLL_SLOT * slot, s32 volume) -{ - slot->volume = volume; -} - -/* Set F-Number ( fnum : 9bit ) */ -__inline static void -setFnumber (OPLL * opll, s32 c, s32 fnum) -{ - CAR(opll,c)->fnum = fnum; - MOD(opll,c)->fnum = fnum; -} - -/* Set Block data (block : 3bit ) */ -__inline static void -setBlock (OPLL * opll, s32 c, s32 block) -{ - CAR(opll,c)->block = block; - MOD(opll,c)->block = block; -} - -__inline static void update_key_status (OPLL * opll) -{ - int ch; - - for (ch = 0; ch < 6; ch++) - opll->slot_on_flag[ch * 2] = opll->slot_on_flag[ch * 2 + 1] = (opll->HiFreq[ch]) & 0x10; -} - -/*********************************************************** - - Initializing - -***********************************************************/ - -static void -OPLL_SLOT_reset (OPLL_SLOT * slot, int type) -{ - slot->type = type; - slot->sintbl = waveform[0]; - slot->phase = 0; - slot->dphase = 0; - slot->output[0] = 0; - slot->output[1] = 0; - slot->feedback = 0; - slot->eg_mode = SETTLE; - slot->eg_phase = EG_DP_WIDTH; - slot->eg_dphase = 0; - slot->rks = 0; - slot->tll = 0; - slot->sustine = 0; - slot->fnum = 0; - slot->block = 0; - slot->volume = 0; - slot->pgout = 0; - slot->egout = 0; -} - -static void -internal_refresh (void) -{ - makeDphaseTable (); - makeDphaseARTable (); - makeDphaseDRTable (); - pm_dphase = (u32) rate_adjust (PM_SPEED * PM_DP_WIDTH / (clk / 72)); - am_dphase = (u32) rate_adjust (AM_SPEED * AM_DP_WIDTH / (clk / 72)); -} - -static void -maketables (u32 c, u32 r) -{ - if (c != clk) - { - clk = c; - makePmTable (); - makeAmTable (); - makeDB2LinTable (); - makeAdjustTable (); - makeTllTable (); - makeRksTable (); - makeSinTable (); - //makeDefaultPatch (); - } - - if (r != rate) - { - rate = r; - internal_refresh (); - } -} - -OPLL *OPLL_new (u32 clk2, u32 rate2) -{ - OPLL *opll; - - maketables (clk2, rate2); - - opll = (OPLL *) calloc (sizeof (OPLL), 1); - if (opll == NULL) - return NULL; - - opll->mask = 0; - - OPLL_reset (opll); - - return opll; -} - - -void -OPLL_delete (OPLL * opll) -{ - free (opll); -} - -/* Reset whole of OPLL except patch datas. */ -void -OPLL_reset (OPLL * opll) -{ - s32 i; - - if (!opll) - return; - - opll->adr = 0; - opll->out = 0; - - opll->pm_phase = 0; - opll->am_phase = 0; - - opll->mask = 0; - - for (i = 0; i < 12; i++) - OPLL_SLOT_reset(&opll->slot[i], i%2); - - for (i = 0; i < 6; i++) - { - opll->key_status[i] = 0; - //setPatch (opll, i, 0); - } - - for (i = 0; i < 0x40; i++) - OPLL_writeReg (opll, i, 0); - -#ifndef EMU2413_COMPACTION - opll->realstep = (u32) ((1 << 31) / rate); - opll->opllstep = (u32) ((1 << 31) / (clk / 72)); - opll->oplltime = 0; -#endif -} - -/* Force Refresh (When external program changes some parameters). */ -void -OPLL_forceRefresh (OPLL * opll) -{ - s32 i; - - if (opll == NULL) - return; - - for (i = 0; i < 12; i++) - { - UPDATE_PG (&opll->slot[i]); - UPDATE_RKS (&opll->slot[i]); - UPDATE_TLL (&opll->slot[i]); - UPDATE_WF (&opll->slot[i]); - UPDATE_EG (&opll->slot[i]); - } -} - -void -OPLL_set_rate (OPLL * opll, u32 r) -{ - if (opll->quality) - rate = 49716; - else - rate = r; - internal_refresh (); - rate = r; -} - -void -OPLL_set_quality (OPLL * opll, u32 q) -{ - opll->quality = q; - OPLL_set_rate (opll, rate); -} - -/********************************************************* - - Generate wave data - -*********************************************************/ -/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */ -#if ( SLOT_AMP_BITS - PG_BITS ) > 0 -#define wave2_2pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS )) -#else -#define wave2_2pi(e) ( (e) << ( PG_BITS - SLOT_AMP_BITS )) -#endif - -/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */ -#if ( SLOT_AMP_BITS - PG_BITS - 1 ) == 0 -#define wave2_4pi(e) (e) -#elif ( SLOT_AMP_BITS - PG_BITS - 1 ) > 0 -#define wave2_4pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 1 )) -#else -#define wave2_4pi(e) ( (e) << ( 1 + PG_BITS - SLOT_AMP_BITS )) -#endif - -/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */ -#if ( SLOT_AMP_BITS - PG_BITS - 2 ) == 0 -#define wave2_8pi(e) (e) -#elif ( SLOT_AMP_BITS - PG_BITS - 2 ) > 0 -#define wave2_8pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 2 )) -#else -#define wave2_8pi(e) ( (e) << ( 2 + PG_BITS - SLOT_AMP_BITS )) -#endif - - - -/* Update AM, PM unit */ -static void -update_ampm (OPLL * opll) -{ - opll->pm_phase = (opll->pm_phase + pm_dphase) & (PM_DP_WIDTH - 1); - opll->am_phase = (opll->am_phase + am_dphase) & (AM_DP_WIDTH - 1); - opll->lfo_am = amtable[HIGHBITS (opll->am_phase, AM_DP_BITS - AM_PG_BITS)]; - opll->lfo_pm = pmtable[HIGHBITS (opll->pm_phase, PM_DP_BITS - PM_PG_BITS)]; -} - -/* PG */ -__inline static void -calc_phase (OPLL_SLOT * slot, s32 lfo) -{ - if (slot->patch.PM) - slot->phase += (slot->dphase * lfo) >> PM_AMP_BITS; - else - slot->phase += slot->dphase; - - slot->phase &= (DP_WIDTH - 1); - - slot->pgout = HIGHBITS (slot->phase, DP_BASE_BITS); -} - -/* EG */ -static void -calc_envelope (OPLL_SLOT * slot, s32 lfo) -{ -#define S2E(x) (SL2EG((s32)(x/SL_STEP))<<(EG_DP_BITS-EG_BITS)) - - static u32 SL[16] = { - S2E (0.0), S2E (3.0), S2E (6.0), S2E (9.0), S2E (12.0), S2E (15.0), S2E (18.0), S2E (21.0), - S2E (24.0), S2E (27.0), S2E (30.0), S2E (33.0), S2E (36.0), S2E (39.0), S2E (42.0), S2E (48.0) - }; - - u32 egout; - - switch (slot->eg_mode) - { - - case ATTACK: - egout = AR_ADJUST_TABLE[HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS)]; - slot->eg_phase += slot->eg_dphase; - if((EG_DP_WIDTH & slot->eg_phase)||(slot->patch.AR==15)) - { - egout = 0; - slot->eg_phase = 0; - slot->eg_mode = DECAY; - UPDATE_EG (slot); - } - break; - - case DECAY: - egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); - slot->eg_phase += slot->eg_dphase; - if (slot->eg_phase >= SL[slot->patch.SL]) - { - if (slot->patch.EG) - { - slot->eg_phase = SL[slot->patch.SL]; - slot->eg_mode = SUSHOLD; - UPDATE_EG (slot); - } - else - { - slot->eg_phase = SL[slot->patch.SL]; - slot->eg_mode = SUSTINE; - UPDATE_EG (slot); - } - } - break; - - case SUSHOLD: - egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); - if (slot->patch.EG == 0) - { - slot->eg_mode = SUSTINE; - UPDATE_EG (slot); - } - break; - - case SUSTINE: - case ERELEASE: - egout = HIGHBITS (slot->eg_phase, EG_DP_BITS - EG_BITS); - slot->eg_phase += slot->eg_dphase; - if (egout >= (1 << EG_BITS)) - { - slot->eg_mode = FINISH; - egout = (1 << EG_BITS) - 1; - } - break; - - case FINISH: - egout = (1 << EG_BITS) - 1; - break; - - default: - egout = (1 << EG_BITS) - 1; - break; - } - - if (slot->patch.AM) - egout = EG2DB (egout + slot->tll) + lfo; - else - egout = EG2DB (egout + slot->tll); - - if (egout >= DB_MUTE) - egout = DB_MUTE - 1; - - slot->egout = egout; -} - -/* CARRIOR */ -__inline static s32 -calc_slot_car (OPLL_SLOT * slot, s32 fm) -{ - slot->output[1] = slot->output[0]; - - if (slot->egout >= (DB_MUTE - 1)) - { - slot->output[0] = 0; - } - else - { - slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout]; - } - - return (slot->output[1] + slot->output[0]) >> 1; -} - -/* MODULATOR */ -__inline static s32 -calc_slot_mod (OPLL_SLOT * slot) -{ - s32 fm; - - slot->output[1] = slot->output[0]; - - if (slot->egout >= (DB_MUTE - 1)) - { - slot->output[0] = 0; - } - else if (slot->patch.FB != 0) - { - fm = wave2_4pi (slot->feedback) >> (7 - slot->patch.FB); - slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout + fm)&(PG_WIDTH-1)] + slot->egout]; - } - else - { - slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; - } - - slot->feedback = (slot->output[1] + slot->output[0]) >> 1; - - return slot->feedback; - -} - -static __inline s16 calc (OPLL * opll) -{ - s32 inst = 0, out = 0; - s32 i; - - update_ampm (opll); - - for (i = 0; i < 12; i++) - { - calc_phase(&opll->slot[i],opll->lfo_pm); - calc_envelope(&opll->slot[i],opll->lfo_am); - } - - for (i = 0; i < 6; i++) - if (!(opll->mask & OPLL_MASK_CH (i)) && (CAR(opll,i)->eg_mode != FINISH)) - inst += calc_slot_car (CAR(opll,i), calc_slot_mod(MOD(opll,i))); - - out = inst; - return (s16) out; -} - -#ifdef EMU2413_COMPACTION -s16 -OPLL_calc (OPLL * opll) -{ - return calc (opll); -} -#else -s16 -OPLL_calc (OPLL * opll) -{ - if (!opll->quality) - return calc (opll); - - while (opll->realstep > opll->oplltime) - { - opll->oplltime += opll->opllstep; - opll->prev = opll->next; - opll->next = calc (opll); - } - - opll->oplltime -= opll->realstep; - opll->out = (s16) (((double) opll->next * (opll->opllstep - opll->oplltime) - + (double) opll->prev * opll->oplltime) / opll->opllstep); - - return (s16) opll->out; -} -#endif - -u32 -OPLL_setMask (OPLL * opll, u32 mask) -{ - u32 ret; - - if (opll) - { - ret = opll->mask; - opll->mask = mask; - return ret; - } - else - return 0; -} - -u32 -OPLL_toggleMask (OPLL * opll, u32 mask) -{ - u32 ret; - - if (opll) - { - ret = opll->mask; - opll->mask ^= mask; - return ret; - } - else - return 0; -} - -/**************************************************** - - I/O Ctrl - -*****************************************************/ - -static void setInstrument(OPLL * opll, unsigned int i, unsigned int inst) -{ - const u8 *src; - OPLL_PATCH *modp, *carp; - - opll->patch_number[i]=inst; - - if(inst) - src=default_inst[inst-1]; - else - src=opll->CustInst; - - modp=&MOD(opll,i)->patch; - carp=&CAR(opll,i)->patch; - - modp->AM=(src[0]>>7)&1; - modp->PM=(src[0]>>6)&1; - modp->EG=(src[0]>>5)&1; - modp->KR=(src[0]>>4)&1; - modp->ML=(src[0]&0xF); - - carp->AM=(src[1]>>7)&1; - carp->PM=(src[1]>>6)&1; - carp->EG=(src[1]>>5)&1; - carp->KR=(src[1]>>4)&1; - carp->ML=(src[1]&0xF); - - modp->KL=(src[2]>>6)&3; - modp->TL=(src[2]&0x3F); - - carp->KL = (src[3] >> 6) & 3; - carp->WF = (src[3] >> 4) & 1; - - modp->WF = (src[3] >> 3) & 1; - - modp->FB = (src[3]) & 7; - - modp->AR = (src[4]>>4)&0xF; - modp->DR = (src[4]&0xF); - - carp->AR = (src[5]>>4)&0xF; - carp->DR = (src[5]&0xF); - - modp->SL = (src[6]>>4)&0xF; - modp->RR = (src[6]&0xF); - - carp->SL = (src[7]>>4)&0xF; - carp->RR = (src[7]&0xF); -} - - -void -OPLL_writeReg (OPLL * opll, u32 reg, u32 data) -{ - - s32 i, v, ch; - - data = data & 0xff; - reg = reg & 0x3f; - - switch (reg) - { - case 0x00: - opll->CustInst[0]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_PG (MOD(opll,i)); - UPDATE_RKS (MOD(opll,i)); - UPDATE_EG (MOD(opll,i)); - } - } - break; - - case 0x01: - opll->CustInst[1]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_PG (CAR(opll,i)); - UPDATE_RKS (CAR(opll,i)); - UPDATE_EG (CAR(opll,i)); - } - } - break; - - case 0x02: - opll->CustInst[2]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_TLL(MOD(opll,i)); - } - } - break; - - case 0x03: - opll->CustInst[3]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_WF(MOD(opll,i)); - UPDATE_WF(CAR(opll,i)); - } - } - break; - - case 0x04: - opll->CustInst[4]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_EG (MOD(opll,i)); - } - } - break; - - case 0x05: - opll->CustInst[5]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_EG(CAR(opll,i)); - } - } - break; - - case 0x06: - opll->CustInst[6]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_EG (MOD(opll,i)); - } - } - break; - - case 0x07: - opll->CustInst[7]=(u8)data; - for (i = 0; i < 6; i++) - { - if (opll->patch_number[i] == 0) - { - setInstrument(opll, i, 0); - UPDATE_EG (CAR(opll,i)); - } - } - break; - - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - ch = reg - 0x10; - opll->LowFreq[ch]=(u8)data; - setFnumber (opll, ch, data + ((opll->HiFreq[ch] & 1) << 8)); - UPDATE_ALL (MOD(opll,ch)); - UPDATE_ALL (CAR(opll,ch)); - break; - - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - ch = reg - 0x20; - opll->HiFreq[ch]=(u8)data; - - setFnumber (opll, ch, ((data & 1) << 8) + opll->LowFreq[ch]); - setBlock (opll, ch, (data >> 1) & 7); - setSustine (opll, ch, (data >> 5) & 1); - if (data & 0x10) - keyOn (opll, ch); - else - keyOff (opll, ch); - UPDATE_ALL (MOD(opll,ch)); - UPDATE_ALL (CAR(opll,ch)); - update_key_status (opll); - break; - - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - opll->InstVol[reg-0x30]=(u8)data; - i = (data >> 4) & 15; - v = data & 15; - setInstrument(opll, reg-0x30, i); - setVolume (opll, reg - 0x30, v << 2); - UPDATE_ALL (MOD(opll,reg - 0x30)); - UPDATE_ALL (CAR(opll,reg - 0x30)); - break; - - default: - break; - - } -} - -// Konami VRC7, based on the YM2413 - -OPLL *OPL = NULL; - -void VRC7sound_Load (void) -{ - if (OPL != NULL) - { -// MessageBox(hWnd,_T("YM2413 already created!"),_T("VRC7"),MSGBOX_FLAGS); - log_printf("YM2413 already created!\n"); - return; - } - else - { - OPL = OPLL_new(3579545,44100); - if (OPL == NULL) - { -// MessageBox(hWnd,_T("Unable to create YM2413!"),_T("VRC7"),MSGBOX_FLAGS); - log_printf("Unable to create YM2413!\n"); - return; - } - } -} - -void VRC7sound_Reset (void) -{ -} - -void VRC7sound_Unload (void) -{ - if (OPL) - { - OPLL_delete(OPL); - OPL = NULL; - } -// else MessageBox(hWnd,_T("Unable to destroy YM2413!"),_T("VRC7"),MSGBOX_FLAGS); -} - -void VRC7sound_Write (int Addr, int Val) -{ - static unsigned char addr = 0; - switch (Addr & 0xF030) - { - case 0x9010: addr = Val; break; - case 0x9030: OPLL_writeReg(OPL,addr,Val); break; - } -} - -int VRC7sound_Get (int numCycles) -{ - return OPLL_calc(OPL) << 3; // currently don't use numCycles -} - -int VRC7sound_SaveLoad (int mode, int x, unsigned char *data) -{ -/* if (mode == STATE_SAVE) - { - memcpy(data+x,OPL,sizeof(OPLL)); - x += sizeof(OPLL); - } - else if (mode == STATE_LOAD) - { - int i; - memcpy(OPL,data+x,sizeof(OPLL)); - x += sizeof(OPLL); - for (i = 0; i < 6; i++) - { - UPDATE_ALL(MOD(OPL,i)); - UPDATE_ALL(CAR(OPL,i)); - } - } - else if (mode == STATE_SIZE) - x += sizeof(OPLL); - else MessageBox(hWnd,_T("Invalid save/load type!"),_T(__FILE__),MB_OK); -*/ return x; -} -#endif void VRC7sound_Load (void) { } void VRC7sound_Reset (void) { } diff --git a/apps/nesemu2/src/misc/config.c b/apps/nesemu2/src/misc/config.c index 81c745c9..370e38fa 100644 --- a/apps/nesemu2/src/misc/config.c +++ b/apps/nesemu2/src/misc/config.c @@ -24,118 +24,18 @@ #include "misc/config.h" #include "misc/vars.h" #include "misc/memutil.h" -#include "misc/log.h" -#include "misc/paths.h" #include "system/main.h" #include "system/system.h" #include "version.h" static vars_t *configvars = 0; - -#if 0 -//this path stuff needs to be moved -static void mkdirr(char *path) -{ - char *tmp = mem_strdup(path); - char *p = tmp; - int num = 0; - - //normalize the path string - paths_normalize(tmp); - - log_printf("mkdirr: creating directory '%s'\n",path); - - //make all the directories between the root and the desired directory - for(num=0,p=tmp;(p = strchr(p,PATH_SEPERATOR));num++) { - if(num == 0) { - p++; - continue; - } - *p = 0; - mkdir(tmp); - chmod(tmp,0755); - *p = PATH_SEPERATOR; - p++; - } - - //now create the directory we want - mkdir(path); - chmod(path,0755); - - //free tmp string - mem_free(tmp); -} - -static void makepath(char *str) -{ - //test if the path exists already, if not create it - if(access(str,0) != 0) - mkdirr(str); -} - -//this function looks around for a configuration file. it checks: -// 1. current working directory -// 2. $HOME directory -// 3. same directory the executable is in -//if it isnt found, it defaults the executable directory -static int findconfig(char *dest) -{ - char *cwd = system_getcwd(); - char *home = getenv("HOME"); - - //first see if the configfilename string isnt empty - if(strcmp(dest,"") != 0) { - return(0); - } - - //now look in the current working directory - sprintf(dest,"%s%c%s",cwd,PATH_SEPERATOR,CONFIG_FILENAME); - log_printf("looking for configuration at '%s'\n",dest); - if(access(dest,06) == 0) { - return(0); - } - - //check the users home directory - if(home) { - sprintf(dest,"%s%c.nesemu2%c%s",home,PATH_SEPERATOR,PATH_SEPERATOR,CONFIG_FILENAME); - log_printf("looking for configuration at '%s'\n",dest); - if(access(dest,06) == 0) { - return(0); - } - } - -#ifdef WIN32 - //win32 it is ok to store in the same directory as executable - //now check the executable directory - sprintf(dest,"%s%c%s",exepath,PATH_SEPERATOR,CONFIG_FILENAME); - log_printf("looking for configuration at '%s'\n",dest); - if(access(dest,06) == 0) { - return(0); - } - - //set default configuration filename - sprintf(dest,"%s%c%s",exepath,PATH_SEPERATOR,CONFIG_FILENAME); - -#else - //linux it is not ok to store in the same directory as executable (/usr/bin or something) - if(home) { - sprintf(dest,"%s%c.nesemu2%c%s",home,PATH_SEPERATOR,PATH_SEPERATOR,CONFIG_FILENAME); - } - else { - sprintf(dest,"%s%c%s",cwd,PATH_SEPERATOR,CONFIG_FILENAME); - log_printf("system_findconfig: HOME environment var not set! using current directory.\n"); - } -#endif - - return(1); -} -#endif +char configfilename[1024] = CONFIG_FILENAME; +char exepath[1024] = ""; //initialize the configuration defaults static vars_t *config_get_defaults() { vars_t *ret = vars_create(); - //char *str; vars_set_int (ret,F_CONFIG,"video.framelimit", 1); vars_set_int (ret,F_CONFIG,"video.fullscreen", 0); @@ -164,65 +64,12 @@ static vars_t *config_get_defaults() vars_set_int (ret,F_CONFIG,"input.joypad1.left", '7'); vars_set_int (ret,F_CONFIG,"input.joypad1.right", '8'); - vars_set_string(ret,F_CONFIG,"input.zapper.trigger", "mb0"); - - vars_set_int (ret,F_CONFIG,"sound.enabled", 1); - -#ifdef WIN32 - vars_set_string(ret,F_CONFIG,"path.data", "%exepath%/data"); - vars_set_string(ret,F_CONFIG,"path.user", "%path.data%"); -#else - vars_set_string(ret,F_CONFIG,"path.data", "/usr/share/nesemu2"); - vars_set_string(ret,F_CONFIG,"path.user", "%home%/.nesemu2"); -#endif - - vars_set_string(ret,F_CONFIG,"path.bios", "%path.data%/bios"); - vars_set_string(ret,F_CONFIG,"path.patch", "%path.data%/patch"); - vars_set_string(ret,F_CONFIG,"path.palette", "%path.data%/palette"); - vars_set_string(ret,F_CONFIG,"path.xml", "%path.data%/xml"); - - vars_set_string(ret,F_CONFIG,"path.save", "%path.user%/save"); - vars_set_string(ret,F_CONFIG,"path.state", "%path.user%/state"); - vars_set_string(ret,F_CONFIG,"path.cheat", "%path.user%/cheat"); - - vars_set_string(ret,F_CONFIG,"palette.source", "generator"); - vars_set_int (ret,F_CONFIG,"palette.hue", -15); - vars_set_int (ret,F_CONFIG,"palette.saturation", 45); - vars_set_string(ret,F_CONFIG,"palette.filename", "roni.pal"); - - vars_set_string(ret,F_CONFIG,"nes.gamegenie.bios", "genie.rom"); - vars_set_int (ret,F_CONFIG,"nes.gamegenie.enabled", 0); - - vars_set_string(ret,F_CONFIG,"nes.fds.bios", "hlefds.bin"); - vars_set_int (ret,F_CONFIG,"nes.fds.hle", 1); - - vars_set_string(ret,F_CONFIG,"nes.nsf.bios", "nsfbios.bin"); - - vars_set_string(ret,F_CONFIG,"nes.region", "ntsc"); - vars_set_int (ret,F_CONFIG,"nes.log_unhandled_io", 0); - vars_set_int (ret,F_CONFIG,"nes.pause_on_load", 0); - - vars_set_int (ret,F_CONFIG,"cartdb.enabled", 1); - vars_set_string(ret,F_CONFIG,"cartdb.filename", "%path.xml%/NesCarts.xml;%path.xml%/NesCarts2.xml"); - - vars_set_string(ret,0,"version",VERSION); - vars_set_string(ret,0,"exepath",exepath); - //if((str = getenv("HOME")) != 0) - // vars_set_string(ret,0,"home",str); - return(ret); } int config_init() { vars_t *v; - //char tmp[1024]; - - //find configuration file - //if(findconfig(configfilename) == 0) - // log_printf("main: found configuration at '%s'\n",configfilename); - //else - // log_printf("main: creating new configuration at '%s'\n",configfilename); configvars = config_get_defaults(); if((v = vars_load(configfilename)) == 0) { @@ -236,12 +83,6 @@ int config_init() vars_destroy(v); } - //make the directories - //makepath(config_get_eval_string(tmp,"path.user")); - //makepath(config_get_eval_string(tmp,"path.save")); - //makepath(config_get_eval_string(tmp,"path.state")); - //makepath(config_get_eval_string(tmp,"path.cheat")); - return(0); } @@ -328,9 +169,6 @@ char *config_get_eval_string(char *dest,char *name) dest[pos] = 0; } - //normalize the path - paths_normalize(dest); - //free tmp string and return mem_free(tmp); return(dest); diff --git a/apps/nesemu2/src/misc/crc32.h b/apps/nesemu2/src/misc/crc32.h index d199f12f..db0d0570 100644 --- a/apps/nesemu2/src/misc/crc32.h +++ b/apps/nesemu2/src/misc/crc32.h @@ -1,7 +1,7 @@ #ifndef __crc32_h__ #define __crc32_h__ -#include "types.h" +#include "common.h" void crc32_gentab(); u32 crc32(unsigned char *block,unsigned int length); diff --git a/apps/nesemu2/src/misc/history.c b/apps/nesemu2/src/misc/history.c deleted file mode 100644 index 8697043a..00000000 --- a/apps/nesemu2/src/misc/history.c +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include "misc/history.h" -#include "misc/memutil.h" -#include "misc/log.h" - -history_t *history_create() -{ - history_t *ret = (history_t*)mem_alloc(sizeof(history_t)); - - memset(ret,0,sizeof(history_t)); - return(ret); -} - -void history_destroy(history_t *h) -{ - history_clear(h); - mem_free(h); -} - -void history_add(history_t *h,char *str) -{ - historyline_t *p = (historyline_t*)mem_alloc(sizeof(historyline_t)); - - p->prev = 0; - p->next = h->lines; - p->str = mem_strdup(str); - if(h->lines == 0) - h->lines = p; - else - h->lines->prev = p; - h->lines = p; - h->cur = 0; -} - -void history_clear(history_t *h) -{ - historyline_t *p,*line = h->lines; - - while(line) { - p = line; - line = line->next; - mem_free(p->str); - mem_free(p); - } -} - -char *history_getcur(history_t *h) -{ - return(h->cur ? h->cur->str : 0); -} - -char *history_getnext(history_t *h) -{ - if(h->cur == 0) - h->cur = h->lines; - else { - if(h->cur->next) - h->cur = h->cur->next; - } - return(history_getcur(h)); -} - -char *history_getprev(history_t *h) -{ - if(h->cur) { - if(h->cur->prev) - h->cur = h->cur->prev; - } - return(history_getcur(h)); -} diff --git a/apps/nesemu2/src/misc/history.h b/apps/nesemu2/src/misc/history.h deleted file mode 100644 index 545cb908..00000000 --- a/apps/nesemu2/src/misc/history.h +++ /dev/null @@ -1,42 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __history_h__ -#define __history_h__ - -typedef struct historyline_s { - struct historyline_s *prev,*next; - char *str; -} historyline_t; - -typedef struct history_s { - historyline_t *lines; - historyline_t *cur; -} history_t; - -history_t *history_create(); -void history_destroy(history_t *h); -void history_add(history_t *h,char *str); -void history_clear(history_t *h); -char *history_getcur(history_t *h); -char *history_getnext(history_t *h); -char *history_getprev(history_t *h); - -#endif diff --git a/apps/nesemu2/src/misc/log.c b/apps/nesemu2/src/misc/log.c deleted file mode 100644 index 2dfd3f60..00000000 --- a/apps/nesemu2/src/misc/log.c +++ /dev/null @@ -1,142 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include -#include -#include -#include "misc/log.h" -#include "misc/config.h" -#include "misc/paths.h" -#include "misc/strutil.h" -#include "misc/history.h" -#include "system/system.h" -#include "version.h" - -#ifndef MAX_PATH - #define MAX_PATH 1024 -#endif - -#define LOGFILENAME "nesemu2.log" - -#if 0 -static void (*loghook)(char*) = 0; -static FILE *logfd = 0; -static char logfilename[MAX_PATH] = ""; -static history_t history = {0,0}; - -int log_init() -{ - historyline_t *line; - - if(logfd) { - printf("log_init: already initialized\n"); - return(0); - } - - //clear the string - memset(logfilename,0,MAX_PATH); - - //parse the log path - config_get_eval_string(logfilename,"path.user"); - - //append the path seperator - str_appendchar(logfilename,PATH_SEPERATOR); - - //append the bios filename - strcat(logfilename,LOGFILENAME); - - //try to open - if((logfd = fopen(logfilename,"wt")) == 0) { - strcpy(logfilename,LOGFILENAME); - if((logfd = fopen(LOGFILENAME,"wt")) == 0) { - printf("log_init: error opening log file '%s'\n",logfilename); - return(1); - } - } - - //catch up the log file with lines that weren't added - line = history.lines; - while(line->next) { - line = line->next; - } - while(line) { - fputs(line->str,logfd); - line = line->prev; - } - fflush(logfd); - history_clear(&history); - - log_printf("log_init: log initialized. nesemu2 v"VERSION"\n"); - log_printf("log_init: log filename is '%s'.\n",logfilename); - return(0); -} - -void log_kill() -{ - if(logfd) - fclose(logfd); - logfd = 0; -} -#endif - -int log_init() { return 0; } -void log_kill() { } - -void log_sethook(void (*hook)(char*)) -{ - //loghook = hook; -} - -void log_print(char *str) -{ - //output message to stdout - printf("%s",str); - -#if 0 - //output message to hook function - if(loghook) { - loghook(str); - } - - //if log file isnt open, maybe logging is disabled or file isnt opened yet - if(logfd == 0) { - history_add(&history,str); - return; - } - - //write to log file - fputs(str,logfd); - - //flush file - fflush(logfd); -#endif -} - -void log_printf(char *str,...) -{ - char buffer[1024]; //buffer to store vsprintf'd message in - va_list argptr; //argument data - - va_start(argptr,str); //get data - vsprintf(buffer,str,argptr); //print to buffer - va_end(argptr); //done! - log_print(buffer); -} diff --git a/apps/nesemu2/src/misc/log.h b/apps/nesemu2/src/misc/log.h deleted file mode 100644 index 5d6c2f9f..00000000 --- a/apps/nesemu2/src/misc/log.h +++ /dev/null @@ -1,30 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __log_h__ -#define __log_h__ - -int log_init(); -void log_kill(); -void log_sethook(void (*hook)(char*)); -void log_print(char *str); -void log_printf(char *str,...); - -#endif diff --git a/apps/nesemu2/src/misc/memfile.c b/apps/nesemu2/src/misc/memfile.c index 4c570bdb..cc9fbb81 100644 --- a/apps/nesemu2/src/misc/memfile.c +++ b/apps/nesemu2/src/misc/memfile.c @@ -22,7 +22,6 @@ #include #include "misc/memfile.h" #include "misc/memutil.h" -#include "misc/log.h" #include memfile_t *memfile_create() diff --git a/apps/nesemu2/src/misc/memfile.h b/apps/nesemu2/src/misc/memfile.h index 27cf3b03..f634c7eb 100644 --- a/apps/nesemu2/src/misc/memfile.h +++ b/apps/nesemu2/src/misc/memfile.h @@ -21,7 +21,7 @@ #ifndef __memfile_h__ #define __memfile_h__ -#include "types.h" +#include "common.h" typedef struct memfile_s { diff --git a/apps/nesemu2/src/misc/memutil.c b/apps/nesemu2/src/misc/memutil.c index 45c0e4da..77fd5edc 100644 --- a/apps/nesemu2/src/misc/memutil.c +++ b/apps/nesemu2/src/misc/memutil.c @@ -18,11 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include +#include "common.h" #include "misc/memutil.h" -#include "misc/log.h" -#include #define MAX_CHUNKS 1024 @@ -166,28 +163,6 @@ void *memutil_realloc(void *ptr,size_t size,char *file,int line) if(ptr == 0) return(memutil_alloc(size,file,line)); assert(0); -#if 0 - void *ret = realloc(ptr,size); - int i; - num_realloc++; - for(i=0;i chunks[i].size) - num_bytes += size - chunks[i].size; - chunks[i].size = size; - break; - } - } - if(i == MAX_CHUNKS) { - log_printf("memutil_realloc: trying to realloc memory not found in list in file %s @ line %d\n",file,line); - } - memutil_count(); - return(ret); -#endif } void memutil_free(void *ptr,char *file,int line) diff --git a/apps/nesemu2/src/misc/paths.c b/apps/nesemu2/src/misc/paths.c deleted file mode 100644 index 9aebb161..00000000 --- a/apps/nesemu2/src/misc/paths.c +++ /dev/null @@ -1,76 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include -#include "misc/paths.h" -#include "misc/memutil.h" -#include "misc/strutil.h" -#include "misc/config.h" -#include "misc/log.h" -#include "emu/emu.h" -#include "system/main.h" - -char *paths_normalize(char *str) -{ - char *p; - - for(p=str;*p;p++) { - if(*p == '/' || *p == '\\') - *p = PATH_SEPERATOR; - } - return(str); -} - -#if 0 -void paths_makestatefilename(char *romfilename,char *dest,int len) -{ - char *p,*tmp = mem_strdup(romfilename); - - //clear the string - memset(dest,0,len); - - //parse the state path - config_get_eval_string(dest,"path.state"); - - //append the path seperator - str_appendchar(dest,PATH_SEPERATOR); - - //normalize the path seperators - paths_normalize(tmp); - - //find the last path seperator - p = strrchr(tmp,PATH_SEPERATOR); - - //if not found then it is plain filename - p = (p == 0) ? tmp : p + 1; - - //append the rom filename - strcat(dest,p); - - //append the state extension) - strcat(dest,".state"); - - //free the temporary string - mem_free(tmp); -} -#endif - -void paths_makestatefilename(char *romfilename,char *dest,int len) { } diff --git a/apps/nesemu2/src/misc/paths.h b/apps/nesemu2/src/misc/paths.h deleted file mode 100644 index c31698b2..00000000 --- a/apps/nesemu2/src/misc/paths.h +++ /dev/null @@ -1,33 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __paths_h__ -#define __paths_h__ - -#ifdef WIN32 - #define PATH_SEPERATOR '\\' -#else - #define PATH_SEPERATOR '/' -#endif - -char *paths_normalize(char *str); -void paths_makestatefilename(char *romfilename,char *dest,int len); - -#endif diff --git a/apps/nesemu2/src/misc/vars.c b/apps/nesemu2/src/misc/vars.c index d67d52b8..d5aa23dd 100644 --- a/apps/nesemu2/src/misc/vars.c +++ b/apps/nesemu2/src/misc/vars.c @@ -30,7 +30,6 @@ vars_add_var is able to add another var with the same name #include "misc/memutil.h" #include "misc/strutil.h" #include "misc/vars.h" -#include "misc/log.h" #include vars_t *vars_create() @@ -48,75 +47,6 @@ void vars_destroy(vars_t *vs) mem_free(vs); } -#if 0 -vars_t *vars_load(char *filename) -{ - vars_t *ret = 0; - FILE *fp; - char line[1024],*p,*oldp; - - if((fp = fopen(filename,"rt")) == 0) { - log_printf("vars_load: error opening file '%s'\n",filename); - return(0); - } - - ret = vars_create(); - while(feof(fp) == 0) { - - //read line from file - if(fgets(line,1024,fp) == NULL) - break; - - //skip past any whitespace - p = str_eatwhitespace(line); - - //comment or empty string, do nothing - if(*p == '#' || *p == 0) - continue; - - //find where to split the string - if((oldp = strchr(p,'=')) == 0) { - log_printf("vars_load: malformed line ('%s')\n",p); - continue; - } - - //parse out the name/data pair - *oldp++ = 0; - p = str_eatwhitespace(p); - oldp = str_eatwhitespace(oldp); - - //add the var to the list - vars_add_var(ret,F_CONFIG,p,oldp); - } - - log_printf("vars_load: loaded file '%s'\n",filename); - fclose(fp); - strcpy(ret->filename,filename); - return(ret); -} - -int vars_save(vars_t *vs,char *filename) -{ - FILE *fp; - var_t *v = vs->vars; - - if(filename == 0) - filename = vs->filename; - if((fp = fopen(filename,"wt")) == 0) { - log_printf("vars_save: error opening '%s'\n",filename); - return(1); - } - while(v) { - if(v->flags == F_CONFIG) - fprintf(fp,"%s = %s\n",v->name,v->data); - v = v->next; - } - fclose(fp); - vs->changed = 0; - return(0); -} -#endif - vars_t *vars_load(char *filename) { return NULL; } int vars_save(vars_t *vs,char *filename) { return 1; } @@ -226,10 +156,6 @@ int vars_get_bool(vars_t *vs,char *name,int def) double vars_get_double(vars_t *vs,char *name,double def) { -#if 0 - sprintf(tmpstr,"%f",def); - return(atof(vars_get_string(vs,name,tmpstr))); -#endif return 0; } diff --git a/apps/nesemu2/src/nes/apu/apu.c b/apps/nesemu2/src/nes/apu/apu.c index 9125d987..0f020a16 100644 --- a/apps/nesemu2/src/nes/apu/apu.c +++ b/apps/nesemu2/src/nes/apu/apu.c @@ -22,7 +22,6 @@ #include "nes/nes.h" #include "nes/state/state.h" -#include "misc/log.h" #include "misc/memutil.h" #include "system/sound.h" diff --git a/apps/nesemu2/src/nes/cart/cart.c b/apps/nesemu2/src/nes/cart/cart.c index abd3da89..2df16f81 100644 --- a/apps/nesemu2/src/nes/cart/cart.c +++ b/apps/nesemu2/src/nes/cart/cart.c @@ -22,8 +22,6 @@ #include #include #include "misc/memutil.h" -#include "misc/log.h" -#include "misc/paths.h" #include "misc/crc32.h" #include "misc/memfile.h" #include "nes/cart/cart.h" @@ -229,20 +227,7 @@ cart_t *cart_load_patched(const char *filename,const char *patchfilename) generate_mask_and_crc32(ret->pc10rom); //store the filename - if(ret->patch) { - char *p; - - paths_normalize(ret->patch->filename); - p = strrchr(ret->patch->filename,PATH_SEPERATOR); - if(p == 0) - p = ret->patch->filename; - else - p++; - ret->filename = (char*)mem_alloc(strlen(filename) + strlen(p) + 2); - sprintf(ret->filename,"%s+%s",filename,p); - } - else - ret->filename = mem_strdup((char*)filename); + ret->filename = mem_strdup((char*)filename); log_printf("cart_load: rom filename is '%s'\n",ret->filename); diff --git a/apps/nesemu2/src/nes/cart/cart.h b/apps/nesemu2/src/nes/cart/cart.h index c2366758..76f99336 100644 --- a/apps/nesemu2/src/nes/cart/cart.h +++ b/apps/nesemu2/src/nes/cart/cart.h @@ -21,7 +21,7 @@ #ifndef __nes__cart_h__ #define __nes__cart_h__ -#include "types.h" +#include "common.h" #include "misc/memfile.h" #include "nes/ppu/tilecache.h" #include "nes/cart/patch/patch.h" diff --git a/apps/nesemu2/src/nes/cart/doctor.c b/apps/nesemu2/src/nes/cart/doctor.c index 994743f1..c559839e 100644 --- a/apps/nesemu2/src/nes/cart/doctor.c +++ b/apps/nesemu2/src/nes/cart/doctor.c @@ -22,9 +22,7 @@ #include #include "misc/memutil.h" #include "misc/strutil.h" -#include "misc/log.h" #include "misc/config.h" -#include "misc/paths.h" #include "nes/cart/cart.h" #include "mappers/mapperid.h" @@ -63,39 +61,6 @@ static int loadbios_new(char *filename, u8 **data, int *size) return(n); } -#if 0 -static int loadbios(cart_t *ret, char *filename) -{ - memfile_t *file; - int n = 0; - - //open bios file - if ((file = memfile_open(filename, "rb")) == 0) { - log_printf("loadbios: error opening fds bios '%s'\n", filename); - return(1); - } - - //setup prg for bios - ret->prg.size = 0x4000; - ret->prg.mask = 0x3FFF; - ret->prg.data = (u8*)mem_alloc(ret->prg.size); - - //read bios - if (memfile_read(ret->prg.data, 1, 0x2000, file) != 0x2000) { - log_printf("loadbios: error reading bios file '%s'\n", filename); - n = 1; - } - else - log_printf("loadbios: loaded bios file '%s'\n", filename); - - //close bios file handle - memfile_close(file); - - //return - return(n); -} -#endif - void copy_block(u8 *dst, u8 *src, int size) { memcpy(dst, src, size); } diff --git a/apps/nesemu2/src/nes/cart/fds.c b/apps/nesemu2/src/nes/cart/fds.c index 3cc3d54d..90ff6623 100644 --- a/apps/nesemu2/src/nes/cart/fds.c +++ b/apps/nesemu2/src/nes/cart/fds.c @@ -22,9 +22,7 @@ #include #include "misc/memutil.h" #include "misc/strutil.h" -#include "misc/log.h" #include "misc/config.h" -#include "misc/paths.h" #include "nes/cart/cart.h" #include "mappers/mapperid.h" diff --git a/apps/nesemu2/src/nes/cart/ines.c b/apps/nesemu2/src/nes/cart/ines.c index c4ccac7b..4a58b5d7 100644 --- a/apps/nesemu2/src/nes/cart/ines.c +++ b/apps/nesemu2/src/nes/cart/ines.c @@ -21,7 +21,6 @@ #include #include #include "misc/memutil.h" -#include "misc/log.h" #include "nes/cart/cart.h" #include "mappers/mappers.h" diff --git a/apps/nesemu2/src/nes/cart/ines20.c b/apps/nesemu2/src/nes/cart/ines20.c index 74b0917c..8dd36987 100644 --- a/apps/nesemu2/src/nes/cart/ines20.c +++ b/apps/nesemu2/src/nes/cart/ines20.c @@ -22,7 +22,6 @@ #include #include #include "misc/memutil.h" -#include "misc/log.h" #include "nes/cart/cart.h" #include "mappers/mappers.h" diff --git a/apps/nesemu2/src/nes/cart/nsf.c b/apps/nesemu2/src/nes/cart/nsf.c index 874f12b4..3ec54cb5 100644 --- a/apps/nesemu2/src/nes/cart/nsf.c +++ b/apps/nesemu2/src/nes/cart/nsf.c @@ -21,69 +21,11 @@ #include #include "nes/cart/cart.h" #include "nes/cart/nsf.h" -#include "misc/log.h" -#include "misc/paths.h" #include "misc/config.h" #include "misc/memutil.h" #include "misc/strutil.h" #include "mappers/mapperid.h" -#if 0 -static int loadbios(cart_t *ret,char *filename) -{ - FILE *fp; - size_t size; - u8 header[10]; - - //open bios file - if((fp = fopen(filename,"rb")) == 0) { - log_printf("loadbios: error opening nsf bios '%s'\n",filename); - return(1); - } - - //get size of the nsf bios - fseek(fp,0,SEEK_END); - size = ftell(fp); - fseek(fp,0,SEEK_SET); - - //bios has a 10 byte header (actually part of the first bank) - // addr size description - // ---- ---- ----------- - // 00 07 ident string (NSFBIOS) - // 07 01 version hi - // 08 01 version lo - // 09 01 - fread(header,1,10,fp); - - if(memcmp(header,"NSFBIOS",7) != 0) { - log_printf("loadbios: nsf bios ident is bad\n"); - fclose(fp); - return(1); - } - - //seek back to the beginning - fseek(fp,0,SEEK_SET); - - //load bios into upper wram - cart_setwramsize(ret,8 + (size / 1024)); - - //read bios - if(fread(ret->wram.data + 8192,1,size,fp) != size) { - log_printf("loadbios: error reading nsf bios file '%s'\n",filename); - mem_free(ret->wram.data); - fclose(fp); - return(1); - } - - //close bios file handle - fclose(fp); - log_printf("loadbios: nsf bios loaded, %d bytes. version %d.%d\n",size,header[7],header[8]); - - //success - return(0); -} -#endif - static int loadbios(cart_t *ret,char *filename) { return -1; } //makes the size a multiple of 4096 for padding diff --git a/apps/nesemu2/src/nes/cart/patch/ips.c b/apps/nesemu2/src/nes/cart/patch/ips.c index 5aeff441..e7a543ce 100644 --- a/apps/nesemu2/src/nes/cart/patch/ips.c +++ b/apps/nesemu2/src/nes/cart/patch/ips.c @@ -21,7 +21,6 @@ #include #include "nes/cart/patch/patch.h" #include "misc/memutil.h" -#include "misc/log.h" int patch_apply_ips(patch_t *p,memfile_t *file) { diff --git a/apps/nesemu2/src/nes/cart/patch/patch.c b/apps/nesemu2/src/nes/cart/patch/patch.c index 2f611648..0cbfa13c 100644 --- a/apps/nesemu2/src/nes/cart/patch/patch.c +++ b/apps/nesemu2/src/nes/cart/patch/patch.c @@ -29,7 +29,6 @@ #include "nes/cart/patch/ips.h" #include "nes/cart/patch/ups.h" #include "misc/memutil.h" -#include "misc/log.h" enum patchformat_e { FORMAT_ERROR = -1, diff --git a/apps/nesemu2/src/nes/cart/patch/ups.c b/apps/nesemu2/src/nes/cart/patch/ups.c index 40b8e244..9acccac6 100644 --- a/apps/nesemu2/src/nes/cart/patch/ups.c +++ b/apps/nesemu2/src/nes/cart/patch/ups.c @@ -19,7 +19,6 @@ ***************************************************************************/ #include "nes/cart/patch/patch.h" -#include "misc/log.h" int patch_apply_ups(patch_t *ret,memfile_t *file) { diff --git a/apps/nesemu2/src/nes/cart/unif.c b/apps/nesemu2/src/nes/cart/unif.c index c2006979..1aca8724 100644 --- a/apps/nesemu2/src/nes/cart/unif.c +++ b/apps/nesemu2/src/nes/cart/unif.c @@ -21,7 +21,6 @@ #include #include #include "misc/memutil.h" -#include "misc/log.h" #include "misc/crc32.h" #include "nes/cart/cart.h" #include "nes/cart/unif.h" diff --git a/apps/nesemu2/src/nes/cpu/cpu.c b/apps/nesemu2/src/nes/cpu/cpu.c index c53af8a8..d0599d98 100644 --- a/apps/nesemu2/src/nes/cpu/cpu.c +++ b/apps/nesemu2/src/nes/cpu/cpu.c @@ -22,7 +22,6 @@ #include #include "nes/nes.h" #include "nes/state/state.h" -#include "misc/log.h" #include "misc/config.h" //show disassembly as we execute diff --git a/apps/nesemu2/src/nes/cpu/cpu.h b/apps/nesemu2/src/nes/cpu/cpu.h index d00de95d..e3abec25 100644 --- a/apps/nesemu2/src/nes/cpu/cpu.h +++ b/apps/nesemu2/src/nes/cpu/cpu.h @@ -21,7 +21,7 @@ #ifndef __nes__cpu_h__ #define __nes__cpu_h__ -#include "types.h" +#include "common.h" typedef struct cpu_s { diff --git a/apps/nesemu2/src/nes/genie.c b/apps/nesemu2/src/nes/genie.c index b8222584..f938b37b 100644 --- a/apps/nesemu2/src/nes/genie.c +++ b/apps/nesemu2/src/nes/genie.c @@ -27,8 +27,6 @@ #include "misc/memutil.h" #include "misc/strutil.h" #include "misc/config.h" -#include "misc/log.h" -#include "misc/paths.h" //holds code information typedef struct codedata_s { @@ -132,69 +130,6 @@ static void genie_write(u32 addr,u8 data) } } -#if 0 -int genie_loadrom(char *filename) -{ - FILE *fp; - size_t len; - u8 buf[16]; - - //try to open the file - if((fp = fopen(filename,"rb")) == 0) { - log_printf("genie_load: error opening game genie rom '%s'\n",filename); - return(1); - } - - //read 16 bytes to see if it has a ines header - fread(buf,1,16,fp); - - //get the file size of the file - fseek(fp,0,SEEK_END); - len = ftell(fp); - fseek(fp,0,SEEK_SET); - - //allocate memory for genierom/geniecache - genierom = (u8*)mem_alloc(0x1000 + 0x400); - geniecache = (cache_t*)mem_alloc(0x400); - - //see if there is an ines header, we know we have 16kb prg, 8kb chr - if(memcmp(buf,inesident,4) == 0) { - fseek(fp,16,SEEK_SET); - fread(genierom,1,0x1000,fp); - fseek(fp,16 + 0x4000,SEEK_SET); - fread(genierom + 0x1000,1,0x400,fp); - } - - //we have no header and 16kb prg, 8kb chr - else if(len == 0x6000) { - fread(genierom,1,0x1000,fp); - fseek(fp,0x4000,SEEK_SET); - fread(genierom + 0x1000,1,0x400,fp); - } - - //if we have raw prg/chr rom dump 4kb prg, 1kb chr - else if(len == 0x1400) { - fread(genierom,1,0x1400,fp); - } - - //now what? error! - else { - fclose(fp); - genie_unload(); - log_printf("genie_load: unable to load genie rom '%s'\n",filename); - return(1); - } - - //cache the genie chr - cache_tiles(genierom + 0x1000,geniecache,64,0); - - //close the file, output message, return - fclose(fp); - log_printf("genie_load: loaded game genie rom '%s' (%d bytes)\n",filename,len); - return(0); -} -#endif - int genie_loadrom(char *filename) { return 1; } int genie_load() diff --git a/apps/nesemu2/src/nes/io.c b/apps/nesemu2/src/nes/io.c index c6c4ab01..0f21bb26 100644 --- a/apps/nesemu2/src/nes/io.c +++ b/apps/nesemu2/src/nes/io.c @@ -18,7 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "misc/log.h" #include "misc/config.h" #include "nes/io.h" #include "nes/nes.h" diff --git a/apps/nesemu2/src/nes/io.h b/apps/nesemu2/src/nes/io.h index 0c87a6e7..3d896ad5 100644 --- a/apps/nesemu2/src/nes/io.h +++ b/apps/nesemu2/src/nes/io.h @@ -21,7 +21,7 @@ #ifndef __nes__io_h__ #define __nes__io_h__ -#include "types.h" +#include "common.h" //nes io handlers u8 nes_read_4000(u32 addr); diff --git a/apps/nesemu2/src/nes/memory.c b/apps/nesemu2/src/nes/memory.c index a0e045cb..c0c3e0db 100644 --- a/apps/nesemu2/src/nes/memory.c +++ b/apps/nesemu2/src/nes/memory.c @@ -20,7 +20,6 @@ #include "nes/memory.h" #include "nes/nes.h" -#include "misc/log.h" void mem_setreadfunc(int page,readfunc_t func) { diff --git a/apps/nesemu2/src/nes/memory.h b/apps/nesemu2/src/nes/memory.h index 3df75fab..bbf3f607 100644 --- a/apps/nesemu2/src/nes/memory.h +++ b/apps/nesemu2/src/nes/memory.h @@ -21,7 +21,7 @@ #ifndef __memory_h__ #define __memory_h__ -#include "types.h" +#include "common.h" int mem_getprgsize(); int mem_getchrsize(); diff --git a/apps/nesemu2/src/nes/movie.c b/apps/nesemu2/src/nes/movie.c deleted file mode 100644 index bd74d750..00000000 --- a/apps/nesemu2/src/nes/movie.c +++ /dev/null @@ -1,292 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include "nes/nes.h" -#include "misc/memutil.h" -#include "misc/memfile.h" -#include "misc/crc32.h" -#include "misc/log.h" -#include "system/video.h" - -#define MOVIE_PREALLOC_SIZE 1024 - -static char movie_ident[] = "NMV\0"; - -int movie_init() -{ - memset(&nes->movie,0,sizeof(movie_t)); - return(0); -} - -void movie_kill() -{ - movie_unload(); -} - -int movie_load(char *filename) -{ - memfile_t *file; - u32 size; - u8 flags; - int devid[3]; - char ident[5]; - - movie_unload(); - if((file = memfile_open(filename,"rb")) == 0) { - log_printf("movie_load: error opening movie '%s'\n",filename); - return(1); - } - //need header and stuff here - memfile_read(ident,1,4,file); - if(memcmp(ident,movie_ident,4) != 0) { - log_printf("movie_load: bad movie ident\n"); - memfile_close(file); - return(2); - } - - //read movie flags - memfile_read(&flags,1,sizeof(u8),file); - if(flags & 1) - nes->movie.mode |= MOVIE_TEST; - - //set required input devices - memfile_read(&devid[0],1,sizeof(int),file); - memfile_read(&devid[1],1,sizeof(int),file); - memfile_read(&devid[2],1,sizeof(int),file); - nes_set_inputdev(0,devid[0]); - nes_set_inputdev(1,devid[1]); - nes_set_inputdev(2,devid[2]); - - //read movie data - memfile_read(&nes->movie.startframe,1,sizeof(u32),file); - memfile_read(&nes->movie.endframe,1,sizeof(u32),file); - memfile_read(&nes->movie.crc32,1,sizeof(u32),file); - memfile_read(&nes->movie.len,1,sizeof(u32),file); - log_printf("movie_load: start, end = %d, %d :: len = %d bytes\n",nes->movie.startframe,nes->movie.endframe,nes->movie.len); - nes->movie.data = (u8*)mem_alloc(nes->movie.len); - memfile_read(nes->movie.data,1,nes->movie.len,file); - size = memfile_size(file) - memfile_tell(file); - nes->movie.state = memfile_create(); - memfile_copy(nes->movie.state,file,size); - memfile_close(file); - return(0); -} - -int movie_save(char *filename) -{ - memfile_t *file; - u8 flags; - - if(filename) { - if(nes->movie.filename) - mem_free(nes->movie.filename); - nes->movie.filename = mem_strdup(filename); - } - else if(nes->movie.filename == 0) { - log_printf("movie_save: error! no filename given and movie doesnt have filename set\n"); - return(0); - } - - if(nes->movie.len == 0) { - log_printf("movie_save: no movie data to save, just setting filename\n"); - return(0); - } - if(nes->movie.mode & MOVIE_RECORD) { - log_printf("movie_save: still recording, stopping...\n"); - movie_stop(); - } - if((file = memfile_open(nes->movie.filename,"wb")) == 0) { - log_printf("movie_save: error opening movie '%s'\n",filename); - return(1); - } - memfile_seek(nes->movie.state,0,SEEK_SET); - - flags = (nes->movie.mode & MOVIE_TEST) ? 1 : 0; - - //header (just ident for now) - memfile_write(movie_ident,1,4,file); - memfile_write(&flags,1,sizeof(u8),file); - - //save input device configuration - memfile_write(&nes->inputdev[0]->id,1,sizeof(int),file); - memfile_write(&nes->inputdev[1]->id,1,sizeof(int),file); - memfile_write(&nes->expdev->id,1,sizeof(int),file); - - //save movie data - memfile_write(&nes->movie.startframe,1,sizeof(u32),file); - memfile_write(&nes->movie.endframe,1,sizeof(u32),file); - memfile_write(&nes->movie.crc32,1,sizeof(u32),file); - memfile_write(&nes->movie.len,1,sizeof(u32),file); - log_printf("movie_save: start, end = %d, %d :: len = %d bytes\n",nes->movie.startframe,nes->movie.endframe,nes->movie.len); - memfile_write(nes->movie.data,1,nes->movie.len,file); - - //append savestate to end of movie - memfile_copy(file,nes->movie.state,memfile_size(nes->movie.state)); - memfile_close(file); - return(0); -} - -void movie_unload() -{ - if(nes->movie.filename) { - movie_save(0); - mem_free(nes->movie.filename); - } - if(nes->movie.data) - mem_free(nes->movie.data); - if(nes->movie.state) - memfile_close(nes->movie.state); - memset(&nes->movie,0,sizeof(movie_t)); -} - -//process frame for movie, MUST be called after input updates -void movie_frame() -{ - if((nes->movie.mode & MOVIE_PLAY) && nes->movie.endframe == nes->ppu.frames) { - log_printf("movie_frame: reached end of movie, stopping.\n"); - movie_stop(); - } - else { - nes->inputdev[0]->movie(nes->movie.mode); - nes->inputdev[1]->movie(nes->movie.mode); - nes->expdev->movie(nes->movie.mode); - } -} - -//initialize movie data for recording -int movie_record() -{ - //get rid of previous data - if(nes->movie.data) - movie_unload(); - - //initialize the movie struct - nes->movie.mode &= ~7; - nes->movie.mode |= MOVIE_RECORD; - nes->movie.pos = 0; - nes->movie.len = MOVIE_PREALLOC_SIZE; - nes->movie.data = (u8*)mem_alloc(nes->movie.len); - nes->movie.startframe = nes->ppu.frames; - - //save state here for loading - nes->movie.state = memfile_create(); - state_save(nes->movie.state); - - return(0); -} - -//setup for playing data -int movie_play() -{ - if(nes->movie.state == 0) { - log_printf("movie_play: cannot play movie, no data loaded\n"); - return(1); - } - - memfile_seek(nes->movie.state,0,SEEK_SET); - state_load(nes->movie.state); - nes->movie.mode &= ~7; - nes->movie.mode |= MOVIE_PLAY; - nes->movie.pos = 0; - return(0); -} - -//stop recording/playing -int movie_stop() -{ - u32 crc; - - switch(nes->movie.mode & 7) { - - //movie stopped (or not loaded, or something...) - case 0: - break; - - //if recording, stop recording - case MOVIE_RECORD: - nes->movie.len = nes->movie.pos; - nes->movie.endframe = nes->ppu.frames; - nes->movie.crc32 = crc32(video_getscreen(),256 * 240); - log_printf("movie_stop: stopped recording, screen crc32 = %08X (%d frames)\n",nes->movie.crc32,nes->movie.endframe - nes->movie.startframe); - break; - - //if playing, check crcs - case MOVIE_PLAY: - crc = crc32(video_getscreen(),256 * 240); - if(crc != nes->movie.crc32) { - nes->movie.mode |= MOVIE_CRCFAIL; - log_printf("movie_stop: error: screen crc32 doesnt match! (wanted %08X, got %08X)\n",nes->movie.crc32,crc); - } - else { - nes->movie.mode |= MOVIE_CRCPASS; - log_printf("movie_stop: screen crc32 matched!\n"); - } - break; - - default: - log_printf("movie_stop: invalid movie mode (%d)\n",nes->movie.mode & 7); - } - - nes->movie.mode &= ~7; - return(0); -} - -//helpers for reading/writing movie data -u8 movie_read_u8() -{ - u8 ret; - - //make sure we are playing - if((nes->movie.mode & MOVIE_PLAY) == 0) { - log_printf("movie_read_u8: not playing, cannot read data\n"); - return(0xFF); - } - - //make sure we dont read past end of the data - if(nes->movie.pos < nes->movie.len) - ret = nes->movie.data[nes->movie.pos++]; - else { - log_printf("movie_read_u8: playing past end of movie, stopping\n"); - movie_stop(); - ret = 0xFF; - } - - return(ret); -} - -void movie_write_u8(u8 data) -{ - //make sure we are recording - if((nes->movie.mode & MOVIE_RECORD) == 0) { - log_printf("movie_write_u8: not recording, cannot write data\n"); - return; - } - - //see if we are at the end of the movie data - if(nes->movie.pos == nes->movie.len) { - nes->movie.len += MOVIE_PREALLOC_SIZE; - nes->movie.data = (u8*)mem_realloc(nes->movie.data,nes->movie.len); - log_printf("movie_write_u8: increasing size of movie buffer (%d bytes)\n",nes->movie.len); - } - - //write the byte - nes->movie.data[nes->movie.pos++] = data; -} diff --git a/apps/nesemu2/src/nes/movie.h b/apps/nesemu2/src/nes/movie.h deleted file mode 100644 index c58ab511..00000000 --- a/apps/nesemu2/src/nes/movie.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __nes__movie_h__ -#define __nes__movie_h__ - -#include "types.h" -#include "misc/memfile.h" - -//movie modes -#define MOVIE_PLAY 0x01 -#define MOVIE_RECORD 0x02 - -//flags -#define MOVIE_TEST 0x10 -#define MOVIE_CRCPASS 0x40 -#define MOVIE_CRCFAIL 0x80 - -typedef struct movie_s { - memfile_t *state; - u8 *data; - u32 len,pos; - int mode; - u32 startframe,endframe; - int port0,port1,exp; - u32 crc32; - char *filename; -} movie_t; - -int movie_init(); -void movie_kill(); -int movie_load(char *filename); -int movie_save(char *filename); -void movie_unload(); -void movie_frame(); -int movie_record(); -int movie_play(); -int movie_stop(); -u8 movie_read_u8(); -void movie_write_u8(u8 data); - -#endif diff --git a/apps/nesemu2/src/nes/nes.c b/apps/nesemu2/src/nes/nes.c index 143af2ed..8ddb8685 100644 --- a/apps/nesemu2/src/nes/nes.c +++ b/apps/nesemu2/src/nes/nes.c @@ -21,10 +21,8 @@ #include #include #include -#include "misc/log.h" #include "misc/config.h" #include "misc/memutil.h" -#include "cartdb/cartdb.h" #include "nes/nes.h" #include "nes/io.h" #include "nes/memory.h" @@ -54,8 +52,6 @@ static int get_device_id(char *str) if(stricmp(str,"joypad0") == 0) ret = I_JOYPAD0; if(stricmp(str,"joypad1") == 0) ret = I_JOYPAD1; - if(stricmp(str,"zapper") == 0) ret = I_ZAPPER; - if(stricmp(str,"powerpad") == 0) ret = I_POWERPAD; return(ret); } @@ -96,14 +92,12 @@ int nes_init() ret += cpu_init(); ret += ppu_init(); ret += apu_init(); - ret += movie_init(); return(ret); } void nes_kill() { if(nes) { - movie_kill(); nes_unload(); genie_unload(); state_kill(); @@ -162,10 +156,6 @@ int nes_load_patched(char *filename,char *patchfilename) else log_printf("nes_load: loaded file '%s'\n",filename); - //check cartdb for rom (will update the cart_t struct) - if(config_get_bool("cartdb.enabled")) - cartdb_find(c); - //see if we should pre-init some wram for the cart if((c->battery & 1) && c->wram.size == 0) { c->wram.size = 8; @@ -187,7 +177,6 @@ int nes_load_patched(char *filename,char *patchfilename) void nes_unload() { - movie_stop(); //need to save sram/diskdata/whatever here if(nes->cart) cart_unload(nes->cart); @@ -273,8 +262,6 @@ void nes_frame() nes->inputdev[0]->update(); nes->inputdev[1]->update(); nes->expdev->update(); - if(nes->movie.mode) - movie_frame(); cpu_execute_frame(); } diff --git a/apps/nesemu2/src/nes/nes.h b/apps/nesemu2/src/nes/nes.h index 2f2ec4ff..b85cd866 100644 --- a/apps/nesemu2/src/nes/nes.h +++ b/apps/nesemu2/src/nes/nes.h @@ -21,8 +21,7 @@ #ifndef __nes_h__ #define __nes_h__ -#include "types.h" -#include "nes/movie.h" +#include "common.h" #include "nes/region.h" #include "nes/cpu/cpu.h" #include "nes/ppu/ppu.h" @@ -78,9 +77,6 @@ typedef struct nes_s { //connected input devices inputdev_t *inputdev[2],*expdev; - //movie support - movie_t movie; - //region information region_t *region; diff --git a/apps/nesemu2/src/nes/ppu/io.c b/apps/nesemu2/src/nes/ppu/io.c index 760dd46b..30c06034 100644 --- a/apps/nesemu2/src/nes/ppu/io.c +++ b/apps/nesemu2/src/nes/ppu/io.c @@ -18,7 +18,6 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "misc/log.h" #include "nes/nes.h" #include "system/video.h" #include "misc/config.h" diff --git a/apps/nesemu2/src/nes/ppu/ppu.h b/apps/nesemu2/src/nes/ppu/ppu.h index 37f834f5..9e203e84 100644 --- a/apps/nesemu2/src/nes/ppu/ppu.h +++ b/apps/nesemu2/src/nes/ppu/ppu.h @@ -21,7 +21,7 @@ #ifndef __nes__ppu_h__ #define __nes__ppu_h__ -#include "types.h" +#include "common.h" #include "tilecache.h" typedef struct ppu_s { diff --git a/apps/nesemu2/src/nes/ppu/step.c b/apps/nesemu2/src/nes/ppu/step.c index 0b35a0e5..1d90d794 100644 --- a/apps/nesemu2/src/nes/ppu/step.c +++ b/apps/nesemu2/src/nes/ppu/step.c @@ -20,7 +20,6 @@ #include "nes/nes.h" #include "system/video.h" -#include "misc/log.h" typedef struct { u64 line; //cache line data diff --git a/apps/nesemu2/src/nes/ppu/tilecache.h b/apps/nesemu2/src/nes/ppu/tilecache.h index 3716869e..19f9fc89 100644 --- a/apps/nesemu2/src/nes/ppu/tilecache.h +++ b/apps/nesemu2/src/nes/ppu/tilecache.h @@ -21,7 +21,7 @@ #ifndef __ppu__tilecache_h__ #define __ppu__tilecache_h__ -#include "types.h" +#include "common.h" #define CACHE_TILE_SIZE 2 #define CACHE_MASK 0x0303030303030303LL diff --git a/apps/nesemu2/src/nes/region.c b/apps/nesemu2/src/nes/region.c index a6cf0ec5..73f0b4e5 100644 --- a/apps/nesemu2/src/nes/region.c +++ b/apps/nesemu2/src/nes/region.c @@ -19,7 +19,6 @@ ***************************************************************************/ #include "nes/nes.h" -#include "misc/log.h" region_t region_ntsc = { REGION_NTSC, diff --git a/apps/nesemu2/src/nes/state/block.c b/apps/nesemu2/src/nes/state/block.c index 4842d397..090f29e3 100644 --- a/apps/nesemu2/src/nes/state/block.c +++ b/apps/nesemu2/src/nes/state/block.c @@ -22,7 +22,6 @@ #include #include #include "misc/memutil.h" -#include "misc/log.h" #include "nes/state/block.h" block_t *block_create(u32 type,u32 size) diff --git a/apps/nesemu2/src/nes/state/state.c b/apps/nesemu2/src/nes/state/state.c index a02a9292..10934c74 100644 --- a/apps/nesemu2/src/nes/state/state.c +++ b/apps/nesemu2/src/nes/state/state.c @@ -21,7 +21,6 @@ #include #include #include -#include "misc/log.h" #include "nes/nes.h" #include "nes/state/state.h" #include "nes/state/block.h" diff --git a/apps/nesemu2/src/palette/palette.c b/apps/nesemu2/src/palette/palette.c index abdcb3ef..a81b35de 100644 --- a/apps/nesemu2/src/palette/palette.c +++ b/apps/nesemu2/src/palette/palette.c @@ -18,19 +18,9 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include "common.h" #include "palette/palette.h" -#include #include "system/video.h" -#if 0 -#include -#include -#include "misc/memutil.h" -#include "misc/strutil.h" -#include "misc/log.h" -#include "misc/config.h" -#include "palette/generator.h" -#include "misc/paths.h" -#endif static palette_t pal = {{ { @@ -115,116 +105,6 @@ static palette_t pal = {{ } }}; -#if 0 -/* measurement by Quietust */ -static const double emphasis_factor[8][3]={ - {1.00, 1.00, 1.00}, - {1.00, 0.80, 0.81}, - {0.78, 0.94, 0.66}, - {0.79, 0.77, 0.63}, - {0.82, 0.83, 1.12}, - {0.81, 0.71, 0.87}, - {0.68, 0.79, 0.79}, - {0.70, 0.70, 0.70} -}; - -static void generate_emphasis(palette_t *p) -{ - int i,j; - - for(j=1;j<8;j++) { - for(i=0;i<64;i++) { - p->pal[j][i].r = (u8)((double)p->pal[0][i].r * emphasis_factor[j][0]); - p->pal[j][i].g = (u8)((double)p->pal[0][i].g * emphasis_factor[j][1]); - p->pal[j][i].b = (u8)((double)p->pal[0][i].b * emphasis_factor[j][2]); - } - } -} - -void palette_destroy(palette_t *p) -{ - mem_free(p); -} - -palette_t *palette_load(char *filename) -{ - palette_t *ret = 0; - FILE *fp; - u8 colors[3]; - int i; - - if((fp = fopen(filename,"rb")) == 0) { - log_printf("palette_load: error opening '%s'\n",filename); - return(0); - } - ret = palette_create(); - for(i=0;i<64;i++) { - fread(colors,1,3,fp); - if(feof(fp)) { - log_printf("palette_load: unexpected end of file in '%s'\n",filename); - palette_destroy(ret); - ret = 0; - break; - } - ret->pal[0][i].r = colors[0]; - ret->pal[0][i].g = colors[1]; - ret->pal[0][i].b = colors[2]; - } - fclose(fp); - generate_emphasis(ret); - log_printf("palette_load: loaded palette '%s'\n",filename); - return(ret); -} - -int palette_save(char *filename,palette_t *p) -{ - //not implemented yet - return(1); -} - -void palette_kill() -{ - palette_destroy(pal); -} - -palette_t *palette_create() -{ - palette_t *ret = 0; - - ret = (palette_t*)mem_alloc(sizeof(palette_t)); - memset(ret,0,sizeof(palette_t)); - return(ret); -} - -int palette_init() -{ - char file[1024]; - - //clear the string - memset(file,0,1024); - - //parse the bios path - config_get_eval_string(file,"path.palette"); - - //append the path seperator - str_appendchar(file,PATH_SEPERATOR); - - //append the bios filename - strcat(file,config_get_string("palette.filename")); - - if(strcmp(config_get_string("palette.source"),"file") == 0) { - pal = palette_load(file); - } - if(pal == 0) { - pal = palette_generate(config_get_int("palette.hue"),config_get_int("palette.saturation")); - } - video_setpalette(&pal); - - return(0); -} - -#endif - palette_t *palette_load(char *filename) { return NULL; } int palette_save(char *filename,palette_t *p) {return 1; } int palette_init() { diff --git a/apps/nesemu2/src/palette/palette.h b/apps/nesemu2/src/palette/palette.h index 8a5c5331..70791bf8 100644 --- a/apps/nesemu2/src/palette/palette.h +++ b/apps/nesemu2/src/palette/palette.h @@ -21,7 +21,7 @@ #ifndef __palette_h__ #define __palette_h__ -#include "types.h" +#include "common.h" typedef union palentry_s { struct { diff --git a/apps/nesemu2/src/system/common/filters.h b/apps/nesemu2/src/system/common/filters.h index 55089974..e4a0d4e2 100644 --- a/apps/nesemu2/src/system/common/filters.h +++ b/apps/nesemu2/src/system/common/filters.h @@ -21,7 +21,7 @@ #ifndef __filters_h__ #define __filters_h__ -#include "types.h" +#include "common.h" #define FILTER_DECL(name) extern filter_t filter_ ##name ; diff --git a/apps/nesemu2/src/system/common/filters/draw/draw.h b/apps/nesemu2/src/system/common/filters/draw/draw.h index c6dd964f..184ba7a7 100644 --- a/apps/nesemu2/src/system/common/filters/draw/draw.h +++ b/apps/nesemu2/src/system/common/filters/draw/draw.h @@ -21,7 +21,7 @@ #ifndef __draw_h__ #define __draw_h__ -#include "types.h" +#include "common.h" void draw1x(void *dest,u32 destp,void *src,u32 srcp,u32 w,u32 h); void draw2x(void *dest,u32 destp,void *src,u32 srcp,u32 w,u32 h); diff --git a/apps/nesemu2/src/system/common/filters/interpolate/interpolate.h b/apps/nesemu2/src/system/common/filters/interpolate/interpolate.h index 8af4f636..5d4215b8 100644 --- a/apps/nesemu2/src/system/common/filters/interpolate/interpolate.h +++ b/apps/nesemu2/src/system/common/filters/interpolate/interpolate.h @@ -1,7 +1,7 @@ #ifndef __interpolate_h__ #define __interpolate_h__ -#include "types.h" +#include "common.h" void interpolate2x(void *dst,u32 dst_slice,void *src,u32 src_slice,u32 width,u32 height); void interpolate3x(void *dst,u32 dst_slice,void *src,u32 src_slice,u32 width,u32 height); diff --git a/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c b/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c index a1f4c79d..a4bce25c 100644 --- a/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c +++ b/apps/nesemu2/src/system/common/filters/ntsc/ntsc.c @@ -20,124 +20,6 @@ #include "ntsc.h" -#if 0 -#include -#include -#include "nes_ntsc/nes_ntsc.h" -#include "misc/memutil.h" -#include "system/video.h" -#include "nes/nes.h" - -#define NES_NTSC_NO_BLITTERS -#include "nes_ntsc/nes_ntsc.inl" - -nes_ntsc_t *ntsc = 0; - -static void palette_changed() -{ - nes_ntsc_setup_t setup; - double sharpness = 100.0f; - double resolution = 100.0f; - double artifacts = 100.0f; - double fringing = 100.0f; - double bleed = 100.0f; - int merge_fields = 1; - - setup.hue = 0; - setup.saturation = 0; - setup.contrast = 0; - setup.brightness = 0; - setup.sharpness = sharpness / 100.0; - setup.gamma = 0; - setup.resolution = resolution / 100.0; - setup.artifacts = artifacts / 100.0; - setup.fringing = fringing / 100.0; - setup.bleed = bleed / 100.0; - setup.merge_fields = merge_fields; - setup.decoder_matrix = NULL; - setup.palette_out = NULL; - setup.palette = NULL; - setup.base_palette = video_getpalette(); - nes_ntsc_init(ntsc,&setup); -} - -int ntsc_init() -{ - ntsc_kill(); - ntsc = (nes_ntsc_t*)mem_alloc(sizeof(nes_ntsc_t)); - palette_changed(); - return(0); -} - -void ntsc_kill() -{ - if(ntsc) - mem_free(ntsc); - ntsc = 0; -} - -void ntsc_palette_changed() -{ - if(ntsc) - palette_changed(); -} - -void ntsc2x(void *d,u32 dst_slice,void *s,u32 src_slice,u32 width,u32 height) -{ - static int phase = 0; - int bpp = 32; - int i,h; - int chunk_count = (256 - 1) / nes_ntsc_in_chunk; - u8 *screen = video_getscreen(); - u8 *src = screen; - u32 *dst = (u32*)d; - u8 *pal = nes->ppu.palette; - - assert(0); - for(h=0;h<240;h++) { - u32 *dststart = dst; - - NES_NTSC_BEGIN_ROW( ntsc, phase, nes_ntsc_black, nes_ntsc_black, pal[*src & 0x1F] | ((*src & 0xE0) << 1) ); - src++; - - for(i=0;i -#endif - -#define inline __inline - -#include "scale2x.h" -#include "scale3x.h" - -#if HAVE_ALLOCA_H -#include -#endif - -#include -#include - -#define SSDST(bits, num) (scale2x_uint##bits *)dst##num -#define SSSRC(bits, num) (const scale2x_uint##bits *)src##num - -/** - * Apply the Scale2x effect on a group of rows. Used internally. - */ -static inline void stage_scale2x(void* dst0, void* dst1, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) -{ - switch (pixel) { -#if defined(__GNUC__) && defined(__i386__) - case 1 : scale2x_8_mmx(SSDST(8,0), SSDST(8,1), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale2x_16_mmx(SSDST(16,0), SSDST(16,1), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale2x_32_mmx(SSDST(32,0), SSDST(32,1), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; -#else - case 1 : scale2x_8_def(SSDST(8,0), SSDST(8,1), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale2x_16_def(SSDST(16,0), SSDST(16,1), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale2x_32_def(SSDST(32,0), SSDST(32,1), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; -#endif - } -} - -/** - * Apply the Scale2x3 effect on a group of rows. Used internally. - */ -static inline void stage_scale2x3(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) -{ - switch (pixel) { -#if defined(__GNUC__) && defined(__i386__) - case 1 : scale2x3_8_mmx(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale2x3_16_mmx(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale2x3_32_mmx(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; -#else - case 1 : scale2x3_8_def(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale2x3_16_def(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale2x3_32_def(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; -#endif - } -} - -/** - * Apply the Scale2x4 effect on a group of rows. Used internally. - */ -static inline void stage_scale2x4(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) -{ - switch (pixel) { -#if defined(__GNUC__) && defined(__i386__) - case 1 : scale2x4_8_mmx(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSDST(8,3), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale2x4_16_mmx(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSDST(16,3), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale2x4_32_mmx(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSDST(32,3), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; -#else - case 1 : scale2x4_8_def(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSDST(8,3), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale2x4_16_def(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSDST(16,3), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale2x4_32_def(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSDST(32,3), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; -#endif - } -} - -/** - * Apply the Scale3x effect on a group of rows. Used internally. - */ -static inline void stage_scale3x(void* dst0, void* dst1, void* dst2, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) -{ - switch (pixel) { - case 1 : scale3x_8_def(SSDST(8,0), SSDST(8,1), SSDST(8,2), SSSRC(8,0), SSSRC(8,1), SSSRC(8,2), pixel_per_row); break; - case 2 : scale3x_16_def(SSDST(16,0), SSDST(16,1), SSDST(16,2), SSSRC(16,0), SSSRC(16,1), SSSRC(16,2), pixel_per_row); break; - case 4 : scale3x_32_def(SSDST(32,0), SSDST(32,1), SSDST(32,2), SSSRC(32,0), SSSRC(32,1), SSSRC(32,2), pixel_per_row); break; - } -} - -/** - * Apply the Scale4x effect on a group of rows. Used internally. - */ -static inline void stage_scale4x(void* dst0, void* dst1, void* dst2, void* dst3, const void* src0, const void* src1, const void* src2, const void* src3, unsigned pixel, unsigned pixel_per_row) -{ - stage_scale2x(dst0, dst1, src0, src1, src2, pixel, 2 * pixel_per_row); - stage_scale2x(dst2, dst3, src1, src2, src3, pixel, 2 * pixel_per_row); -} - -#define SCDST(i) (dst+(i)*dst_slice) -#define SCSRC(i) (src+(i)*src_slice) -#define SCMID(i) (mid[(i)]) - -/** - * Apply the Scale2x effect on a bitmap. - * The destination bitmap is filled with the scaled version of the source bitmap. - * The source bitmap isn't modified. - * The destination bitmap must be manually allocated before calling the function, - * note that the resulting size is exactly 2x2 times the size of the source bitmap. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -void scale2x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) -{ - unsigned char* dst = (unsigned char*)void_dst; - const unsigned char* src = (const unsigned char*)void_src; - unsigned count; - unsigned pixel = 4; - - assert(height >= 2); - - count = height; - - stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); - - dst = SCDST(2); - - count -= 2; - while (count) { - stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); - - dst = SCDST(2); - src = SCSRC(1); - - --count; - } - - stage_scale2x(SCDST(0), SCDST(1), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); - -#if defined(__GNUC__) && defined(__i386__) - scale2x_mmx_emms(); -#endif -} - -/** - * Apply the Scale2x3 effect on a bitmap. - * The destination bitmap is filled with the scaled version of the source bitmap. - * The source bitmap isn't modified. - * The destination bitmap must be manually allocated before calling the function, - * note that the resulting size is exactly 2x3 times the size of the source bitmap. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -static void scale2x3(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) -{ - unsigned char* dst = (unsigned char*)void_dst; - const unsigned char* src = (const unsigned char*)void_src; - unsigned count; - - assert(height >= 2); - - count = height; - - stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); - - dst = SCDST(3); - - count -= 2; - while (count) { - stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); - - dst = SCDST(3); - src = SCSRC(1); - - --count; - } - - stage_scale2x3(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); - -#if defined(__GNUC__) && defined(__i386__) - scale2x_mmx_emms(); -#endif -} - -/** - * Apply the Scale2x4 effect on a bitmap. - * The destination bitmap is filled with the scaled version of the source bitmap. - * The source bitmap isn't modified. - * The destination bitmap must be manually allocated before calling the function, - * note that the resulting size is exactly 2x4 times the size of the source bitmap. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -static void scale2x4(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) -{ - unsigned char* dst = (unsigned char*)void_dst; - const unsigned char* src = (const unsigned char*)void_src; - unsigned count; - - assert(height >= 2); - - count = height; - - stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); - - dst = SCDST(4); - - count -= 2; - while (count) { - stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); - - dst = SCDST(4); - src = SCSRC(1); - - --count; - } - - stage_scale2x4(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); - -#if defined(__GNUC__) && defined(__i386__) - scale2x_mmx_emms(); -#endif -} - -/** - * Apply the Scale3x effect on a bitmap. - * The destination bitmap is filled with the scaled version of the source bitmap. - * The source bitmap isn't modified. - * The destination bitmap must be manually allocated before calling the function, - * note that the resulting size is exactly 3x3 times the size of the source bitmap. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -void scale3x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) -{ - unsigned char* dst = (unsigned char*)void_dst; - const unsigned char* src = (const unsigned char*)void_src; - unsigned count; - unsigned pixel = 4; - - assert(height >= 2); - - count = height; - - stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); - - dst = SCDST(3); - - count -= 2; - while (count) { - stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); - - dst = SCDST(3); - src = SCSRC(1); - - --count; - } - - stage_scale3x(SCDST(0), SCDST(1), SCDST(2), SCSRC(0), SCSRC(1), SCSRC(1), pixel, width); -} - -/** - * Apply the Scale4x effect on a bitmap. - * The destination bitmap is filled with the scaled version of the source bitmap. - * The source bitmap isn't modified. - * The destination bitmap must be manually allocated before calling the function, - * note that the resulting size is exactly 4x4 times the size of the source bitmap. - * \note This function requires also a small buffer bitmap used internally to store - * intermediate results. This bitmap must have at least an horizontal size in bytes of 2*width*pixel, - * and a vertical size of 6 rows. The memory of this buffer must not be allocated - * in video memory because it's also read and not only written. Generally - * a heap (malloc) or a stack (alloca) buffer is the best choice. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_mid Pointer at the first pixel of the buffer bitmap. - * \param mid_slice Size in bytes of a buffer bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -static void scale4x_buf(void* void_dst, unsigned dst_slice, void* void_mid, unsigned mid_slice, const void* void_src, unsigned src_slice, unsigned pixel, unsigned width, unsigned height) -{ - unsigned char* dst = (unsigned char*)void_dst; - const unsigned char* src = (const unsigned char*)void_src; - unsigned count; - unsigned char* mid[6]; - - assert(height >= 4); - - count = height; - - /* set the 6 buffer pointers */ - mid[0] = (unsigned char*)void_mid; - mid[1] = mid[0] + mid_slice; - mid[2] = mid[1] + mid_slice; - mid[3] = mid[2] + mid_slice; - mid[4] = mid[3] + mid_slice; - mid[5] = mid[4] + mid_slice; - - stage_scale2x(SCMID(-2+6), SCMID(-1+6), SCSRC(0), SCSRC(0), SCSRC(1), pixel, width); - stage_scale2x(SCMID(0), SCMID(1), SCSRC(0), SCSRC(1), SCSRC(2), pixel, width); - stage_scale2x(SCMID(2), SCMID(3), SCSRC(1), SCSRC(2), SCSRC(3), pixel, width); - stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(-2+6), SCMID(-2+6), SCMID(-1+6), SCMID(0), pixel, width); - - dst = SCDST(4); - - stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(-1+6), SCMID(0), SCMID(1), SCMID(2), pixel, width); - - dst = SCDST(4); - - count -= 4; - while (count) { - unsigned char* tmp; - - stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(4), pixel, width); - stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width); - - dst = SCDST(4); - src = SCSRC(1); - - tmp = SCMID(0); /* shift by 2 position */ - SCMID(0) = SCMID(2); - SCMID(2) = SCMID(4); - SCMID(4) = tmp; - tmp = SCMID(1); - SCMID(1) = SCMID(3); - SCMID(3) = SCMID(5); - SCMID(5) = tmp; - - --count; - } - - stage_scale2x(SCMID(4), SCMID(5), SCSRC(2), SCSRC(3), SCSRC(3), pixel, width); - stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(1), SCMID(2), SCMID(3), SCMID(4), pixel, width); - - dst = SCDST(4); - - stage_scale4x(SCDST(0), SCDST(1), SCDST(2), SCDST(3), SCMID(3), SCMID(4), SCMID(5), SCMID(5), pixel, width); - -#if defined(__GNUC__) && defined(__i386__) - scale2x_mmx_emms(); -#endif -} - -/** - * Apply the Scale4x effect on a bitmap. - * The destination bitmap is filled with the scaled version of the source bitmap. - * The source bitmap isn't modified. - * The destination bitmap must be manually allocated before calling the function, - * note that the resulting size is exactly 4x4 times the size of the source bitmap. - * \note This function operates like ::scale4x_buf() but the intermediate buffer is - * automatically allocated in the stack. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -void scale4x(void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) -{ - unsigned mid_slice; - void* mid; - unsigned pixel = 4; - - mid_slice = 2 * pixel * width; /* required space for 1 row buffer */ - - mid_slice = (mid_slice + 0x7) & ~0x7; /* align to 8 bytes */ - -#if HAVE_ALLOCA - mid = alloca(6 * mid_slice); /* allocate space for 6 row buffers */ - - assert(mid != 0); /* alloca should never fails */ -#else - mid = malloc(6 * mid_slice); /* allocate space for 6 row buffers */ - - if (!mid) - return; -#endif - - scale4x_buf(void_dst, dst_slice, mid, mid_slice, void_src, src_slice, pixel, width, height); - -#if !HAVE_ALLOCA - free(mid); -#endif -} - -/** - * Check if the scale implementation is applicable at the given arguments. - * \param scale Scale factor. 2, 203 (fox 2x3), 204 (for 2x4), 3 or 4. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - * \return - * - -1 on precondition violated. - * - 0 on success. - */ -int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned height) -{ - if (pixel != 1 && pixel != 2 && pixel != 4) - return -1; - - switch (scale) { - case 202 : - case 203 : - case 204 : - case 2 : - case 303 : - case 3 : - if (height < 2) - return -1; - break; - case 404 : - case 4 : - if (height < 4) - return -1; - break; - default: - return -1; - } - - if (width < 2) - return -1; - - return 0; -} - -/** - * Apply the Scale effect on a bitmap. - * This function is simply a common interface for ::scale2x(), ::scale3x() and ::scale4x(). - * \param scale Scale factor. 2, 203 (fox 2x3), 204 (for 2x4), 3 or 4. - * \param void_dst Pointer at the first pixel of the destination bitmap. - * \param dst_slice Size in bytes of a destination bitmap row. - * \param void_src Pointer at the first pixel of the source bitmap. - * \param src_slice Size in bytes of a source bitmap row. - * \param pixel Bytes per pixel of the source and destination bitmap. - * \param width Horizontal size in pixels of the source bitmap. - * \param height Vertical size in pixels of the source bitmap. - */ -void scale(unsigned scale, void* void_dst, unsigned dst_slice, const void* void_src, unsigned src_slice, unsigned width, unsigned height) -{ - switch (scale) { - case 202 : - case 2 : - scale2x(void_dst, dst_slice, void_src, src_slice, width, height); - break; - case 203 : - scale2x3(void_dst, dst_slice, void_src, src_slice, 4, width, height); - break; - case 204 : - scale2x4(void_dst, dst_slice, void_src, src_slice, 4, width, height); - break; - case 303 : - case 3 : - scale3x(void_dst, dst_slice, void_src, src_slice, width, height); - break; - case 404 : - case 4 : - scale4x(void_dst, dst_slice, void_src, src_slice, width, height); - break; - } -} - -#endif - -#include "types.h" +#include "common.h" void scale(unsigned scale, void* void_dst, u32 dst_slice, void* void_src, u32 src_slice, u32 width, u32 height) { } diff --git a/apps/nesemu2/src/system/input.h b/apps/nesemu2/src/system/input.h index 6d7e0954..07ec4a72 100644 --- a/apps/nesemu2/src/system/input.h +++ b/apps/nesemu2/src/system/input.h @@ -21,7 +21,7 @@ #ifndef __system__input_h__ #define __system__input_h__ -#include "types.h" +#include "common.h" //system keys #define INPUT_HARDRESET 0x00100000 diff --git a/apps/nesemu2/src/system/sdl/console/console.c b/apps/nesemu2/src/system/sdl/console/console.c deleted file mode 100644 index 06a35a1f..00000000 --- a/apps/nesemu2/src/system/sdl/console/console.c +++ /dev/null @@ -1,189 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include "emu/commands.h" -#include "misc/log.h" -#include "misc/memutil.h" -#include "palette/palette.h" -#include "system/input.h" -#include "system/video.h" -#include "system/sdl/console/console.h" -#include "system/sdl/console/font.h" -#include "system/sdl/console/linebuffer.h" - -#define YPOS_STEP 13 - -#define CONSOLE_BGCOLOR 0x0F0FC0 -#define CONSOLE_BORDER 0x0F0F80 - -//showing flag -static int showing; - -//console video buffer -static u32 *screen; - -//width and height of console -static int width,height; - -//y position of bottom of console -static int ypos; - -//cursor position -//static int cursorpos = 0; - -//last message outputted -static char statusmsg[1024]; -static int statustime = 0; - -//input buffer -static char inputbuf[1024]; -static int inputbuflen,inputbufpos; -static int cursorblink; - -void console_loghook(char *str) -{ - linebuffer_add(str); - strncpy(statusmsg,str,1024); - statustime = 60 * 3; -} - -int console_init() -{ - int ret = 0; - - memset(inputbuf,0,1024); - inputbuflen = inputbufpos = 0; - cursorblink = 0; - showing = 0; - width = video_getwidth(); - height = video_getheight() * 3 / 5 + 4; - screen = mem_alloc(width * height * sizeof(u32)); - ret += linebuffer_init((width - 8) / 8); - log_sethook(console_loghook); - log_printf("console init'd: width, height = %d, %d\n",width,height); - return(ret); -} - -void console_kill() -{ - log_sethook(0); - linebuffer_kill(); - if(screen) - mem_free(screen); -} - -void console_draw(u32 *dest,int w,int h) -{ - int x,y,i,n; - u32 *src = screen; - u32 pixel; - - w /= 4; - //clear console screen - for(y=0;y<(height-4);y++) { - for(x=0;x> 3) & 0x1F1F1F; - pixel |= src[x] & 0xE0E0E0; - dest[x] = pixel; - } - dest += w; - src += width; - } - - //show status message - if(ypos == 0 && statustime) { - statusmsg[w / 8] = 0; - font_drawstr(statusmsg,dest,w); - } -} - -void console_print(char *str) -{ - -} - -void console_show() -{ - showing = 1; -} - -void console_hide() -{ - showing = 0; -} - -void console_update() { } - -void console_keyevent(int state,int sym) { } diff --git a/apps/nesemu2/src/system/sdl/console/console.h b/apps/nesemu2/src/system/sdl/console/console.h deleted file mode 100644 index d47f447a..00000000 --- a/apps/nesemu2/src/system/sdl/console/console.h +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __console_h__ -#define __console_h__ - -#include "types.h" - -int console_init(); -void console_kill(); -void console_draw(u32 *dest,int w,int h); -void console_update(); -void console_keyevent(int state,int sym); - -#endif diff --git a/apps/nesemu2/src/system/sdl/console/font.c b/apps/nesemu2/src/system/sdl/console/font.c deleted file mode 100644 index 13d24a8c..00000000 --- a/apps/nesemu2/src/system/sdl/console/font.c +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include "font.h" -#include "fontdata.h" - -#define TEXTCOLOR 0xEEEEEE - -static int getindex(char ch) -{ - int i; - - for(i=0;fontmap[i];i++) { - if(fontmap[i] == ch) - return(i); - } - return(-1); -} - -void font_drawchar(char ch,u32 *dest,int pitch) -{ - int x,y,index = getindex(ch); - u8 *bits = fontbits; - - if(index == -1) - return; - bits += index * 8; - for(y=0;y<8;y++) { - for(x=0;x<8;x++) { - if(bits[y] & (1 << x)) - dest[7 - x] = TEXTCOLOR; - } - dest += pitch; - } -} - -void font_drawstr(char *str,u32 *dest,int pitch) -{ - while(*str) { - font_drawchar(*str++,dest,pitch); - dest += 8; - } -} diff --git a/apps/nesemu2/src/system/sdl/console/font.h b/apps/nesemu2/src/system/sdl/console/font.h deleted file mode 100644 index 18521cd8..00000000 --- a/apps/nesemu2/src/system/sdl/console/font.h +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __font_h__ -#define __font_h__ - -#include "types.h" - -//draw character to dest -void font_drawchar(char ch,u32 *dest,int pitch); - -//draw string to dest (make sure it doesnt pass edge of screen!) -void font_drawstr(char *str,u32 *dest,int pitch); - -#endif diff --git a/apps/nesemu2/src/system/sdl/console/fontdata.c b/apps/nesemu2/src/system/sdl/console/fontdata.c deleted file mode 100644 index 235c4a27..00000000 --- a/apps/nesemu2/src/system/sdl/console/fontdata.c +++ /dev/null @@ -1,142 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -/* i do not remember where i found this font */ - -#include "fontdata.h" - -/* map: - !"#$%&'()*+,-./ - 0123456789:;<=>? - @ABCDEFGHIJKLMNO - PQRSTUVWXYZ[\]^_ - `abcdefghijklmno - pqrstuvwxyz{|}~ -*/ - -u8 fontmap[] = { - ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/', - '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?', - '@','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','{','|','}','~',' ', - 0 -}; - -//dimensions: w=128 h=48 bpp=1 -u8 fontbits[] = { - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00, - 0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00, - 0x00,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x00, - 0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00, - 0x62,0x66,0x0C,0x18,0x30,0x66,0x46,0x00, - 0x1C,0x36,0x1C,0x38,0x6B,0x66,0x3B,0x00, - 0x0C,0x0C,0x0C,0x00,0x00,0x00,0x00,0x00, - 0x06,0x0C,0x18,0x18,0x18,0x0C,0x06,0x00, - 0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00, - 0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00, - 0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60, - 0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00, - 0x02,0x06,0x0C,0x18,0x30,0x60,0x40,0x00, - 0x38,0x4C,0xCE,0xC6,0xE6,0x64,0x38,0x00, - 0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00, - 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00, - 0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00, - 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00, - 0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00, - 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00, - 0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00, - 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00, - 0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00, - 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, - 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x60, - 0x00,0x18,0x30,0x60,0x30,0x18,0x00,0x00, - 0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00, - 0x00,0x18,0x0C,0x06,0x0C,0x18,0x00,0x00, - 0x7C,0xC6,0x06,0x1C,0x30,0x00,0x30,0x00, - 0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3E,0x00, - 0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00, - 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00, - 0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00, - 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00, - 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00, - 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00, - 0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00, - 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00, - 0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00, - 0x1E,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00, - 0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x00, - 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00, - 0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00, - 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00, - 0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, - 0xFC,0xC6,0xC6,0xFC,0xC0,0xC0,0xC0,0x00, - 0x7C,0xC6,0xC6,0xC6,0xDA,0xCC,0x76,0x00, - 0xFC,0xC6,0xC6,0xFC,0xD8,0xCC,0xC6,0x00, - 0x7C,0xC6,0xC0,0x7C,0x06,0xC6,0x7C,0x00, - 0xFC,0x30,0x30,0x30,0x30,0x30,0x30,0x00, - 0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00, - 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00, - 0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00, - 0xC6,0xEE,0x7C,0x38,0x7C,0xEE,0xC6,0x00, - 0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00, - 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00, - 0x1E,0x18,0x18,0x18,0x18,0x18,0x1E,0x00, - 0x40,0x60,0x30,0x18,0x0C,0x06,0x02,0x00, - 0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00, - 0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00, - 0xC0,0x60,0x30,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x78,0x0C,0x7C,0xCC,0x76,0x00, - 0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xF8,0x00, - 0x00,0x00,0x78,0xCC,0xC0,0xCC,0x78,0x00, - 0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x7C,0x00, - 0x00,0x00,0x78,0xCC,0xFC,0xC0,0x78,0x00, - 0x1C,0x30,0x30,0x78,0x30,0x30,0x30,0x00, - 0x00,0x00,0x7C,0xCC,0xCC,0x7C,0x0C,0x78, - 0xC0,0xC0,0xF8,0xCC,0xCC,0xCC,0xCC,0x00, - 0x00,0x30,0x00,0x30,0x30,0x30,0x30,0x00, - 0x00,0x18,0x00,0x18,0x18,0x18,0x18,0x70, - 0xC0,0xC0,0xCC,0xD8,0xF0,0xD8,0xCC,0x00, - 0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00, - 0x00,0x00,0xEC,0xFE,0xD6,0xD6,0xD6,0x00, - 0x00,0x00,0xD8,0xEC,0xCC,0xCC,0xCC,0x00, - 0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00, - 0x00,0x00,0xF8,0xCC,0xCC,0xF8,0xC0,0xC0, - 0x00,0x00,0x7C,0xCC,0xCC,0x7C,0x0C,0x0C, - 0x00,0x00,0xD8,0xEC,0xC0,0xC0,0xC0,0x00, - 0x00,0x00,0x78,0xC0,0x78,0x0C,0xF8,0x00, - 0x00,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00, - 0x00,0x00,0xCC,0xCC,0xCC,0xDC,0x6C,0x00, - 0x00,0x00,0xCC,0xCC,0xCC,0x78,0x30,0x00, - 0x00,0x00,0xD6,0xD6,0xD6,0xFE,0x6C,0x00, - 0x00,0x00,0xCC,0x78,0x30,0x78,0xCC,0x00, - 0x00,0x00,0xCC,0xCC,0xCC,0x7C,0x0C,0x78, - 0x00,0x00,0xFC,0x18,0x30,0x60,0xFC,0x00, - 0x0E,0x18,0x18,0x30,0x18,0x18,0x0E,0x00, - 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00, - 0x70,0x18,0x18,0x0C,0x18,0x18,0x70,0x00, - 0x60,0xF2,0x9E,0x0C,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; diff --git a/apps/nesemu2/src/system/sdl/console/fontdata.h b/apps/nesemu2/src/system/sdl/console/fontdata.h deleted file mode 100644 index 420b33c4..00000000 --- a/apps/nesemu2/src/system/sdl/console/fontdata.h +++ /dev/null @@ -1,29 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __fontdata_h__ -#define __fontdata_h__ - -#include "types.h" - -extern u8 fontmap[]; -extern u8 fontbits[]; - -#endif diff --git a/apps/nesemu2/src/system/sdl/console/linebuffer.c b/apps/nesemu2/src/system/sdl/console/linebuffer.c deleted file mode 100644 index 9835b935..00000000 --- a/apps/nesemu2/src/system/sdl/console/linebuffer.c +++ /dev/null @@ -1,139 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#include -#include -#include -#include "misc/memutil.h" -#include "system/sdl/console/linebuffer.h" - -#define CONSOLE_LINEBUF_SIZE 128 - -//line buffer (circular) -static char **linebuf = 0; - -//max width of strings in the buffer (for chopping them) -static size_t maxlen; - -//next available line for use in the buffer -static int nextline; - -//is the buffer full? are we circling? -static int full; - -static void increment_nextline() -{ - nextline++; - if(nextline == CONSOLE_LINEBUF_SIZE) { - nextline = 0; - full = 1; - } -} - -static void freeline(int n) -{ - if(linebuf[n]) - mem_free(linebuf[n]); - linebuf[n] = 0; -} - -int linebuffer_init(int len) -{ - linebuf = (char**)mem_alloc(sizeof(char*) * CONSOLE_LINEBUF_SIZE); - memset(linebuf,0,sizeof(char*) * CONSOLE_LINEBUF_SIZE); - nextline = 0; - maxlen = len; - return(0); -} - -void linebuffer_kill() -{ - if(linebuf) { - linebuffer_clear(); - mem_free(linebuf); - } -} - -static void addline(char *str) -{ - freeline(nextline); - linebuf[nextline] = mem_strdup(str); - increment_nextline(); -} -void linebuffer_add(char *str) -{ - //free line (if it is already used) - if(strlen(str) < maxlen) { - addline(str); - } - else { - char *tmp = mem_strdup(str); - char *tmpp,*p = tmp; - size_t len = strlen(p); - - while(len >= maxlen) { - tmpp = mem_strdup(p); - tmpp[maxlen] = 0; - addline(tmpp); - mem_free(tmpp); - p[maxlen-1] = 0; -// printf("adding wrapped line: '%s' (len = %d, maxlen = %d)\n",p,len,maxlen); - p += maxlen; - len -= maxlen; - } -// printf("adding wrapped line (tail): '%s' (len = %d, maxlen = %d)\n",p,len,maxlen); - addline(p); - mem_free(tmp); - } -} - -void linebuffer_clear() -{ - int i; - - for(i=0;i= CONSOLE_LINEBUF_SIZE) - return(0); - return(linebuf[index]); -} - -//get a line relative to the last line added -char *linebuffer_getrelative(int index) -{ - int line = nextline - (1 + index); - - if(line < 0) - line = CONSOLE_LINEBUF_SIZE - (1 + index); - return(linebuffer_get(line)); -} - -//returns the number of lines used -int linebuffer_count() -{ - return(full ? CONSOLE_LINEBUF_SIZE : nextline); -} diff --git a/apps/nesemu2/src/system/sdl/console/linebuffer.h b/apps/nesemu2/src/system/sdl/console/linebuffer.h deleted file mode 100644 index 9459c402..00000000 --- a/apps/nesemu2/src/system/sdl/console/linebuffer.h +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by James Holodnak * - * jamesholodnak@gmail.com * - * * - * 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., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef __linebuffer_h__ -#define __linebuffer_h__ - -int linebuffer_init(int len); -void linebuffer_kill(); -void linebuffer_add(char *str); -void linebuffer_clear(); -char *linebuffer_get(int index); -char *linebuffer_getrelative(int index); -int linebuffer_count(); - -#endif diff --git a/apps/nesemu2/src/system/sdl/main.c b/apps/nesemu2/src/system/sdl/main.c index 0a6230c1..373ac060 100644 --- a/apps/nesemu2/src/system/sdl/main.c +++ b/apps/nesemu2/src/system/sdl/main.c @@ -18,103 +18,30 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include -#include "version.h" #include "emu/emu.h" -#include "emu/commands.h" -#include "emu/events.h" -#include "misc/log.h" -#include "misc/config.h" -#include "misc/paths.h" -#include "misc/memutil.h" #include "nes/nes.h" #include "system/main.h" -#include "system/system.h" -#include "system/video.h" -#include "system/input.h" -#include "palette/palette.h" -//#include "palette/generator.h" -#include "system/sdl/console/console.h" - -#include -#include - -//required -char configfilename[1024] = CONFIG_FILENAME; -char exepath[1024] = ""; - -//todo: this is getting ugly -int main(const char *romfilename) -{ - //int i; - int ret; - //int recordmovie = 0; - //char *p; - //char romfilename[1024] = ""; - //char patchfilename[1024] = ""; - //char moviefilename[1024] = ""; - //char testfilename[1024] = ""; +int main(const char *romfilename) { _ioe_init(); - //clear the tmp strings and configfile string - //memset(romfilename,0,1024); - //memset(patchfilename,0,1024); - //memset(moviefilename,0,1024); - //memset(configfilename,0,1024); - //memset(testfilename,0,1024); - - //strcpy(romfilename,"mario"); - //strcpy(romfilename,"roms/lunar_pool.nes"); - //strcpy(romfilename,"roms/sky_destroyer.nes"); - - //add extra subsystems - emu_addsubsystem("console",console_init,console_kill); - //initialize the emulator - if(emu_init() != 0) { - log_printf("main: emu_init() failed\n"); - return(2); + if (emu_init() != 0) { + log_printf("main: emu_init() failed\n"); + return(2); } - //load rom specified by arguments - //if(strcmp(romfilename,"") != 0) { - emu_event(E_LOADROM,(void*)romfilename); - //} + int ret = nes_load((char*)romfilename); + assert(ret == 0); + nes_reset(1); + running = 1; - //load patch - //if(strcmp(patchfilename,"") != 0) { - // emu_event(E_LOADPATCH,(void*)patchfilename); - //} - - //see what we need to do with the movie - //if(strcmp(moviefilename,"") != 0) { - // if(recordmovie) { - // if(nes->cart == 0) - // log_printf("main: cannot record movie without rom loaded.\n"); - // else { - // emu_event(E_SAVEMOVIE,(void*)moviefilename); - // emu_event(E_RECORDMOVIE,0); - // nes->movie.mode |= (recordmovie > 1) ? MOVIE_TEST : 0; - // } - // } - // else { - // emu_event(E_LOADMOVIE,(void*)moviefilename); - // emu_event(E_PLAYMOVIE,0); - // } - //} - - //begin automated tests - //if(strcmp(testfilename,"") != 0) - // ret = emu_mainloop_test(testfilename); - - //or begin the main loop - //else + //begin the main loop ret = emu_mainloop(); //destroy emulator emu_kill(); //return to os - return(emu_exit(ret)); + return (emu_exit(ret)); } diff --git a/apps/nesemu2/src/system/sdl/video.c b/apps/nesemu2/src/system/sdl/video.c index a2e4fb47..372b00f3 100644 --- a/apps/nesemu2/src/system/sdl/video.c +++ b/apps/nesemu2/src/system/sdl/video.c @@ -19,44 +19,32 @@ ***************************************************************************/ #include -#include "misc/log.h" #include "palette/palette.h" #include "nes/nes.h" #include "system/system.h" #include "system/video.h" -#include "system/sdl/console/console.h" #include "misc/memutil.h" #include "misc/config.h" #include "system/common/filters.h" -//system related variables -static int screenw,screenh,screenbpp; -static int screenscale; +#define screenbpp 32 -//palette with emphasis applied -static u8 palette[8][64 * 3]; +//system related variables +static int screenw,screenh; +static int screenscale; //palette data fed to video system static u32 palette32[8][256]; //32 bit color //caches of all available colors -static u16 palettecache16[256]; static u32 palettecache32[256]; -//actual values written to nes palette ram -static u8 palettecache[32]; - //for frame limiting static int interval = 0; static u64 lasttime = 0; //pointer to scree and copy of the nes screen -static u32 *screen = 0; -static u8 *nesscreen = 0; - -//draw function pointer and pointer to current video filter -static void (*drawfunc)(void*,u32,void*,u32,u32,u32); //dest,destpitch,src,srcpitch,width,height -static filter_t *filter; +static u32 screen[256 * (240 + 16) * (screenbpp / 8) * 4] = {0}; //for correct colors //static int rshift,gshift,bshift; @@ -65,81 +53,30 @@ static filter_t *filter; #define gshift 8 #define bshift 0 -static int find_drawfunc(int scale,int bpp) -{ - int i; - - for(i=0;filter->modes[i].scale;i++) { - if(filter->modes[i].scale == scale) { - switch(bpp) { - case 32: - drawfunc = filter->modes[i].draw32; - return(0); - case 16: - case 15: - // drawfunc = filter->modes[i].draw16; - // return(0); - default: - log_printf("find_drawfunc: unsupported bit depth (%d)\n",bpp); - return(2); - } - } - } - return(1); -} - int video_init() { - if(nesscreen == 0) - nesscreen = (u8*)mem_alloc(256 * (240 + 16)); - //setup timer to limit frames interval = system_getfrequency() / 60; lasttime = system_gettick(); //clear palette caches - memset(palettecache16,0,256*sizeof(u16)); memset(palettecache32,0,256*sizeof(u32)); //set screen info screenscale = config_get_int("video.scale"); - screenbpp = 32; - //initialize the video filters filter_init(); - //get pointer to video filter - filter = filter_get((screenscale == 1) ? F_NONE : filter_get_int(config_get_string("video.filter"))); - - if(find_drawfunc(screenscale,screenbpp) != 0) { - log_printf("video_init: error finding appropriate draw func, using draw1x\n"); - filter = &filter_draw; - drawfunc = filter->modes[0].draw32; - } - - //calculate desired screen dimensions - screenw = filter->minwidth / filter->minscale * screenscale; - screenh = filter->minheight / filter->minscale * screenscale; - screenw = screen_width(); screenh = screen_height(); - //allocate memory for temp screen buffer - screen = (u32*)mem_realloc(screen,256 * (240 + 16) * (screenbpp / 8) * 4); - return(0); } void video_kill() { filter_kill(); - if(screen) - mem_free(screen); - if(nesscreen) - mem_free(nesscreen); - screen = 0; - nesscreen = 0; } int video_reinit() @@ -154,20 +91,18 @@ void video_startframe() void video_endframe() { - u64 t; - //draw everything draw_rect(screen, 0, 0, 256, 240); draw_sync(); - console_draw(screen, 256*4,screenh); +#ifdef __ISA_NATIVE__ //simple frame limiter - if(config_get_bool("video.framelimit")) { - do { - t = system_gettick(); - } while(t - lasttime < interval); - lasttime = t; - } + u64 t; + do { + t = system_gettick(); + } while(t - lasttime < interval); + lasttime = t; +#endif } //this handles lines for gui/status messages @@ -176,7 +111,6 @@ void video_updateline(int line,u8 *s) u32 *dest = screen + (line * 256); int i; - memcpy(nesscreen + (line * 256),s,256); if(line >= 8 && line < 232) { for(i=0;i<256;i++) { *dest++ = palettecache32[*s++]; @@ -194,13 +128,7 @@ void video_updatepixel(int line,int pixel,u8 s) { int offset = (line * 256) + pixel; - nesscreen[offset] = s; - if(line >= 8 && line < 232) { - screen[offset] = palettecache32[s]; - } - else { - screen[offset] = 0; - } + screen[offset] = palettecache32[s]; } //this handles palette changes from the nes engine @@ -214,7 +142,6 @@ void video_updatepalette(u8 addr,u8 data) palettecache32[addr+0xA0] = palette32[5][data]; palettecache32[addr+0xC0] = palette32[6][data]; palettecache32[addr+0xE0] = palette32[7][data]; - palettecache[addr] = data; } //must be called AFTER video_init @@ -223,14 +150,6 @@ void video_setpalette(palette_t *p) int i,j; palentry_t e; - //for(j=0;j<8;j++) { - // for(i=0;i<64;i++) { - // palette[j][(i * 3) + 0] = p->pal[j][i].r; - // palette[j][(i * 3) + 1] = p->pal[j][i].g; - // palette[j][(i * 3) + 2] = p->pal[j][i].b; - // } - //} - for(j=0;j<8;j++) { for(i=0;i<256;i++) { e.val = p->pal[j][i & 0x3F]; @@ -244,23 +163,6 @@ void video_setpalette(palette_t *p) int video_getwidth() { return(screenw); } int video_getheight() { return(screenh); } int video_getbpp() { return(screenbpp); } -u8 *video_getscreen() { return(nesscreen); } -u8 *video_getpalette() { return((u8*)palette); } - -int video_zapperhit(int x,int y) -{ - int ret = 0; - u8 *e; - u8 color; - - color = palettecache[nesscreen[x + y * 256]]; - e = &palette[(color >> 5) & 7][(color & 0x3F) * 3]; - assert(0); - ret += (int)(e[0] * 0.299f); - ret += (int)(e[1] * 0.587f); - ret += (int)(e[2] * 0.114f); - return((ret >= 0x40) ? 1 : 0); -} //kludge-city! int video_getxoffset() { return(0); } diff --git a/apps/nesemu2/src/system/system.h b/apps/nesemu2/src/system/system.h index 76b79234..f88a0b23 100644 --- a/apps/nesemu2/src/system/system.h +++ b/apps/nesemu2/src/system/system.h @@ -21,7 +21,7 @@ #ifndef __system_h__ #define __system_h__ -#include "types.h" +#include "common.h" int system_init(); void system_kill(); @@ -30,7 +30,4 @@ char *system_getcwd(); u64 system_gettick(); u64 system_getfrequency(); -//this needs to be dealt with -int system_findconfig(char *dest); - #endif diff --git a/apps/nesemu2/src/system/video.h b/apps/nesemu2/src/system/video.h index bc475ac9..9ae7345f 100644 --- a/apps/nesemu2/src/system/video.h +++ b/apps/nesemu2/src/system/video.h @@ -34,7 +34,5 @@ void video_setpalette(palette_t *p); int video_getwidth(); int video_getheight(); int video_getbpp(); -u8 *video_getscreen(); -u8 *video_getpalette(); #endif From aa39e9ac8233f4c13e8b8a3f4eba2527e167c4dc Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Fri, 29 Nov 2019 19:22:08 +0800 Subject: [PATCH 008/234] apps,nesemu2: run on riscv32-nemu * remove u64 division --- apps/nesemu2/src/common.h | 4 ++-- apps/nesemu2/src/emu/emu.c | 2 +- apps/nesemu2/src/mappers/fds/calls/vector.c | 2 -- apps/nesemu2/src/mappers/nsf/nsf.c | 4 ++-- apps/nesemu2/src/misc/config.c | 2 +- apps/nesemu2/src/system/sdl/system.c | 6 +++--- apps/nesemu2/src/system/sdl/video.c | 4 ++-- apps/nesemu2/src/system/system.h | 4 ++-- 8 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/nesemu2/src/common.h b/apps/nesemu2/src/common.h index c205e17d..79523c55 100644 --- a/apps/nesemu2/src/common.h +++ b/apps/nesemu2/src/common.h @@ -28,12 +28,12 @@ typedef int8_t s8; typedef int16_t s16; -typedef int32_t s32; +typedef int s32; typedef int64_t s64; typedef uint8_t u8; typedef uint16_t u16; -typedef uint32_t u32; +typedef unsigned u32; typedef uint64_t u64; typedef u8 (*readfunc_t)(u32); diff --git a/apps/nesemu2/src/emu/emu.c b/apps/nesemu2/src/emu/emu.c index 5984b191..04477173 100644 --- a/apps/nesemu2/src/emu/emu.c +++ b/apps/nesemu2/src/emu/emu.c @@ -106,7 +106,7 @@ int emu_mainloop() { u8 *line = (u8*)mem_alloc(512); int i,p; - u64 t,total,frames; + u32 t,total,frames; //initialize the palette in case the rom isnt loaded first for(i=0;i<512;i++) { diff --git a/apps/nesemu2/src/mappers/fds/calls/vector.c b/apps/nesemu2/src/mappers/fds/calls/vector.c index 8317c1b9..2f239387 100644 --- a/apps/nesemu2/src/mappers/fds/calls/vector.c +++ b/apps/nesemu2/src/mappers/fds/calls/vector.c @@ -23,8 +23,6 @@ #include "mappers/fds/calls.h" #include "mappers/fds/hle.h" -u32 uptime(); - //nmi vector HLECALL(nmi) { diff --git a/apps/nesemu2/src/mappers/nsf/nsf.c b/apps/nesemu2/src/mappers/nsf/nsf.c index fd12df78..15d4f999 100644 --- a/apps/nesemu2/src/mappers/nsf/nsf.c +++ b/apps/nesemu2/src/mappers/nsf/nsf.c @@ -158,14 +158,14 @@ static void write_bios(u32 addr,u8 data) //playspeed low case 0x3410: playspeed = (playspeed & 0xFF00) | data; - irqlatch = (u32)((u64)178684 * playspeed / 100000); + irqlatch = (u32)(44671u * playspeed / 25000); log_printf("irqlatch = %d (playspeed = %d)\n",irqlatch,playspeed); break; //playspeed high case 0x3411: playspeed = (playspeed & 0x00FF) | (data << 8); - irqlatch = (u32)((u64)178684 * playspeed / 100000); + irqlatch = (u32)(44671u * playspeed / 25000); log_printf("irqlatch = %d (playspeed = %d)\n",irqlatch,playspeed); break; diff --git a/apps/nesemu2/src/misc/config.c b/apps/nesemu2/src/misc/config.c index 370e38fa..ce8c6d9f 100644 --- a/apps/nesemu2/src/misc/config.c +++ b/apps/nesemu2/src/misc/config.c @@ -124,7 +124,7 @@ char *config_get_eval_string(char *dest,char *name) //see if it is missing the '%' if((p2 = strchr(p + 1,'%')) == 0) { - log_printf("config_get_eval_string: missing ending '%', just copying\n"); + log_printf("config_get_eval_string: missing ending '%%', just copying\n"); } //not missing, replace with variable data diff --git a/apps/nesemu2/src/system/sdl/system.c b/apps/nesemu2/src/system/sdl/system.c index eaf43619..56a4d84d 100644 --- a/apps/nesemu2/src/system/sdl/system.c +++ b/apps/nesemu2/src/system/sdl/system.c @@ -18,11 +18,11 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include +#include "common.h" int system_init() { return 0; } void system_kill() { } void system_checkevents() { } char *system_getcwd() { return ""; } -uint64_t system_gettick() { return uptime(); } -uint64_t system_getfrequency() { return 1000; } +u32 system_gettick() { return uptime(); } +u32 system_getfrequency() { return 1000; } diff --git a/apps/nesemu2/src/system/sdl/video.c b/apps/nesemu2/src/system/sdl/video.c index 372b00f3..f7c8391c 100644 --- a/apps/nesemu2/src/system/sdl/video.c +++ b/apps/nesemu2/src/system/sdl/video.c @@ -44,7 +44,7 @@ static int interval = 0; static u64 lasttime = 0; //pointer to scree and copy of the nes screen -static u32 screen[256 * (240 + 16) * (screenbpp / 8) * 4] = {0}; +static uint32_t screen[256 * (240 + 16) * (screenbpp / 8) * 4] = {0}; //for correct colors //static int rshift,gshift,bshift; @@ -108,7 +108,7 @@ void video_endframe() //this handles lines for gui/status messages void video_updateline(int line,u8 *s) { - u32 *dest = screen + (line * 256); + uint32_t *dest = screen + (line * 256); int i; if(line >= 8 && line < 232) { diff --git a/apps/nesemu2/src/system/system.h b/apps/nesemu2/src/system/system.h index f88a0b23..1cccf132 100644 --- a/apps/nesemu2/src/system/system.h +++ b/apps/nesemu2/src/system/system.h @@ -27,7 +27,7 @@ int system_init(); void system_kill(); void system_checkevents(); char *system_getcwd(); -u64 system_gettick(); -u64 system_getfrequency(); +u32 system_gettick(); +u32 system_getfrequency(); #endif From f26fa60f06eb24d6b26af698afb714ef3170b6f8 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Fri, 29 Nov 2019 19:32:16 +0800 Subject: [PATCH 009/234] apps,nesemu2: add README --- apps/nesemu2/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/nesemu2/README.md diff --git a/apps/nesemu2/README.md b/apps/nesemu2/README.md new file mode 100644 index 00000000..953ae86e --- /dev/null +++ b/apps/nesemu2/README.md @@ -0,0 +1,14 @@ +# nesemu2 + +Ported from https://github.com/holodnak/nesemu2. + +nesemu2 is a fully functional NES emulator, which can run more roms than LiteNES. + +Input control: +* U — SELECT +* I — START +* J — A +* k — B +* W/S/A/D — UP/DOWN/LEFT/RIGHT + +Need IOE to run. From 29c4241896d1cf55ac35b96050664fe4e790d278 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Fri, 29 Nov 2019 19:52:15 +0800 Subject: [PATCH 010/234] apps,litenes,roms: fix kungfu --- apps/litenes/src/roms/rom/kungfu.nes | Bin 24576 -> 24592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/litenes/src/roms/rom/kungfu.nes b/apps/litenes/src/roms/rom/kungfu.nes index e165bc491a36c0b4fca9a1268d2aac4b6dadd6b2..d210b5ea1d2c23fcc66df2d06ef0216de43e2dc6 100644 GIT binary patch delta 5202 zcmZ`-3tUuHw?F6148v1K0Ree!6yy==h?*oSnq*4nCS_Vyd>y^wqw+ILvs)R^Wn)=* zN48HiC9a3hLt>EOBc0+gjLZz;2?X((sEnh)_3#)#QMl`jP~W}3dl_b(z4!X>y&ik* zwf33xGFn$ghrIm4;xRN${rUImyf|&_Pa&HaR323RujRo&>&|Tl8dS~|&{CioX3|Esslv!eJ%V#M zBP!Upj40K}&N87le9KX8BZ@H!9XC#jyFrLW-&ydL9>-6`GM|~qZj+oDx>ftDy)>(XDc{)5QH6j zg0zHXmb`tZT-XX+Bli^gR11Ek7>|S}LJm$BLU9Ja95ot27vU53f=0-mX<6ex)i261K{Rq`cHuLhD)jk>BzzN|J4 zO75ls$rhfOtw!v1A}J#2*NML}!7tX3l?C|p?SlF znManWr)BO^?NoEZAWYDPXR0vHWVez$Re*DI6;=oeHiA^D#?oEHs!F1lk;zWpY0Hcd z5W;_Ds~CtPCq`JAt!B7W)q)?KD#4#!YTyJfbkHCK$|Oi8zQExXsS1rUYL2Al6${hR zZX=@4p&BFNjl4oTMyLckWQ9}Mvqqt2sPnqq8RonRZqHYoW`Zbm+4z-N?BRqnPjL`C zT~!N`;1As*%}%~9Hjy{(G+K-nLaV(3Wre~(4Gv=PutH07d9i}p;ePOiRvk*OA+M_6 zokgD$7_4IlTU5K7to%|{B@JJ&Iui? zM+4-cdP-XCB@}rTJU9+cUkRK(wxe2z zM1`coIhl?p&%1=tS;tqqT%{+hx8zxVu#{R(SjsImmP;0^rPb1BQPdo&nSQ?PeEoUb zd8RhFc0|pTnm1~uko9hF(dESA=I?dMG8j5YMGlS|q^3|Y)c65K>+xQ<1e!iXrhAMX zvFLo$sl?mMZ!I6Wd_b8(R(rfoPba^5>>XEB%SS{dNK+da-F#6Ay3fH8dDKqE^~mW2 z%Ps}(JUxeeI^s3@7P&COpB_W*jR*p&_Kf%X;X+A|X+WnE53(h-rLK_5`p6Q`SC#uk z_E++Q=VCgR40_I`emk0p&hUrNjq)tie@_mM9PhoqUhu}H1$uqc?|!Mj;9snI_6cbknc$(VgP6Q!eZXq# z&>j*tDw9qm!l-~3_t*1hlZt&IEbl4t8Tk>cQ?OOP3obZUqy!o{0J;YLjNjTJfVJ2G za0S4xj@$+y;^sB!H4skYQb8#$5ooakSnd2-m@g>A#HS4>F&;+kPq{KjEk;Af{*=pz zL2?HW9sJoCfsTPpv!87D8tpx;kzWNHajcOqm1vIt2-;EPl-E+Q!PDFS^?=`@@_odg zf?=Tn!h8asi}&*fpqMSXxb5Pqi_%4}hOmY`jlwr_%C3=#8rR~_1r08>9^m+^qCeEp zZD{H_!BtK(4G`(uP$Eh59_tI(WSC&B;cHBwt@VJAj<=xGJa)P9L7Ow?CU-$%!%WV|AWv!RUq>)XmQARqgTU9`0i=UXR;cEfIJ#y8iZ&$$$HCVrbB7I z#H10sK#N>_0-e6-a15-|Fzl$mN+5WuR(O+rr_t)h3F&gD3?cwg^)32L9GNh^7o(xq`n_R2o22Wju4TykUdn@+|{Sf@WnX8X>jw~#Hq zk#rF$@D263c1hI~-Sm3XS4~AtSDMt9r(RCE{OR#)zG^0L#g**}hOcvl;Qa0Kc8U?4 z!s~Ed_+fmQ{f!Lz`%VLZdI(T;c{`LJ)t`W>%SCQXQWX6!E=;xy18uD156iV+1$k*q zG>wmcJVrq~?Yk<+2uTYVLzj}=fY52@Z@_Lj@ujAb#WHmR(IwYlXfwKUQ~%5zuq*`= zyi*@@2k*eU(VS*X^b?2_Fm}9WGtL2aD9*tz zOj&;wSqK*xKvU%Nz+mru8+hq6!b-;S4e z3Y@P9i5|VISyZs4e>fa?*pyK`2Ceu5d^|5?BK;2eFk~E^L3W1(Lun`tNud9Y*h3b? zc7l8Mr0)6u-P0^i1r-?UE?!~w4a&M9<6gi_zx%<0V}A#^Ie@%YA2i5b1+PFzb`v8LKq=2Yye zlN?tGb`kkBY*u*6T@KB^1HNW-3ihQIzEM;QDqO+l+4#PKWJw7ZM%y-WJuJ#8<*s!d zn?T&dqeo}7=)Cww6D@jJ*YTcFO36#~U=I*B-0!7_t;o>I@fG48U^T+oooe0MvbAMf z%R=jcmVGUG=^t=&w5*{OE)+?{jLsDzVja8C3jh2XK>yHk!g{Qwv&G)RE+jvO`^1j9 zTSiF{yi1f;`V2DYVU&Dd|K5FEVcp1YG$|k*Qh1}^S_R}}2G_DFciGoSPk8LyA=JVhAYx8Mr?0{&2*T?E%THQc#E*^{I; zB8ZM4Z4p5)s%)@X3U;@Au8VL5CtvM~;a%lj8_e9`x7HbNKq)qUgXx+sUbu!gh+_c} z`4=MYo&!OWBjf2W$hVQ>7Ya5gLni*hLOO=O05TPFDP?dGC&wm)$L#xjyX1IOfIR_m z_h?)J`(fiNuD^F*8a?z>LuBFHdOA-Atr!OV^HR^W7yJ*<)?rW}$n%~D(`A8{HW5(r zkOssK2|YRtbq1jtz_~jFPi&Wwfe!Tqnth(QOql2ghbX}+_^{8nv-@mM&x7NzkVO-A zsZX`*=@x~jV_ceQr)ce!`_b<3EV?xt=-cV~BN_nq!1-AcQMz0Q8e-eXtxjO;-@&-TRk z#`IKoGGH9ZGp55P`WnX81!@kFU)ULPB?|G}|gC5*-u;)zA!=6#SO=M!+jI^#tV0ZT; zykl>a!nIKU7#!f zPkHVzJ#u7!X#eE?x&5#7FYjO7|6c!kGAe#Ky_#%{Up_AM3H*8h(m_+zL+oAG8i zTp@nzdLLt*K&|V0O#0$u==mgk=4WX?J%+5IgFLYVUZQglbg7~49Gt`c^f*sZqKk5O z^-Iku%%k+rJ=vjyI;0ToKs_W+$B^F!U7q_69cz9(%=Z-Sc?#&r9M2s(?&0IfniWBP zxo>yG{+#mDwd;XDH6Ofr?0e~6Bng@I%}8*?7}*O52q!qPOUap83w;(0IBJS|z=2ry&mO_$m~u^drUFy9No8JYE--y#(wLLX5f)^Q zGEX+oFwZl;Yd&pmHUDhBX>KroZ7w%k%$vT)8@D|HTmS$7 delta 5237 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 Ai~s-t From ed7ae6225f8e00d788bd7ca598c7eaa884041d6c Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Fri, 29 Nov 2019 19:53:30 +0800 Subject: [PATCH 011/234] apps,litenes,roms: add more roms --- apps/litenes/src/roms/rom/100in1.nes | Bin 0 -> 1048592 bytes apps/litenes/src/roms/rom/54in1.nes | Bin 0 -> 262160 bytes apps/litenes/src/roms/rom/72in1.nes | Bin 0 -> 1572880 bytes apps/litenes/src/roms/rom/castlevania.nes | Bin 0 -> 131088 bytes apps/litenes/src/roms/rom/mario3.nes | Bin 0 -> 393232 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/litenes/src/roms/rom/100in1.nes create mode 100644 apps/litenes/src/roms/rom/54in1.nes create mode 100644 apps/litenes/src/roms/rom/72in1.nes create mode 100644 apps/litenes/src/roms/rom/castlevania.nes create mode 100644 apps/litenes/src/roms/rom/mario3.nes diff --git a/apps/litenes/src/roms/rom/100in1.nes b/apps/litenes/src/roms/rom/100in1.nes new file mode 100644 index 0000000000000000000000000000000000000000..f69781ef813971eda80a85d04b3a9f56ea48554d GIT binary patch literal 1048592 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

?4VUY136 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�)%p?i-ZE@UM)#? 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�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 z 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

@qn?p(p)p9sct%gIF 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�f9e%DDqcnq*B$sBmj6?I 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%0Dk% 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 z

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%0ASXv;GJ6-o0js%)jNUrzug%FXZH(C2q>WD&U3 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≀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±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%0AE%zN)GHJ%m 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 EKk+Uw?*IS* literal 0 HcmV?d00001 diff --git a/apps/litenes/src/roms/rom/castlevania.nes b/apps/litenes/src/roms/rom/castlevania.nes new file mode 100644 index 0000000000000000000000000000000000000000..b45b006002c7b4e01103f014bd4dbaec9f4ca7ef GIT binary patch literal 131088 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 literal 0 HcmV?d00001 diff --git a/apps/litenes/src/roms/rom/mario3.nes b/apps/litenes/src/roms/rom/mario3.nes new file mode 100644 index 0000000000000000000000000000000000000000..d44538daf291ffe90da4e0592060e6ae9728b5de GIT binary patch literal 393232 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ģICk1lG0*rY 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 literal 0 HcmV?d00001 From 325e11548baaa4a924b116b4fede1cc52252b6dd Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Sat, 30 Nov 2019 00:47:19 +0800 Subject: [PATCH 012/234] apps,nesemu2: fix .gitignore under wrong directory --- apps/nesemu2/src/roms/{ => gen}/.gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/nesemu2/src/roms/{ => gen}/.gitignore (100%) diff --git a/apps/nesemu2/src/roms/.gitignore b/apps/nesemu2/src/roms/gen/.gitignore similarity index 100% rename from apps/nesemu2/src/roms/.gitignore rename to apps/nesemu2/src/roms/gen/.gitignore From 0213a56a7be071e3a44868c81aba9a62b79e4062 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Sat, 30 Nov 2019 23:28:39 +0800 Subject: [PATCH 013/234] apps: add fecux and run on native --- apps/fceux/Makefile | 12 + apps/fceux/src/asm.cpp | 529 +++ apps/fceux/src/asm.h | 2 + apps/fceux/src/boards/01-222.cpp | 111 + apps/fceux/src/boards/09-034a.cpp | 97 + apps/fceux/src/boards/103.cpp | 112 + apps/fceux/src/boards/106.cpp | 108 + apps/fceux/src/boards/108.cpp | 58 + apps/fceux/src/boards/112.cpp | 90 + apps/fceux/src/boards/116.cpp | 324 ++ apps/fceux/src/boards/117.cpp | 91 + apps/fceux/src/boards/120.cpp | 59 + apps/fceux/src/boards/121.cpp | 129 + apps/fceux/src/boards/12in1.cpp | 73 + apps/fceux/src/boards/15.cpp | 117 + apps/fceux/src/boards/151.cpp | 59 + apps/fceux/src/boards/156.cpp | 114 + apps/fceux/src/boards/158B.cpp | 71 + apps/fceux/src/boards/164.cpp | 232 + apps/fceux/src/boards/168.cpp | 78 + apps/fceux/src/boards/170.cpp | 62 + apps/fceux/src/boards/175.cpp | 79 + apps/fceux/src/boards/176.cpp | 159 + apps/fceux/src/boards/177.cpp | 81 + apps/fceux/src/boards/178.cpp | 199 + apps/fceux/src/boards/18.cpp | 134 + apps/fceux/src/boards/183.cpp | 111 + apps/fceux/src/boards/185.cpp | 107 + apps/fceux/src/boards/186.cpp | 122 + apps/fceux/src/boards/187.cpp | 84 + apps/fceux/src/boards/189.cpp | 44 + apps/fceux/src/boards/190.cpp | 85 + apps/fceux/src/boards/193.cpp | 71 + apps/fceux/src/boards/199.cpp | 97 + apps/fceux/src/boards/206.cpp | 78 + apps/fceux/src/boards/208.cpp | 78 + apps/fceux/src/boards/222.cpp | 99 + apps/fceux/src/boards/225.cpp | 86 + apps/fceux/src/boards/228.cpp | 85 + apps/fceux/src/boards/230.cpp | 78 + apps/fceux/src/boards/232.cpp | 68 + apps/fceux/src/boards/234.cpp | 79 + apps/fceux/src/boards/235.cpp | 63 + apps/fceux/src/boards/244.cpp | 77 + apps/fceux/src/boards/246.cpp | 87 + apps/fceux/src/boards/252.cpp | 135 + apps/fceux/src/boards/253.cpp | 152 + apps/fceux/src/boards/28.cpp | 215 + apps/fceux/src/boards/32.cpp | 103 + apps/fceux/src/boards/33.cpp | 117 + apps/fceux/src/boards/34.cpp | 93 + apps/fceux/src/boards/36.cpp | 69 + apps/fceux/src/boards/3d-block.cpp | 95 + apps/fceux/src/boards/40.cpp | 86 + apps/fceux/src/boards/41.cpp | 69 + apps/fceux/src/boards/411120-c.cpp | 64 + apps/fceux/src/boards/42.cpp | 84 + apps/fceux/src/boards/43.cpp | 91 + apps/fceux/src/boards/46.cpp | 69 + apps/fceux/src/boards/50.cpp | 85 + apps/fceux/src/boards/51.cpp | 84 + apps/fceux/src/boards/57.cpp | 84 + apps/fceux/src/boards/603-5052.cpp | 44 + apps/fceux/src/boards/62.cpp | 69 + apps/fceux/src/boards/65.cpp | 105 + apps/fceux/src/boards/67.cpp | 106 + apps/fceux/src/boards/68.cpp | 164 + apps/fceux/src/boards/69.cpp | 282 ++ apps/fceux/src/boards/71.cpp | 64 + apps/fceux/src/boards/72.cpp | 64 + apps/fceux/src/boards/77.cpp | 75 + apps/fceux/src/boards/79.cpp | 61 + apps/fceux/src/boards/80.cpp | 193 + apps/fceux/src/boards/80013-B.cpp | 76 + apps/fceux/src/boards/8157.cpp | 85 + apps/fceux/src/boards/82.cpp | 98 + apps/fceux/src/boards/8237.cpp | 173 + apps/fceux/src/boards/830118C.cpp | 68 + apps/fceux/src/boards/88.cpp | 83 + apps/fceux/src/boards/8in1.cpp | 67 + apps/fceux/src/boards/90.cpp | 507 +++ apps/fceux/src/boards/91.cpp | 85 + apps/fceux/src/boards/96.cpp | 70 + apps/fceux/src/boards/99.cpp | 79 + apps/fceux/src/boards/BMW8544.cpp | 79 + apps/fceux/src/boards/F-15.cpp | 62 + apps/fceux/src/boards/__dummy_mapper.cpp | 100 + apps/fceux/src/boards/a9746.cpp | 75 + apps/fceux/src/boards/ac-08.cpp | 68 + apps/fceux/src/boards/addrlatch.cpp | 523 +++ apps/fceux/src/boards/ax5705.cpp | 116 + apps/fceux/src/boards/bandai.cpp | 465 ++ apps/fceux/src/boards/bb.cpp | 63 + apps/fceux/src/boards/bmc13in1jy110.cpp | 97 + apps/fceux/src/boards/bmc42in1r.cpp | 86 + apps/fceux/src/boards/bmc64in1nr.cpp | 83 + apps/fceux/src/boards/bmc70in1.cpp | 121 + apps/fceux/src/boards/bonza.cpp | 126 + apps/fceux/src/boards/bs-5.cpp | 84 + apps/fceux/src/boards/cheapocabra.cpp | 270 ++ apps/fceux/src/boards/cityfighter.cpp | 124 + apps/fceux/src/boards/coolboy.cpp | 196 + apps/fceux/src/boards/dance2000.cpp | 110 + apps/fceux/src/boards/datalatch.cpp | 543 +++ apps/fceux/src/boards/dream.cpp | 51 + apps/fceux/src/boards/edu2000.cpp | 78 + apps/fceux/src/boards/eh8813a.cpp | 83 + apps/fceux/src/boards/emu2413.c | 1078 +++++ apps/fceux/src/boards/emu2413.h | 139 + apps/fceux/src/boards/et-100.cpp | 103 + apps/fceux/src/boards/et-4320.cpp | 119 + apps/fceux/src/boards/famicombox.cpp | 115 + apps/fceux/src/boards/ffe.cpp | 154 + apps/fceux/src/boards/fk23c.cpp | 284 ++ apps/fceux/src/boards/ghostbusters63in1.cpp | 96 + apps/fceux/src/boards/gs-2004.cpp | 63 + apps/fceux/src/boards/gs-2013.cpp | 63 + apps/fceux/src/boards/h2288.cpp | 70 + apps/fceux/src/boards/hp10xx_hp20xx.cpp | 177 + apps/fceux/src/boards/hp898f.cpp | 69 + apps/fceux/src/boards/inlnsf.cpp | 62 + apps/fceux/src/boards/karaoke.cpp | 63 + apps/fceux/src/boards/kof97.cpp | 46 + apps/fceux/src/boards/ks7010.cpp | 85 + apps/fceux/src/boards/ks7012.cpp | 84 + apps/fceux/src/boards/ks7013.cpp | 77 + apps/fceux/src/boards/ks7016.cpp | 85 + apps/fceux/src/boards/ks7017.cpp | 115 + apps/fceux/src/boards/ks7030.cpp | 140 + apps/fceux/src/boards/ks7031.cpp | 80 + apps/fceux/src/boards/ks7032.cpp | 88 + apps/fceux/src/boards/ks7037.cpp | 126 + apps/fceux/src/boards/ks7057.cpp | 92 + apps/fceux/src/boards/le05.cpp | 62 + apps/fceux/src/boards/lh32.cpp | 79 + apps/fceux/src/boards/lh53.cpp | 108 + apps/fceux/src/boards/malee.cpp | 41 + apps/fceux/src/boards/mapinc.h | 12 + apps/fceux/src/boards/mihunche.cpp | 68 + apps/fceux/src/boards/mmc1.cpp | 404 ++ apps/fceux/src/boards/mmc2and4.cpp | 138 + apps/fceux/src/boards/mmc3.cpp | 1365 ++++++ apps/fceux/src/boards/mmc3.h | 26 + apps/fceux/src/boards/mmc5.cpp | 1063 +++++ apps/fceux/src/boards/n106.cpp | 448 ++ apps/fceux/src/boards/n625092.cpp | 95 + apps/fceux/src/boards/novel.cpp | 50 + apps/fceux/src/boards/onebus.cpp | 297 ++ apps/fceux/src/boards/pec-586.cpp | 131 + apps/fceux/src/boards/rt-01.cpp | 55 + apps/fceux/src/boards/sa-9602b.cpp | 61 + apps/fceux/src/boards/sachen.cpp | 409 ++ apps/fceux/src/boards/sb-2000.cpp | 195 + apps/fceux/src/boards/sc-127.cpp | 123 + apps/fceux/src/boards/sheroes.cpp | 79 + apps/fceux/src/boards/sl1632.cpp | 107 + apps/fceux/src/boards/subor.cpp | 88 + apps/fceux/src/boards/super24.cpp | 93 + apps/fceux/src/boards/supervision.cpp | 87 + apps/fceux/src/boards/t-227-1.cpp | 100 + apps/fceux/src/boards/t-262.cpp | 73 + apps/fceux/src/boards/tengen.cpp | 139 + apps/fceux/src/boards/tf-1201.cpp | 109 + apps/fceux/src/boards/transformer.cpp | 97 + apps/fceux/src/boards/unrom512.cpp | 283 ++ apps/fceux/src/boards/vrc1.cpp | 70 + apps/fceux/src/boards/vrc2and4.cpp | 232 + apps/fceux/src/boards/vrc3.cpp | 135 + apps/fceux/src/boards/vrc5.cpp | 265 ++ apps/fceux/src/boards/vrc6.cpp | 378 ++ apps/fceux/src/boards/vrc7.cpp | 205 + apps/fceux/src/boards/vrc7p.cpp | 120 + apps/fceux/src/boards/yoko.cpp | 237 + apps/fceux/src/cart.cpp | 575 +++ apps/fceux/src/cart.h | 107 + apps/fceux/src/cheat.cpp | 904 ++++ apps/fceux/src/cheat.h | 64 + apps/fceux/src/conddebug.cpp | 523 +++ apps/fceux/src/conddebug.h | 69 + apps/fceux/src/config.cpp | 59 + apps/fceux/src/debug.cpp | 873 ++++ apps/fceux/src/debug.h | 171 + apps/fceux/src/drawing.cpp | 525 +++ apps/fceux/src/drawing.h | 6 + apps/fceux/src/driver.h | 355 ++ apps/fceux/src/drivers/common/args.cpp | 108 + apps/fceux/src/drivers/common/args.h | 12 + apps/fceux/src/drivers/common/cheat.cpp | 544 +++ apps/fceux/src/drivers/common/cheat.h | 23 + apps/fceux/src/drivers/common/config.cpp | 374 ++ apps/fceux/src/drivers/common/config.h | 38 + apps/fceux/src/drivers/common/configSys.cpp | 747 ++++ apps/fceux/src/drivers/common/configSys.h | 94 + apps/fceux/src/drivers/common/hq2x.cpp | 2991 +++++++++++++ apps/fceux/src/drivers/common/hq2x.h | 4 + apps/fceux/src/drivers/common/hq3x.cpp | 3865 +++++++++++++++++ apps/fceux/src/drivers/common/hq3x.h | 4 + apps/fceux/src/drivers/common/nes_ntsc.c | 302 ++ apps/fceux/src/drivers/common/nes_ntsc.h | 193 + .../src/drivers/common/nes_ntsc_config.h | 29 + apps/fceux/src/drivers/common/nes_ntsc_impl.h | 441 ++ apps/fceux/src/drivers/common/scale2x.cpp | 924 ++++ apps/fceux/src/drivers/common/scale2x.h | 52 + apps/fceux/src/drivers/common/scale3x.cpp | 383 ++ apps/fceux/src/drivers/common/scale3x.h | 33 + apps/fceux/src/drivers/common/scalebit.cpp | 387 ++ apps/fceux/src/drivers/common/scalebit.h | 43 + apps/fceux/src/drivers/common/vidblit.cpp | 982 +++++ apps/fceux/src/drivers/common/vidblit.h | 32 + apps/fceux/src/drivers/sdl/config.cpp | 475 ++ apps/fceux/src/drivers/sdl/config.h | 28 + apps/fceux/src/drivers/sdl/dface.h | 37 + apps/fceux/src/drivers/sdl/gui.h | 88 + apps/fceux/src/drivers/sdl/icon.xpm | 55 + apps/fceux/src/drivers/sdl/input.cpp | 2450 +++++++++++ apps/fceux/src/drivers/sdl/input.h | 60 + apps/fceux/src/drivers/sdl/keyscan.h | 48 + apps/fceux/src/drivers/sdl/main.h | 100 + apps/fceux/src/drivers/sdl/sdl-icon.h | 134 + apps/fceux/src/drivers/sdl/sdl-joystick.cpp | 123 + apps/fceux/src/drivers/sdl/sdl-opengl.cpp | 251 ++ apps/fceux/src/drivers/sdl/sdl-opengl.h | 6 + apps/fceux/src/drivers/sdl/sdl-sound.cpp | 272 ++ apps/fceux/src/drivers/sdl/sdl-throttle.cpp | 150 + apps/fceux/src/drivers/sdl/sdl-video.cpp | 861 ++++ apps/fceux/src/drivers/sdl/sdl-video.h | 17 + apps/fceux/src/drivers/sdl/sdl.cpp | 1043 +++++ apps/fceux/src/drivers/sdl/sdl.h | 32 + apps/fceux/src/drivers/sdl/throttle.h | 2 + apps/fceux/src/drivers/sdl/unix-netplay.cpp | 360 ++ apps/fceux/src/drivers/sdl/unix-netplay.h | 6 + .../src/drivers/videolog/nesvideos-piece.cpp | 1249 ++++++ .../src/drivers/videolog/nesvideos-piece.h | 50 + apps/fceux/src/drivers/videolog/quantize.h | 185 + apps/fceux/src/drivers/videolog/rgbtorgb.cpp | 1111 +++++ apps/fceux/src/drivers/videolog/rgbtorgb.h | 68 + apps/fceux/src/drivers/videolog/simd.h | 365 ++ apps/fceux/src/emufile.cpp | 279 ++ apps/fceux/src/emufile.h | 346 ++ apps/fceux/src/emufile_types.h | 16 + apps/fceux/src/fceu.cpp | 1386 ++++++ apps/fceux/src/fceu.h | 176 + apps/fceux/src/fceulua.h | 91 + apps/fceux/src/fcoeffs.h | 1585 +++++++ apps/fceux/src/fds.cpp | 810 ++++ apps/fceux/src/fds.h | 6 + apps/fceux/src/file.cpp | 806 ++++ apps/fceux/src/file.h | 170 + apps/fceux/src/filter.cpp | 209 + apps/fceux/src/filter.h | 3 + apps/fceux/src/fir/README | 1 + apps/fceux/src/fir/c44100ntsc.coef | 1039 +++++ apps/fceux/src/fir/c44100ntsc.h | 512 +++ apps/fceux/src/fir/c44100ntsc.scm | 12 + apps/fceux/src/fir/c44100pal.coef | 1039 +++++ apps/fceux/src/fir/c44100pal.h | 512 +++ apps/fceux/src/fir/c44100pal.scm | 12 + apps/fceux/src/fir/c48000ntsc.coef | 1039 +++++ apps/fceux/src/fir/c48000ntsc.h | 512 +++ apps/fceux/src/fir/c48000ntsc.scm | 12 + apps/fceux/src/fir/c48000pal.coef | 1039 +++++ apps/fceux/src/fir/c48000pal.h | 512 +++ apps/fceux/src/fir/c48000pal.scm | 12 + apps/fceux/src/fir/c96000ntsc.coef | 1039 +++++ apps/fceux/src/fir/c96000ntsc.h | 512 +++ apps/fceux/src/fir/c96000ntsc.scm | 12 + apps/fceux/src/fir/c96000pal.coef | 1039 +++++ apps/fceux/src/fir/c96000pal.h | 512 +++ apps/fceux/src/fir/c96000pal.scm | 12 + apps/fceux/src/git.h | 144 + apps/fceux/src/ines-bad.h | 41 + apps/fceux/src/ines-correct.h | 284 ++ apps/fceux/src/ines.cpp | 1044 +++++ apps/fceux/src/ines.h | 276 ++ apps/fceux/src/input.cpp | 1320 ++++++ apps/fceux/src/input.h | 310 ++ apps/fceux/src/input/arkanoid.cpp | 119 + apps/fceux/src/input/bworld.cpp | 74 + apps/fceux/src/input/cursor.cpp | 86 + apps/fceux/src/input/fkb.cpp | 89 + apps/fceux/src/input/fkb.h | 72 + apps/fceux/src/input/ftrainer.cpp | 74 + apps/fceux/src/input/hypershot.cpp | 50 + apps/fceux/src/input/mahjong.cpp | 79 + apps/fceux/src/input/mouse.cpp | 83 + apps/fceux/src/input/oekakids.cpp | 95 + apps/fceux/src/input/pec586kb.cpp | 96 + apps/fceux/src/input/powerpad.cpp | 85 + apps/fceux/src/input/quiz.cpp | 75 + apps/fceux/src/input/shadow.cpp | 150 + apps/fceux/src/input/share.h | 9 + apps/fceux/src/input/snesmouse.cpp | 115 + apps/fceux/src/input/suborkb.cpp | 84 + apps/fceux/src/input/suborkb.h | 102 + apps/fceux/src/input/toprider.cpp | 62 + apps/fceux/src/input/zapper.cpp | 224 + apps/fceux/src/input/zapper.h | 16 + apps/fceux/src/movie.cpp | 2081 +++++++++ apps/fceux/src/movie.h | 316 ++ apps/fceux/src/netplay.cpp | 326 ++ apps/fceux/src/netplay.h | 23 + apps/fceux/src/nsf.cpp | 651 +++ apps/fceux/src/nsf.h | 55 + apps/fceux/src/oldmovie.cpp | 677 +++ apps/fceux/src/oldmovie.h | 30 + apps/fceux/src/ops.inc | 486 +++ apps/fceux/src/palette.cpp | 652 +++ apps/fceux/src/palette.h | 11 + apps/fceux/src/palettes/palettes.h | 325 ++ apps/fceux/src/palettes/rp2c04001.h | 64 + apps/fceux/src/palettes/rp2c04002.h | 64 + apps/fceux/src/palettes/rp2c04003.h | 64 + apps/fceux/src/palettes/rp2c05004.h | 64 + apps/fceux/src/ppu.cpp | 2460 +++++++++++ apps/fceux/src/ppu.h | 50 + apps/fceux/src/pputile.inc | 146 + apps/fceux/src/sound.cpp | 1383 ++++++ apps/fceux/src/sound.h | 85 + apps/fceux/src/state.cpp | 1000 +++++ apps/fceux/src/state.h | 79 + apps/fceux/src/types-des.h | 458 ++ apps/fceux/src/types.h | 144 + apps/fceux/src/unif.cpp | 633 +++ apps/fceux/src/unif.h | 164 + apps/fceux/src/utils/ConvertUTF.c | 539 +++ apps/fceux/src/utils/ConvertUTF.h | 149 + apps/fceux/src/utils/backward.cpp | 33 + apps/fceux/src/utils/backward.hpp | 2196 ++++++++++ apps/fceux/src/utils/crc32.h | 1 + apps/fceux/src/utils/endian.cpp | 313 ++ apps/fceux/src/utils/endian.h | 111 + apps/fceux/src/utils/general.cpp | 39 + apps/fceux/src/utils/general.h | 3 + apps/fceux/src/utils/guid.cpp | 47 + apps/fceux/src/utils/guid.h | 18 + apps/fceux/src/utils/ioapi.cpp | 246 ++ apps/fceux/src/utils/ioapi.h | 208 + apps/fceux/src/utils/md5.cpp | 244 ++ apps/fceux/src/utils/md5.h | 23 + apps/fceux/src/utils/memory.cpp | 80 + apps/fceux/src/utils/memory.h | 36 + apps/fceux/src/utils/unzip.h | 437 ++ apps/fceux/src/utils/valuearray.h | 21 + apps/fceux/src/utils/xstring.cpp | 793 ++++ apps/fceux/src/utils/xstring.h | 132 + apps/fceux/src/version.h | 67 + apps/fceux/src/video.cpp | 535 +++ apps/fceux/src/video.h | 42 + apps/fceux/src/vsuni.cpp | 376 ++ apps/fceux/src/vsuni.h | 7 + apps/fceux/src/wave.cpp | 126 + apps/fceux/src/wave.h | 4 + apps/fceux/src/x6502.cpp | 632 +++ apps/fceux/src/x6502.h | 94 + apps/fceux/src/x6502abbrev.h | 20 + apps/fceux/src/x6502struct.h | 27 + 356 files changed, 91567 insertions(+) create mode 100644 apps/fceux/Makefile create mode 100644 apps/fceux/src/asm.cpp create mode 100644 apps/fceux/src/asm.h create mode 100644 apps/fceux/src/boards/01-222.cpp create mode 100644 apps/fceux/src/boards/09-034a.cpp create mode 100644 apps/fceux/src/boards/103.cpp create mode 100644 apps/fceux/src/boards/106.cpp create mode 100644 apps/fceux/src/boards/108.cpp create mode 100644 apps/fceux/src/boards/112.cpp create mode 100644 apps/fceux/src/boards/116.cpp create mode 100644 apps/fceux/src/boards/117.cpp create mode 100644 apps/fceux/src/boards/120.cpp create mode 100644 apps/fceux/src/boards/121.cpp create mode 100644 apps/fceux/src/boards/12in1.cpp create mode 100644 apps/fceux/src/boards/15.cpp create mode 100644 apps/fceux/src/boards/151.cpp create mode 100644 apps/fceux/src/boards/156.cpp create mode 100644 apps/fceux/src/boards/158B.cpp create mode 100644 apps/fceux/src/boards/164.cpp create mode 100644 apps/fceux/src/boards/168.cpp create mode 100644 apps/fceux/src/boards/170.cpp create mode 100644 apps/fceux/src/boards/175.cpp create mode 100644 apps/fceux/src/boards/176.cpp create mode 100644 apps/fceux/src/boards/177.cpp create mode 100644 apps/fceux/src/boards/178.cpp create mode 100644 apps/fceux/src/boards/18.cpp create mode 100644 apps/fceux/src/boards/183.cpp create mode 100644 apps/fceux/src/boards/185.cpp create mode 100644 apps/fceux/src/boards/186.cpp create mode 100644 apps/fceux/src/boards/187.cpp create mode 100644 apps/fceux/src/boards/189.cpp create mode 100644 apps/fceux/src/boards/190.cpp create mode 100644 apps/fceux/src/boards/193.cpp create mode 100644 apps/fceux/src/boards/199.cpp create mode 100644 apps/fceux/src/boards/206.cpp create mode 100644 apps/fceux/src/boards/208.cpp create mode 100644 apps/fceux/src/boards/222.cpp create mode 100644 apps/fceux/src/boards/225.cpp create mode 100644 apps/fceux/src/boards/228.cpp create mode 100644 apps/fceux/src/boards/230.cpp create mode 100644 apps/fceux/src/boards/232.cpp create mode 100644 apps/fceux/src/boards/234.cpp create mode 100644 apps/fceux/src/boards/235.cpp create mode 100644 apps/fceux/src/boards/244.cpp create mode 100644 apps/fceux/src/boards/246.cpp create mode 100644 apps/fceux/src/boards/252.cpp create mode 100644 apps/fceux/src/boards/253.cpp create mode 100644 apps/fceux/src/boards/28.cpp create mode 100644 apps/fceux/src/boards/32.cpp create mode 100644 apps/fceux/src/boards/33.cpp create mode 100644 apps/fceux/src/boards/34.cpp create mode 100644 apps/fceux/src/boards/36.cpp create mode 100644 apps/fceux/src/boards/3d-block.cpp create mode 100644 apps/fceux/src/boards/40.cpp create mode 100644 apps/fceux/src/boards/41.cpp create mode 100644 apps/fceux/src/boards/411120-c.cpp create mode 100644 apps/fceux/src/boards/42.cpp create mode 100644 apps/fceux/src/boards/43.cpp create mode 100644 apps/fceux/src/boards/46.cpp create mode 100644 apps/fceux/src/boards/50.cpp create mode 100644 apps/fceux/src/boards/51.cpp create mode 100644 apps/fceux/src/boards/57.cpp create mode 100644 apps/fceux/src/boards/603-5052.cpp create mode 100644 apps/fceux/src/boards/62.cpp create mode 100644 apps/fceux/src/boards/65.cpp create mode 100644 apps/fceux/src/boards/67.cpp create mode 100644 apps/fceux/src/boards/68.cpp create mode 100644 apps/fceux/src/boards/69.cpp create mode 100644 apps/fceux/src/boards/71.cpp create mode 100644 apps/fceux/src/boards/72.cpp create mode 100644 apps/fceux/src/boards/77.cpp create mode 100644 apps/fceux/src/boards/79.cpp create mode 100644 apps/fceux/src/boards/80.cpp create mode 100644 apps/fceux/src/boards/80013-B.cpp create mode 100644 apps/fceux/src/boards/8157.cpp create mode 100644 apps/fceux/src/boards/82.cpp create mode 100644 apps/fceux/src/boards/8237.cpp create mode 100644 apps/fceux/src/boards/830118C.cpp create mode 100644 apps/fceux/src/boards/88.cpp create mode 100644 apps/fceux/src/boards/8in1.cpp create mode 100644 apps/fceux/src/boards/90.cpp create mode 100644 apps/fceux/src/boards/91.cpp create mode 100644 apps/fceux/src/boards/96.cpp create mode 100644 apps/fceux/src/boards/99.cpp create mode 100644 apps/fceux/src/boards/BMW8544.cpp create mode 100644 apps/fceux/src/boards/F-15.cpp create mode 100644 apps/fceux/src/boards/__dummy_mapper.cpp create mode 100644 apps/fceux/src/boards/a9746.cpp create mode 100644 apps/fceux/src/boards/ac-08.cpp create mode 100644 apps/fceux/src/boards/addrlatch.cpp create mode 100644 apps/fceux/src/boards/ax5705.cpp create mode 100644 apps/fceux/src/boards/bandai.cpp create mode 100644 apps/fceux/src/boards/bb.cpp create mode 100644 apps/fceux/src/boards/bmc13in1jy110.cpp create mode 100644 apps/fceux/src/boards/bmc42in1r.cpp create mode 100644 apps/fceux/src/boards/bmc64in1nr.cpp create mode 100644 apps/fceux/src/boards/bmc70in1.cpp create mode 100644 apps/fceux/src/boards/bonza.cpp create mode 100644 apps/fceux/src/boards/bs-5.cpp create mode 100644 apps/fceux/src/boards/cheapocabra.cpp create mode 100644 apps/fceux/src/boards/cityfighter.cpp create mode 100644 apps/fceux/src/boards/coolboy.cpp create mode 100644 apps/fceux/src/boards/dance2000.cpp create mode 100644 apps/fceux/src/boards/datalatch.cpp create mode 100644 apps/fceux/src/boards/dream.cpp create mode 100644 apps/fceux/src/boards/edu2000.cpp create mode 100644 apps/fceux/src/boards/eh8813a.cpp create mode 100644 apps/fceux/src/boards/emu2413.c create mode 100644 apps/fceux/src/boards/emu2413.h create mode 100644 apps/fceux/src/boards/et-100.cpp create mode 100644 apps/fceux/src/boards/et-4320.cpp create mode 100644 apps/fceux/src/boards/famicombox.cpp create mode 100644 apps/fceux/src/boards/ffe.cpp create mode 100644 apps/fceux/src/boards/fk23c.cpp create mode 100644 apps/fceux/src/boards/ghostbusters63in1.cpp create mode 100644 apps/fceux/src/boards/gs-2004.cpp create mode 100644 apps/fceux/src/boards/gs-2013.cpp create mode 100644 apps/fceux/src/boards/h2288.cpp create mode 100644 apps/fceux/src/boards/hp10xx_hp20xx.cpp create mode 100644 apps/fceux/src/boards/hp898f.cpp create mode 100644 apps/fceux/src/boards/inlnsf.cpp create mode 100644 apps/fceux/src/boards/karaoke.cpp create mode 100644 apps/fceux/src/boards/kof97.cpp create mode 100644 apps/fceux/src/boards/ks7010.cpp create mode 100644 apps/fceux/src/boards/ks7012.cpp create mode 100644 apps/fceux/src/boards/ks7013.cpp create mode 100644 apps/fceux/src/boards/ks7016.cpp create mode 100644 apps/fceux/src/boards/ks7017.cpp create mode 100644 apps/fceux/src/boards/ks7030.cpp create mode 100644 apps/fceux/src/boards/ks7031.cpp create mode 100644 apps/fceux/src/boards/ks7032.cpp create mode 100644 apps/fceux/src/boards/ks7037.cpp create mode 100644 apps/fceux/src/boards/ks7057.cpp create mode 100644 apps/fceux/src/boards/le05.cpp create mode 100644 apps/fceux/src/boards/lh32.cpp create mode 100644 apps/fceux/src/boards/lh53.cpp create mode 100644 apps/fceux/src/boards/malee.cpp create mode 100644 apps/fceux/src/boards/mapinc.h create mode 100644 apps/fceux/src/boards/mihunche.cpp create mode 100644 apps/fceux/src/boards/mmc1.cpp create mode 100644 apps/fceux/src/boards/mmc2and4.cpp create mode 100644 apps/fceux/src/boards/mmc3.cpp create mode 100644 apps/fceux/src/boards/mmc3.h create mode 100644 apps/fceux/src/boards/mmc5.cpp create mode 100644 apps/fceux/src/boards/n106.cpp create mode 100644 apps/fceux/src/boards/n625092.cpp create mode 100644 apps/fceux/src/boards/novel.cpp create mode 100644 apps/fceux/src/boards/onebus.cpp create mode 100644 apps/fceux/src/boards/pec-586.cpp create mode 100644 apps/fceux/src/boards/rt-01.cpp create mode 100644 apps/fceux/src/boards/sa-9602b.cpp create mode 100644 apps/fceux/src/boards/sachen.cpp create mode 100644 apps/fceux/src/boards/sb-2000.cpp create mode 100644 apps/fceux/src/boards/sc-127.cpp create mode 100644 apps/fceux/src/boards/sheroes.cpp create mode 100644 apps/fceux/src/boards/sl1632.cpp create mode 100644 apps/fceux/src/boards/subor.cpp create mode 100644 apps/fceux/src/boards/super24.cpp create mode 100644 apps/fceux/src/boards/supervision.cpp create mode 100644 apps/fceux/src/boards/t-227-1.cpp create mode 100644 apps/fceux/src/boards/t-262.cpp create mode 100644 apps/fceux/src/boards/tengen.cpp create mode 100644 apps/fceux/src/boards/tf-1201.cpp create mode 100644 apps/fceux/src/boards/transformer.cpp create mode 100644 apps/fceux/src/boards/unrom512.cpp create mode 100644 apps/fceux/src/boards/vrc1.cpp create mode 100644 apps/fceux/src/boards/vrc2and4.cpp create mode 100644 apps/fceux/src/boards/vrc3.cpp create mode 100644 apps/fceux/src/boards/vrc5.cpp create mode 100644 apps/fceux/src/boards/vrc6.cpp create mode 100644 apps/fceux/src/boards/vrc7.cpp create mode 100644 apps/fceux/src/boards/vrc7p.cpp create mode 100644 apps/fceux/src/boards/yoko.cpp create mode 100644 apps/fceux/src/cart.cpp create mode 100644 apps/fceux/src/cart.h create mode 100644 apps/fceux/src/cheat.cpp create mode 100644 apps/fceux/src/cheat.h create mode 100644 apps/fceux/src/conddebug.cpp create mode 100644 apps/fceux/src/conddebug.h create mode 100644 apps/fceux/src/config.cpp create mode 100644 apps/fceux/src/debug.cpp create mode 100644 apps/fceux/src/debug.h create mode 100644 apps/fceux/src/drawing.cpp create mode 100644 apps/fceux/src/drawing.h create mode 100644 apps/fceux/src/driver.h create mode 100644 apps/fceux/src/drivers/common/args.cpp create mode 100644 apps/fceux/src/drivers/common/args.h create mode 100644 apps/fceux/src/drivers/common/cheat.cpp create mode 100644 apps/fceux/src/drivers/common/cheat.h create mode 100644 apps/fceux/src/drivers/common/config.cpp create mode 100644 apps/fceux/src/drivers/common/config.h create mode 100644 apps/fceux/src/drivers/common/configSys.cpp create mode 100644 apps/fceux/src/drivers/common/configSys.h create mode 100644 apps/fceux/src/drivers/common/hq2x.cpp create mode 100644 apps/fceux/src/drivers/common/hq2x.h create mode 100644 apps/fceux/src/drivers/common/hq3x.cpp create mode 100644 apps/fceux/src/drivers/common/hq3x.h create mode 100644 apps/fceux/src/drivers/common/nes_ntsc.c create mode 100644 apps/fceux/src/drivers/common/nes_ntsc.h create mode 100644 apps/fceux/src/drivers/common/nes_ntsc_config.h create mode 100644 apps/fceux/src/drivers/common/nes_ntsc_impl.h create mode 100644 apps/fceux/src/drivers/common/scale2x.cpp create mode 100644 apps/fceux/src/drivers/common/scale2x.h create mode 100644 apps/fceux/src/drivers/common/scale3x.cpp create mode 100644 apps/fceux/src/drivers/common/scale3x.h create mode 100644 apps/fceux/src/drivers/common/scalebit.cpp create mode 100644 apps/fceux/src/drivers/common/scalebit.h create mode 100644 apps/fceux/src/drivers/common/vidblit.cpp create mode 100644 apps/fceux/src/drivers/common/vidblit.h create mode 100644 apps/fceux/src/drivers/sdl/config.cpp create mode 100644 apps/fceux/src/drivers/sdl/config.h create mode 100644 apps/fceux/src/drivers/sdl/dface.h create mode 100644 apps/fceux/src/drivers/sdl/gui.h create mode 100644 apps/fceux/src/drivers/sdl/icon.xpm create mode 100644 apps/fceux/src/drivers/sdl/input.cpp create mode 100644 apps/fceux/src/drivers/sdl/input.h create mode 100644 apps/fceux/src/drivers/sdl/keyscan.h create mode 100644 apps/fceux/src/drivers/sdl/main.h create mode 100644 apps/fceux/src/drivers/sdl/sdl-icon.h create mode 100644 apps/fceux/src/drivers/sdl/sdl-joystick.cpp create mode 100644 apps/fceux/src/drivers/sdl/sdl-opengl.cpp create mode 100644 apps/fceux/src/drivers/sdl/sdl-opengl.h create mode 100644 apps/fceux/src/drivers/sdl/sdl-sound.cpp create mode 100644 apps/fceux/src/drivers/sdl/sdl-throttle.cpp create mode 100644 apps/fceux/src/drivers/sdl/sdl-video.cpp create mode 100644 apps/fceux/src/drivers/sdl/sdl-video.h create mode 100644 apps/fceux/src/drivers/sdl/sdl.cpp create mode 100644 apps/fceux/src/drivers/sdl/sdl.h create mode 100644 apps/fceux/src/drivers/sdl/throttle.h create mode 100644 apps/fceux/src/drivers/sdl/unix-netplay.cpp create mode 100644 apps/fceux/src/drivers/sdl/unix-netplay.h create mode 100644 apps/fceux/src/drivers/videolog/nesvideos-piece.cpp create mode 100644 apps/fceux/src/drivers/videolog/nesvideos-piece.h create mode 100644 apps/fceux/src/drivers/videolog/quantize.h create mode 100644 apps/fceux/src/drivers/videolog/rgbtorgb.cpp create mode 100644 apps/fceux/src/drivers/videolog/rgbtorgb.h create mode 100644 apps/fceux/src/drivers/videolog/simd.h create mode 100644 apps/fceux/src/emufile.cpp create mode 100644 apps/fceux/src/emufile.h create mode 100644 apps/fceux/src/emufile_types.h create mode 100644 apps/fceux/src/fceu.cpp create mode 100644 apps/fceux/src/fceu.h create mode 100644 apps/fceux/src/fceulua.h create mode 100644 apps/fceux/src/fcoeffs.h create mode 100644 apps/fceux/src/fds.cpp create mode 100644 apps/fceux/src/fds.h create mode 100644 apps/fceux/src/file.cpp create mode 100644 apps/fceux/src/file.h create mode 100644 apps/fceux/src/filter.cpp create mode 100644 apps/fceux/src/filter.h create mode 100644 apps/fceux/src/fir/README create mode 100644 apps/fceux/src/fir/c44100ntsc.coef create mode 100644 apps/fceux/src/fir/c44100ntsc.h create mode 100644 apps/fceux/src/fir/c44100ntsc.scm create mode 100644 apps/fceux/src/fir/c44100pal.coef create mode 100644 apps/fceux/src/fir/c44100pal.h create mode 100644 apps/fceux/src/fir/c44100pal.scm create mode 100644 apps/fceux/src/fir/c48000ntsc.coef create mode 100644 apps/fceux/src/fir/c48000ntsc.h create mode 100644 apps/fceux/src/fir/c48000ntsc.scm create mode 100644 apps/fceux/src/fir/c48000pal.coef create mode 100644 apps/fceux/src/fir/c48000pal.h create mode 100644 apps/fceux/src/fir/c48000pal.scm create mode 100644 apps/fceux/src/fir/c96000ntsc.coef create mode 100644 apps/fceux/src/fir/c96000ntsc.h create mode 100644 apps/fceux/src/fir/c96000ntsc.scm create mode 100644 apps/fceux/src/fir/c96000pal.coef create mode 100644 apps/fceux/src/fir/c96000pal.h create mode 100644 apps/fceux/src/fir/c96000pal.scm create mode 100644 apps/fceux/src/git.h create mode 100644 apps/fceux/src/ines-bad.h create mode 100644 apps/fceux/src/ines-correct.h create mode 100644 apps/fceux/src/ines.cpp create mode 100644 apps/fceux/src/ines.h create mode 100644 apps/fceux/src/input.cpp create mode 100644 apps/fceux/src/input.h create mode 100644 apps/fceux/src/input/arkanoid.cpp create mode 100644 apps/fceux/src/input/bworld.cpp create mode 100644 apps/fceux/src/input/cursor.cpp create mode 100644 apps/fceux/src/input/fkb.cpp create mode 100644 apps/fceux/src/input/fkb.h create mode 100644 apps/fceux/src/input/ftrainer.cpp create mode 100644 apps/fceux/src/input/hypershot.cpp create mode 100644 apps/fceux/src/input/mahjong.cpp create mode 100644 apps/fceux/src/input/mouse.cpp create mode 100644 apps/fceux/src/input/oekakids.cpp create mode 100644 apps/fceux/src/input/pec586kb.cpp create mode 100644 apps/fceux/src/input/powerpad.cpp create mode 100644 apps/fceux/src/input/quiz.cpp create mode 100644 apps/fceux/src/input/shadow.cpp create mode 100644 apps/fceux/src/input/share.h create mode 100644 apps/fceux/src/input/snesmouse.cpp create mode 100644 apps/fceux/src/input/suborkb.cpp create mode 100644 apps/fceux/src/input/suborkb.h create mode 100644 apps/fceux/src/input/toprider.cpp create mode 100644 apps/fceux/src/input/zapper.cpp create mode 100644 apps/fceux/src/input/zapper.h create mode 100644 apps/fceux/src/movie.cpp create mode 100644 apps/fceux/src/movie.h create mode 100644 apps/fceux/src/netplay.cpp create mode 100644 apps/fceux/src/netplay.h create mode 100644 apps/fceux/src/nsf.cpp create mode 100644 apps/fceux/src/nsf.h create mode 100644 apps/fceux/src/oldmovie.cpp create mode 100644 apps/fceux/src/oldmovie.h create mode 100644 apps/fceux/src/ops.inc create mode 100644 apps/fceux/src/palette.cpp create mode 100644 apps/fceux/src/palette.h create mode 100644 apps/fceux/src/palettes/palettes.h create mode 100644 apps/fceux/src/palettes/rp2c04001.h create mode 100644 apps/fceux/src/palettes/rp2c04002.h create mode 100644 apps/fceux/src/palettes/rp2c04003.h create mode 100644 apps/fceux/src/palettes/rp2c05004.h create mode 100644 apps/fceux/src/ppu.cpp create mode 100644 apps/fceux/src/ppu.h create mode 100644 apps/fceux/src/pputile.inc create mode 100644 apps/fceux/src/sound.cpp create mode 100644 apps/fceux/src/sound.h create mode 100644 apps/fceux/src/state.cpp create mode 100644 apps/fceux/src/state.h create mode 100644 apps/fceux/src/types-des.h create mode 100644 apps/fceux/src/types.h create mode 100644 apps/fceux/src/unif.cpp create mode 100644 apps/fceux/src/unif.h create mode 100644 apps/fceux/src/utils/ConvertUTF.c create mode 100644 apps/fceux/src/utils/ConvertUTF.h create mode 100644 apps/fceux/src/utils/backward.cpp create mode 100644 apps/fceux/src/utils/backward.hpp create mode 100644 apps/fceux/src/utils/crc32.h create mode 100644 apps/fceux/src/utils/endian.cpp create mode 100644 apps/fceux/src/utils/endian.h create mode 100644 apps/fceux/src/utils/general.cpp create mode 100644 apps/fceux/src/utils/general.h create mode 100644 apps/fceux/src/utils/guid.cpp create mode 100644 apps/fceux/src/utils/guid.h create mode 100644 apps/fceux/src/utils/ioapi.cpp create mode 100644 apps/fceux/src/utils/ioapi.h create mode 100644 apps/fceux/src/utils/md5.cpp create mode 100644 apps/fceux/src/utils/md5.h create mode 100644 apps/fceux/src/utils/memory.cpp create mode 100644 apps/fceux/src/utils/memory.h create mode 100644 apps/fceux/src/utils/unzip.h create mode 100644 apps/fceux/src/utils/valuearray.h create mode 100644 apps/fceux/src/utils/xstring.cpp create mode 100644 apps/fceux/src/utils/xstring.h create mode 100644 apps/fceux/src/version.h create mode 100644 apps/fceux/src/video.cpp create mode 100644 apps/fceux/src/video.h create mode 100644 apps/fceux/src/vsuni.cpp create mode 100644 apps/fceux/src/vsuni.h create mode 100644 apps/fceux/src/wave.cpp create mode 100644 apps/fceux/src/wave.h create mode 100644 apps/fceux/src/x6502.cpp create mode 100644 apps/fceux/src/x6502.h create mode 100644 apps/fceux/src/x6502abbrev.h create mode 100644 apps/fceux/src/x6502struct.h diff --git a/apps/fceux/Makefile b/apps/fceux/Makefile new file mode 100644 index 00000000..bcacc8da --- /dev/null +++ b/apps/fceux/Makefile @@ -0,0 +1,12 @@ +NAME := fceux +SRCS := $(shell find -L ./src/ -name "*.c" -o -name "*.cpp") +ROMS := $(shell find -L ./src/ -name "*.nes") + +CFLAGS += -DPSS_STYLE=1 +LDFLAGS += -lSDL +#PREBUILD := rom +include $(AM_HOME)/Makefile.app + +.PHONY: rom +#rom: $(ROMS) +# @cd src/roms && python3 build-roms.py diff --git a/apps/fceux/src/asm.cpp b/apps/fceux/src/asm.cpp new file mode 100644 index 00000000..5b2f5028 --- /dev/null +++ b/apps/fceux/src/asm.cpp @@ -0,0 +1,529 @@ +/// \file +/// \brief 6502 assembler and disassembler + +#include "types.h" +#include "utils/xstring.h" +#include "debug.h" +#include "asm.h" +#include "x6502.h" + +#include +#include +#include +///assembles the string to an instruction located at addr, storing opcodes in output buffer +int Assemble(unsigned char *output, int addr, char *str) { + //unsigned char opcode[3] = { 0,0,0 }; + output[0] = output[1] = output[2] = 0; + char astr[128],ins[4]; + int len = strlen(str); + if ((!len) || (len > 0x127)) return 1; + + strcpy(astr,str); + str_ucase(astr); + sscanf(astr,"%3s",ins); //get instruction + if (strlen(ins) != 3) return 1; + strcpy(astr,strstr(astr,ins)+3); //heheh, this is probably a bad idea, but let's do it anyway! + if ((astr[0] != ' ') && (astr[0] != 0)) return 1; + + //remove all whitespace + str_strip(astr,STRIP_SP|STRIP_TAB|STRIP_CR|STRIP_LF); + + //repair syntax + chr_replace(astr,'[','('); //brackets + chr_replace(astr,']',')'); + chr_replace(astr,'{','('); + chr_replace(astr,'}',')'); + chr_replace(astr,';',0); //comments + str_replace(astr,"0X","$"); //miscellaneous + + //This does the following: + // 1) Sets opcode[0] on success, else returns 1. + // 2) Parses text in *astr to build the rest of the assembled + // data in 'opcode', else returns 1 on error. + + if (!strlen(astr)) { + //Implied instructions + if (!strcmp(ins,"BRK")) output[0] = 0x00; + else if (!strcmp(ins,"PHP")) output[0] = 0x08; + else if (!strcmp(ins,"ASL")) output[0] = 0x0A; + else if (!strcmp(ins,"CLC")) output[0] = 0x18; + else if (!strcmp(ins,"PLP")) output[0] = 0x28; + else if (!strcmp(ins,"ROL")) output[0] = 0x2A; + else if (!strcmp(ins,"SEC")) output[0] = 0x38; + else if (!strcmp(ins,"RTI")) output[0] = 0x40; + else if (!strcmp(ins,"PHA")) output[0] = 0x48; + else if (!strcmp(ins,"LSR")) output[0] = 0x4A; + else if (!strcmp(ins,"CLI")) output[0] = 0x58; + else if (!strcmp(ins,"RTS")) output[0] = 0x60; + else if (!strcmp(ins,"PLA")) output[0] = 0x68; + else if (!strcmp(ins,"ROR")) output[0] = 0x6A; + else if (!strcmp(ins,"SEI")) output[0] = 0x78; + else if (!strcmp(ins,"DEY")) output[0] = 0x88; + else if (!strcmp(ins,"TXA")) output[0] = 0x8A; + else if (!strcmp(ins,"TYA")) output[0] = 0x98; + else if (!strcmp(ins,"TXS")) output[0] = 0x9A; + else if (!strcmp(ins,"TAY")) output[0] = 0xA8; + else if (!strcmp(ins,"TAX")) output[0] = 0xAA; + else if (!strcmp(ins,"CLV")) output[0] = 0xB8; + else if (!strcmp(ins,"TSX")) output[0] = 0xBA; + else if (!strcmp(ins,"INY")) output[0] = 0xC8; + else if (!strcmp(ins,"DEX")) output[0] = 0xCA; + else if (!strcmp(ins,"CLD")) output[0] = 0xD8; + else if (!strcmp(ins,"INX")) output[0] = 0xE8; + else if (!strcmp(ins,"NOP")) output[0] = 0xEA; + else if (!strcmp(ins,"SED")) output[0] = 0xF8; + else return 1; + } + else { + //Instructions with Operands + if (!strcmp(ins,"ORA")) output[0] = 0x01; + else if (!strcmp(ins,"ASL")) output[0] = 0x06; + else if (!strcmp(ins,"BPL")) output[0] = 0x10; + else if (!strcmp(ins,"JSR")) output[0] = 0x20; + else if (!strcmp(ins,"AND")) output[0] = 0x21; + else if (!strcmp(ins,"BIT")) output[0] = 0x24; + else if (!strcmp(ins,"ROL")) output[0] = 0x26; + else if (!strcmp(ins,"BMI")) output[0] = 0x30; + else if (!strcmp(ins,"EOR")) output[0] = 0x41; + else if (!strcmp(ins,"LSR")) output[0] = 0x46; + else if (!strcmp(ins,"JMP")) output[0] = 0x4C; + else if (!strcmp(ins,"BVC")) output[0] = 0x50; + else if (!strcmp(ins,"ADC")) output[0] = 0x61; + else if (!strcmp(ins,"ROR")) output[0] = 0x66; + else if (!strcmp(ins,"BVS")) output[0] = 0x70; + else if (!strcmp(ins,"STA")) output[0] = 0x81; + else if (!strcmp(ins,"STY")) output[0] = 0x84; + else if (!strcmp(ins,"STX")) output[0] = 0x86; + else if (!strcmp(ins,"BCC")) output[0] = 0x90; + else if (!strcmp(ins,"LDY")) output[0] = 0xA0; + else if (!strcmp(ins,"LDA")) output[0] = 0xA1; + else if (!strcmp(ins,"LDX")) output[0] = 0xA2; + else if (!strcmp(ins,"BCS")) output[0] = 0xB0; + else if (!strcmp(ins,"CPY")) output[0] = 0xC0; + else if (!strcmp(ins,"CMP")) output[0] = 0xC1; + else if (!strcmp(ins,"DEC")) output[0] = 0xC6; + else if (!strcmp(ins,"BNE")) output[0] = 0xD0; + else if (!strcmp(ins,"CPX")) output[0] = 0xE0; + else if (!strcmp(ins,"SBC")) output[0] = 0xE1; + else if (!strcmp(ins,"INC")) output[0] = 0xE6; + else if (!strcmp(ins,"BEQ")) output[0] = 0xF0; + else return 1; + + { + //Parse Operands + // It's not the sexiest thing ever, but it works well enough! + + //TODO: + // Add branches. + // Fix certain instructions. (Setting bits is not 100% perfect.) + // Fix instruction/operand matching. (Instructions like "jmp ($94),Y" are no good!) + // Optimizations? + int tmpint; + char tmpchr,tmpstr[20]; + + if (sscanf(astr,"#$%2X%c",&tmpint,&tmpchr) == 1) { //#Immediate + switch (output[0]) { + case 0x20: case 0x4C: //Jumps + case 0x10: case 0x30: case 0x50: case 0x70: //Branches + case 0x90: case 0xB0: case 0xD0: case 0xF0: + case 0x06: case 0x24: case 0x26: case 0x46: //Other instructions incapable of #Immediate + case 0x66: case 0x81: case 0x84: case 0x86: + case 0xC6: case 0xE6: + return 1; + default: + //cheap hack for certain instructions + switch (output[0]) { + case 0xA0: case 0xA2: case 0xC0: case 0xE0: + break; + default: + output[0] |= 0x08; + break; + } + output[1] = tmpint; + break; + } + } + else if (sscanf(astr,"$%4X%c",&tmpint,&tmpchr) == 1) { //Absolute, Zero Page, Branch, or Jump + switch (output[0]) { + case 0x20: case 0x4C: //Jumps + output[1] = (tmpint & 0xFF); + output[2] = (tmpint >> 8); + break; + case 0x10: case 0x30: case 0x50: case 0x70: //Branches + case 0x90: case 0xB0: case 0xD0: case 0xF0: + tmpint -= (addr+2); + if ((tmpint < -128) || (tmpint > 127)) return 1; + output[1] = (tmpint & 0xFF); + break; + //return 1; //FIX ME + default: + if (tmpint > 0xFF) { //Absolute + output[0] |= 0x0C; + output[1] = (tmpint & 0xFF); + output[2] = (tmpint >> 8); + } + else { //Zero Page + output[0] |= 0x04; + output[1] = (tmpint & 0xFF); + } + break; + } + } + else if (sscanf(astr,"$%4X%s",&tmpint,tmpstr) == 2) { //Absolute,X, Zero Page,X, Absolute,Y or Zero Page,Y + if (!strcmp(tmpstr,",X")) { //Absolute,X or Zero Page,X + switch (output[0]) { + case 0x20: case 0x4C: //Jumps + case 0x10: case 0x30: case 0x50: case 0x70: //Branches + case 0x90: case 0xB0: case 0xD0: case 0xF0: + case 0x24: case 0x86: case 0xA2: case 0xC0: //Other instructions incapable of Absolute,X or Zero Page,X + case 0xE0: + return 1; + default: + if (tmpint > 0xFF) { //Absolute + if (output[0] == 0x84) return 1; //No STY Absolute,X! + output[0] |= 0x1C; + output[1] = (tmpint & 0xFF); + output[2] = (tmpint >> 8); + } + else { //Zero Page + output[0] |= 0x14; + output[1] = (tmpint & 0xFF); + } + break; + } + } + else if (!strcmp(tmpstr,",Y")) { //Absolute,Y or Zero Page,Y + switch (output[0]) { + case 0x20: case 0x4C: //Jumps + case 0x10: case 0x30: case 0x50: case 0x70: //Branches + case 0x90: case 0xB0: case 0xD0: case 0xF0: + case 0x06: case 0x24: case 0x26: case 0x46: //Other instructions incapable of Absolute,Y or Zero Page,Y + case 0x66: case 0x84: case 0x86: case 0xA0: + case 0xC0: case 0xC6: case 0xE0: case 0xE6: + return 1; + case 0xA2: //cheap hack for LDX + output[0] |= 0x04; + default: + if (tmpint > 0xFF) { //Absolute + if (output[0] == 0x86) return 1; //No STX Absolute,Y! + output[0] |= 0x18; + output[1] = (tmpint & 0xFF); + output[2] = (tmpint >> 8); + } + else { //Zero Page + if ((output[0] != 0x86) && (output[0] != 0xA2)) return 1; //only STX and LDX Absolute,Y! + output[0] |= 0x10; + output[1] = (tmpint & 0xFF); + } + break; + } + } + else return 1; + } + else if (sscanf(astr,"($%4X%s",&tmpint,tmpstr) == 2) { //Jump (Indirect), (Indirect,X) or (Indirect),Y + switch (output[0]) { + case 0x20: //Jumps + case 0x10: case 0x30: case 0x50: case 0x70: //Branches + case 0x90: case 0xB0: case 0xD0: case 0xF0: + case 0x06: case 0x24: case 0x26: case 0x46: //Other instructions incapable of Jump (Indirect), (Indirect,X) or (Indirect),Y + case 0x66: case 0x84: case 0x86: case 0xA0: + case 0xA2: case 0xC0: case 0xC6: case 0xE0: + case 0xE6: + return 1; + default: + if ((!strcmp(tmpstr,")")) && (output[0] == 0x4C)) { //Jump (Indirect) + output[0] = 0x6C; + output[1] = (tmpint & 0xFF); + output[2] = (tmpint >> 8); + } + else if ((!strcmp(tmpstr,",X)")) && (tmpint <= 0xFF) && (output[0] != 0x4C)) { //(Indirect,X) + output[1] = (tmpint & 0xFF); + } + else if ((!strcmp(tmpstr,"),Y")) && (tmpint <= 0xFF) && (output[0] != 0x4C)) { //(Indirect),Y + output[0] |= 0x10; + output[1] = (tmpint & 0xFF); + } + else return 1; + break; + } + } + else return 1; + } + } + + return 0; +} + +///disassembles the opcodes in the buffer assuming the provided address. Uses GetMem() and 6502 current registers to query referenced values. returns a static string buffer. +char *Disassemble(int addr, uint8 *opcode) { + static char str[64]={0},chr[5]={0}; + uint16 tmp,tmp2; + + //these may be replaced later with passed-in values to make a lighter-weight disassembly mode that may not query the referenced values + #define RX (X.X) + #define RY (X.Y) + + switch (opcode[0]) { + #define relative(a) { \ + if (((a)=opcode[1])&0x80) (a) = addr-(((a)-1)^0xFF); \ + else (a)+=addr; \ + } + #define absolute(a) { \ + (a) = opcode[1] | opcode[2]<<8; \ + } + #define zpIndex(a,i) { \ + (a) = (opcode[1]+(i))&0xFF; \ + } + #define indirectX(a) { \ + (a) = (opcode[1]+RX)&0xFF; \ + (a) = GetMem((a)) | (GetMem(((a)+1)&0xff))<<8; \ + } + #define indirectY(a) { \ + (a) = GetMem(opcode[1]) | (GetMem((opcode[1]+1)&0xff))<<8; \ + (a) += RY; \ + } + + + #ifdef BRK_3BYTE_HACK + case 0x00: + sprintf(str,"BRK %02X %02X", opcode[1], opcode[2]); + break; + #else + case 0x00: strcpy(str,"BRK"); break; + #endif + + //odd, 1-byte opcodes + case 0x08: strcpy(str,"PHP"); break; + case 0x0A: strcpy(str,"ASL"); break; + case 0x18: strcpy(str,"CLC"); break; + case 0x28: strcpy(str,"PLP"); break; + case 0x2A: strcpy(str,"ROL"); break; + case 0x38: strcpy(str,"SEC"); break; + case 0x40: strcpy(str,"RTI"); break; + case 0x48: strcpy(str,"PHA"); break; + case 0x4A: strcpy(str,"LSR"); break; + case 0x58: strcpy(str,"CLI"); break; + case 0x60: strcpy(str,"RTS"); break; + case 0x68: strcpy(str,"PLA"); break; + case 0x6A: strcpy(str,"ROR"); break; + case 0x78: strcpy(str,"SEI"); break; + case 0x88: strcpy(str,"DEY"); break; + case 0x8A: strcpy(str,"TXA"); break; + case 0x98: strcpy(str,"TYA"); break; + case 0x9A: strcpy(str,"TXS"); break; + case 0xA8: strcpy(str,"TAY"); break; + case 0xAA: strcpy(str,"TAX"); break; + case 0xB8: strcpy(str,"CLV"); break; + case 0xBA: strcpy(str,"TSX"); break; + case 0xC8: strcpy(str,"INY"); break; + case 0xCA: strcpy(str,"DEX"); break; + case 0xD8: strcpy(str,"CLD"); break; + case 0xE8: strcpy(str,"INX"); break; + case 0xEA: strcpy(str,"NOP"); break; + case 0xF8: strcpy(str,"SED"); break; + + //(Indirect,X) + case 0x01: strcpy(chr,"ORA"); goto _indirectx; + case 0x21: strcpy(chr,"AND"); goto _indirectx; + case 0x41: strcpy(chr,"EOR"); goto _indirectx; + case 0x61: strcpy(chr,"ADC"); goto _indirectx; + case 0x81: strcpy(chr,"STA"); goto _indirectx; + case 0xA1: strcpy(chr,"LDA"); goto _indirectx; + case 0xC1: strcpy(chr,"CMP"); goto _indirectx; + case 0xE1: strcpy(chr,"SBC"); goto _indirectx; + _indirectx: + indirectX(tmp); + sprintf(str,"%s ($%02X,X) @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + break; + + //Zero Page + case 0x05: strcpy(chr,"ORA"); goto _zeropage; + case 0x06: strcpy(chr,"ASL"); goto _zeropage; + case 0x24: strcpy(chr,"BIT"); goto _zeropage; + case 0x25: strcpy(chr,"AND"); goto _zeropage; + case 0x26: strcpy(chr,"ROL"); goto _zeropage; + case 0x45: strcpy(chr,"EOR"); goto _zeropage; + case 0x46: strcpy(chr,"LSR"); goto _zeropage; + case 0x65: strcpy(chr,"ADC"); goto _zeropage; + case 0x66: strcpy(chr,"ROR"); goto _zeropage; + case 0x84: strcpy(chr,"STY"); goto _zeropage; + case 0x85: strcpy(chr,"STA"); goto _zeropage; + case 0x86: strcpy(chr,"STX"); goto _zeropage; + case 0xA4: strcpy(chr,"LDY"); goto _zeropage; + case 0xA5: strcpy(chr,"LDA"); goto _zeropage; + case 0xA6: strcpy(chr,"LDX"); goto _zeropage; + case 0xC4: strcpy(chr,"CPY"); goto _zeropage; + case 0xC5: strcpy(chr,"CMP"); goto _zeropage; + case 0xC6: strcpy(chr,"DEC"); goto _zeropage; + case 0xE4: strcpy(chr,"CPX"); goto _zeropage; + case 0xE5: strcpy(chr,"SBC"); goto _zeropage; + case 0xE6: strcpy(chr,"INC"); goto _zeropage; + _zeropage: + // ################################## Start of SP CODE ########################### + // Change width to %04X // don't! + sprintf(str,"%s $%02X = #$%02X", chr,opcode[1],GetMem(opcode[1])); + // ################################## End of SP CODE ########################### + break; + + //#Immediate + case 0x09: strcpy(chr,"ORA"); goto _immediate; + case 0x29: strcpy(chr,"AND"); goto _immediate; + case 0x49: strcpy(chr,"EOR"); goto _immediate; + case 0x69: strcpy(chr,"ADC"); goto _immediate; + //case 0x89: strcpy(chr,"STA"); goto _immediate; //baka, no STA #imm!! + case 0xA0: strcpy(chr,"LDY"); goto _immediate; + case 0xA2: strcpy(chr,"LDX"); goto _immediate; + case 0xA9: strcpy(chr,"LDA"); goto _immediate; + case 0xC0: strcpy(chr,"CPY"); goto _immediate; + case 0xC9: strcpy(chr,"CMP"); goto _immediate; + case 0xE0: strcpy(chr,"CPX"); goto _immediate; + case 0xE9: strcpy(chr,"SBC"); goto _immediate; + _immediate: + sprintf(str,"%s #$%02X", chr,opcode[1]); + break; + + //Absolute + case 0x0D: strcpy(chr,"ORA"); goto _absolute; + case 0x0E: strcpy(chr,"ASL"); goto _absolute; + case 0x2C: strcpy(chr,"BIT"); goto _absolute; + case 0x2D: strcpy(chr,"AND"); goto _absolute; + case 0x2E: strcpy(chr,"ROL"); goto _absolute; + case 0x4D: strcpy(chr,"EOR"); goto _absolute; + case 0x4E: strcpy(chr,"LSR"); goto _absolute; + case 0x6D: strcpy(chr,"ADC"); goto _absolute; + case 0x6E: strcpy(chr,"ROR"); goto _absolute; + case 0x8C: strcpy(chr,"STY"); goto _absolute; + case 0x8D: strcpy(chr,"STA"); goto _absolute; + case 0x8E: strcpy(chr,"STX"); goto _absolute; + case 0xAC: strcpy(chr,"LDY"); goto _absolute; + case 0xAD: strcpy(chr,"LDA"); goto _absolute; + case 0xAE: strcpy(chr,"LDX"); goto _absolute; + case 0xCC: strcpy(chr,"CPY"); goto _absolute; + case 0xCD: strcpy(chr,"CMP"); goto _absolute; + case 0xCE: strcpy(chr,"DEC"); goto _absolute; + case 0xEC: strcpy(chr,"CPX"); goto _absolute; + case 0xED: strcpy(chr,"SBC"); goto _absolute; + case 0xEE: strcpy(chr,"INC"); goto _absolute; + _absolute: + absolute(tmp); + sprintf(str,"%s $%04X = #$%02X", chr,tmp,GetMem(tmp)); + break; + + //branches + case 0x10: strcpy(chr,"BPL"); goto _branch; + case 0x30: strcpy(chr,"BMI"); goto _branch; + case 0x50: strcpy(chr,"BVC"); goto _branch; + case 0x70: strcpy(chr,"BVS"); goto _branch; + case 0x90: strcpy(chr,"BCC"); goto _branch; + case 0xB0: strcpy(chr,"BCS"); goto _branch; + case 0xD0: strcpy(chr,"BNE"); goto _branch; + case 0xF0: strcpy(chr,"BEQ"); goto _branch; + _branch: + relative(tmp); + sprintf(str,"%s $%04X", chr,tmp); + break; + + //(Indirect),Y + case 0x11: strcpy(chr,"ORA"); goto _indirecty; + case 0x31: strcpy(chr,"AND"); goto _indirecty; + case 0x51: strcpy(chr,"EOR"); goto _indirecty; + case 0x71: strcpy(chr,"ADC"); goto _indirecty; + case 0x91: strcpy(chr,"STA"); goto _indirecty; + case 0xB1: strcpy(chr,"LDA"); goto _indirecty; + case 0xD1: strcpy(chr,"CMP"); goto _indirecty; + case 0xF1: strcpy(chr,"SBC"); goto _indirecty; + _indirecty: + indirectY(tmp); + sprintf(str,"%s ($%02X),Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + break; + + //Zero Page,X + case 0x15: strcpy(chr,"ORA"); goto _zeropagex; + case 0x16: strcpy(chr,"ASL"); goto _zeropagex; + case 0x35: strcpy(chr,"AND"); goto _zeropagex; + case 0x36: strcpy(chr,"ROL"); goto _zeropagex; + case 0x55: strcpy(chr,"EOR"); goto _zeropagex; + case 0x56: strcpy(chr,"LSR"); goto _zeropagex; + case 0x75: strcpy(chr,"ADC"); goto _zeropagex; + case 0x76: strcpy(chr,"ROR"); goto _zeropagex; + case 0x94: strcpy(chr,"STY"); goto _zeropagex; + case 0x95: strcpy(chr,"STA"); goto _zeropagex; + case 0xB4: strcpy(chr,"LDY"); goto _zeropagex; + case 0xB5: strcpy(chr,"LDA"); goto _zeropagex; + case 0xD5: strcpy(chr,"CMP"); goto _zeropagex; + case 0xD6: strcpy(chr,"DEC"); goto _zeropagex; + case 0xF5: strcpy(chr,"SBC"); goto _zeropagex; + case 0xF6: strcpy(chr,"INC"); goto _zeropagex; + _zeropagex: + zpIndex(tmp,RX); + // ################################## Start of SP CODE ########################### + // Change width to %04X // don't! + sprintf(str,"%s $%02X,X @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + // ################################## End of SP CODE ########################### + break; + + //Absolute,Y + case 0x19: strcpy(chr,"ORA"); goto _absolutey; + case 0x39: strcpy(chr,"AND"); goto _absolutey; + case 0x59: strcpy(chr,"EOR"); goto _absolutey; + case 0x79: strcpy(chr,"ADC"); goto _absolutey; + case 0x99: strcpy(chr,"STA"); goto _absolutey; + case 0xB9: strcpy(chr,"LDA"); goto _absolutey; + case 0xBE: strcpy(chr,"LDX"); goto _absolutey; + case 0xD9: strcpy(chr,"CMP"); goto _absolutey; + case 0xF9: strcpy(chr,"SBC"); goto _absolutey; + _absolutey: + absolute(tmp); + tmp2=(tmp+RY); + sprintf(str,"%s $%04X,Y @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); + break; + + //Absolute,X + case 0x1D: strcpy(chr,"ORA"); goto _absolutex; + case 0x1E: strcpy(chr,"ASL"); goto _absolutex; + case 0x3D: strcpy(chr,"AND"); goto _absolutex; + case 0x3E: strcpy(chr,"ROL"); goto _absolutex; + case 0x5D: strcpy(chr,"EOR"); goto _absolutex; + case 0x5E: strcpy(chr,"LSR"); goto _absolutex; + case 0x7D: strcpy(chr,"ADC"); goto _absolutex; + case 0x7E: strcpy(chr,"ROR"); goto _absolutex; + case 0x9D: strcpy(chr,"STA"); goto _absolutex; + case 0xBC: strcpy(chr,"LDY"); goto _absolutex; + case 0xBD: strcpy(chr,"LDA"); goto _absolutex; + case 0xDD: strcpy(chr,"CMP"); goto _absolutex; + case 0xDE: strcpy(chr,"DEC"); goto _absolutex; + case 0xFD: strcpy(chr,"SBC"); goto _absolutex; + case 0xFE: strcpy(chr,"INC"); goto _absolutex; + _absolutex: + absolute(tmp); + tmp2=(tmp+RX); + sprintf(str,"%s $%04X,X @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); + break; + + //jumps + case 0x20: strcpy(chr,"JSR"); goto _jump; + case 0x4C: strcpy(chr,"JMP"); goto _jump; + case 0x6C: absolute(tmp); sprintf(str,"JMP ($%04X) = $%04X", tmp,GetMem(tmp)|GetMem(tmp+1)<<8); break; + _jump: + absolute(tmp); + sprintf(str,"%s $%04X", chr,tmp); + break; + + //Zero Page,Y + case 0x96: strcpy(chr,"STX"); goto _zeropagey; + case 0xB6: strcpy(chr,"LDX"); goto _zeropagey; + _zeropagey: + zpIndex(tmp,RY); + // ################################## Start of SP CODE ########################### + // Change width to %04X // don't! + sprintf(str,"%s $%02X,Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + // ################################## End of SP CODE ########################### + break; + + //UNDEFINED + default: strcpy(str,"ERROR"); break; + + } + + return str; +} diff --git a/apps/fceux/src/asm.h b/apps/fceux/src/asm.h new file mode 100644 index 00000000..5dff9f32 --- /dev/null +++ b/apps/fceux/src/asm.h @@ -0,0 +1,2 @@ +int Assemble(unsigned char *output, int addr, char *str); +char *Disassemble(int addr, uint8 *opcode); diff --git a/apps/fceux/src/boards/01-222.cpp b/apps/fceux/src/boards/01-222.cpp new file mode 100644 index 00000000..414d62db --- /dev/null +++ b/apps/fceux/src/boards/01-222.cpp @@ -0,0 +1,111 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + * TXC mappers, originally much complex banksitching + * + * P/N PRG MAP, UNIF Name + * 01-22111-000 (05-00002-010) (132, 22211) - MGC-001 Qi Wang + * 01-22110-000 (52S ) - MGC-002 2-in-1 Gun + * 01-22111-100 (02-00002-010) (173 ) - MGC-008 Mahjong Block + * (079 ) - MGC-012 Poke Block + * 01-22110-200 (05-00002-010) (036 ) - MGC-014 Strike Wolf + * 01-22000-400 (05-00002-010) (036 ) - MGC-015 Policeman + * 01-22017-000 (05-PT017-080) (189 ) - MGC-017 Thunder Warrior + * 01-11160-000 (04-02310-000) ( , 11160) - MGC-023 6-in-1 + * 01-22026-000 (05-04010-090) ( ) - MGC-026 4-in-1 + * 01-22270-000 (05-00002-010) (132, 22211) - MGC-xxx Creatom + * 01-22200-400 (------------) (079 ) - ET.03 F-15 City War + * (172 ) - 1991 Du Ma Racing + * + */ + +#include "mapinc.h" + +static uint8 reg[4], cmd, is172, is173; +static SFORMAT StateRegs[] = +{ + { reg, 4, "REGS" }, + { &cmd, 1, "CMD" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, (reg[2] >> 2) & 1); + if (is172) + setchr8((((cmd ^ reg[2]) >> 3) & 2) | (((cmd ^ reg[2]) >> 5) & 1)); // 1991 DU MA Racing probably CHR bank sequence is WRONG, so it is possible to + // rearrange CHR banks for normal UNIF board and mapper 172 is unneccessary + else + setchr8(reg[2] & 3); +} + +static DECLFW(UNL22211WriteLo) { +// FCEU_printf("bs %04x %02x\n",A,V); + reg[A & 3] = V; +} + +static DECLFW(UNL22211WriteHi) { +// FCEU_printf("bs %04x %02x\n",A,V); + cmd = V; + Sync(); +} + +static DECLFR(UNL22211ReadLo) { + return (reg[1] ^ reg[2]) | (is173 ? 0x01 : 0x40); +// if(reg[3]) +// return reg[2]; +// else +// return X.DB; +} + +static void UNL22211Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0x4100, 0x4100, UNL22211ReadLo); + SetWriteHandler(0x4100, 0x4103, UNL22211WriteLo); + SetWriteHandler(0x8000, 0xFFFF, UNL22211WriteHi); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNL22211_Init(CartInfo *info) { + is172 = 0; + is173 = 0; + info->Power = UNL22211Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper172_Init(CartInfo *info) { + is172 = 1; + is173 = 0; + info->Power = UNL22211Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper173_Init(CartInfo *info) { + is172 = 0; + is173 = 1; + info->Power = UNL22211Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/09-034a.cpp b/apps/fceux/src/boards/09-034a.cpp new file mode 100644 index 00000000..7847cd9d --- /dev/null +++ b/apps/fceux/src/boards/09-034a.cpp @@ -0,0 +1,97 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * FDS Conversions + * + * Super Mario Bros 2j (Alt Full) is a BAD incomplete dump, should be mapper 43 + * + * Both Voleyball and Zanac by Whirlind Manu shares the same PCB, but with + * some differences: Voleyball has 8K CHR ROM and 8K ROM at 6000K, Zanac + * have 8K CHR RAM and banked 16K ROM mapper at 6000 as two 8K banks. +* + * Super Mario Bros 2j (Alt Small) uses additionally IRQ timer to drive framerate + * + * PCB for this mapper is "09-034A" + */ + +#include "mapinc.h" + +static uint8 prg; +static uint32 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 4, "IRQC" }, + { &IRQa, 4, "IRQA" }, + { &prg, 1, "PRG" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(1, 0x6000, prg); + setprg32(0x8000, 0); + setchr8(0); +} + +static DECLFW(UNLSMB2JWrite1) { + prg = V & 1; + Sync(); +} + +static DECLFW(UNLSMB2JWrite2) { + IRQa = V & 1; + IRQCount = 0; + X6502_IRQEnd(FCEU_IQEXT); +} + +static DECLFR(UNLSMB2JRead) { + return 0xFF; +} + +static void UNLSMB2JPower(void) { + prg = 0; + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetReadHandler(0x4042, 0x4055, UNLSMB2JRead); + SetWriteHandler(0x4068, 0x4068, UNLSMB2JWrite2); + SetWriteHandler(0x4027, 0x4027, UNLSMB2JWrite1); +} + +static void UNLSMB2JIRQHook(int a) { + if (IRQa) + { + if (IRQCount < 5750) // completely by guess + IRQCount += a; + else { + IRQa = 0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLSMB2J_Init(CartInfo *info) { + info->Power = UNLSMB2JPower; + MapIRQHook = UNLSMB2JIRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/103.cpp b/apps/fceux/src/boards/103.cpp new file mode 100644 index 00000000..16a622c4 --- /dev/null +++ b/apps/fceux/src/boards/103.cpp @@ -0,0 +1,112 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg0, reg1, reg2; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®0, 1, "REG0" }, + { ®1, 1, "REG1" }, + { ®2, 1, "REG2" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg8(0x8000, 0xc); + setprg8(0xe000, 0xf); + if (reg2 & 0x10) { + setprg8(0x6000, reg0); + setprg8(0xa000, 0xd); + setprg8(0xc000, 0xe); + } else { + setprg8r(0x10, 0x6000, 0); + setprg4(0xa000, (0xd << 1)); + setprg2(0xb000, (0xd << 2) + 2); + setprg2r(0x10, 0xb800, 4); + setprg2r(0x10, 0xc000, 5); + setprg2r(0x10, 0xc800, 6); + setprg2r(0x10, 0xd000, 7); + setprg2(0xd800, (0xe << 2) + 3); + } + setmirror(reg1 ^ 1); +} + +static DECLFW(M103RamWrite0) { + WRAM[A & 0x1FFF] = V; +} + +static DECLFW(M103RamWrite1) { + WRAM[0x2000 + ((A - 0xB800) & 0x1FFF)] = V; +} + +static DECLFW(M103Write0) { + reg0 = V & 0xf; + Sync(); +} + +static DECLFW(M103Write1) { + reg1 = (V >> 3) & 1; + Sync(); +} + +static DECLFW(M103Write2) { + reg2 = V; + Sync(); +} + +static void M103Power(void) { + reg0 = reg1 = 0; reg2 = 0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, M103RamWrite0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0xB800, 0xD7FF, M103RamWrite1); + SetWriteHandler(0x8000, 0x8FFF, M103Write0); + SetWriteHandler(0xE000, 0xEFFF, M103Write1); + SetWriteHandler(0xF000, 0xFFFF, M103Write2); +} + +static void M103Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper103_Init(CartInfo *info) { + info->Power = M103Power; + info->Close = M103Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 16384; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/106.cpp b/apps/fceux/src/boards/106.cpp new file mode 100644 index 00000000..e427d293 --- /dev/null +++ b/apps/fceux/src/boards/106.cpp @@ -0,0 +1,108 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg[16], IRQa; +static uint32 IRQCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { &IRQa, 1, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { reg, 16, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setchr1(0x0000, reg[0] & 0xfe); + setchr1(0x0400, reg[1] | 1); + setchr1(0x0800, reg[2] & 0xfe); + setchr1(0x0c00, reg[3] | 1); + setchr1(0x1000, reg[4]); + setchr1(0x1400, reg[5]); + setchr1(0x1800, reg[6]); + setchr1(0x1c00, reg[7]); + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, (reg[0x8] & 0xf) | 0x10); + setprg8(0xA000, (reg[0x9] & 0x1f)); + setprg8(0xC000, (reg[0xa] & 0x1f)); + setprg8(0xE000, (reg[0xb] & 0xf) | 0x10); + setmirror((reg[0xc] & 1) ^ 1); +} + +static DECLFW(M106Write) { + A &= 0xF; + switch (A) { + case 0xD: IRQa = 0; IRQCount = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xE: IRQCount = (IRQCount & 0xFF00) | V; break; + case 0xF: IRQCount = (IRQCount & 0x00FF) | (V << 8); IRQa = 1; break; + default: reg[A] = V; Sync(); break; + } +} + +static void M106Power(void) { + reg[8] = reg[9] = reg[0xa] = reg[0xb] = -1; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0xFFFF, M106Write); +} + +static void M106Reset(void) { +} + +static void M106Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void M106CpuHook(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount > 0x10000) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper106_Init(CartInfo *info) { + info->Reset = M106Reset; + info->Power = M106Power; + info->Close = M106Close; + MapIRQHook = M106CpuHook; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/108.cpp b/apps/fceux/src/boards/108.cpp new file mode 100644 index 00000000..4c463f96 --- /dev/null +++ b/apps/fceux/src/boards/108.cpp @@ -0,0 +1,58 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, reg); + setprg32(0x8000, ~0); + setchr8(0); +} + +static DECLFW(M108Write) { + reg = V; + Sync(); +} + +static void M108Power(void) { + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0x8FFF, M108Write); // regular 108 + SetWriteHandler(0xF000, 0xFFFF, M108Write); // simplified Kaiser BB Hack +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper108_Init(CartInfo *info) { + info->Power = M108Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/112.cpp b/apps/fceux/src/boards/112.cpp new file mode 100644 index 00000000..f0ee7aca --- /dev/null +++ b/apps/fceux/src/boards/112.cpp @@ -0,0 +1,90 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * NTDEC, ASDER games + * + */ + +#include "mapinc.h" + +static uint8 reg[8]; +static uint8 mirror, cmd, bank; +static uint8 *WRAM = NULL; + +static SFORMAT StateRegs[] = +{ + { &cmd, 1, "CMD" }, + { &mirror, 1, "MIRR" }, + { &bank, 1, "BANK" }, + { reg, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setmirror(mirror ^ 1); + setprg8(0x8000, reg[0]); + setprg8(0xA000, reg[1]); + setchr2(0x0000, (reg[2] >> 1)); + setchr2(0x0800, (reg[3] >> 1)); + setchr1(0x1000, ((bank & 0x10) << 4) | reg[4]); + setchr1(0x1400, ((bank & 0x20) << 3) | reg[5]); + setchr1(0x1800, ((bank & 0x40) << 2) | reg[6]); + setchr1(0x1C00, ((bank & 0x80) << 1) | reg[7]); +} + +static DECLFW(M112Write) { + switch (A) { + case 0xe000: mirror = V & 1; Sync();; break; + case 0x8000: cmd = V & 7; break; + case 0xa000: reg[cmd] = V; Sync(); break; + case 0xc000: bank = V; Sync(); break; + } +} + +static void M112Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void M112Power(void) { + bank = 0; + setprg16(0xC000, ~0); + setprg8r(0x10, 0x6000, 0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M112Write); + SetWriteHandler(0x4020, 0x5FFF, M112Write); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(8, 0x6000, WRAM); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper112_Init(CartInfo *info) { + info->Power = M112Power; + info->Close = M112Close; + GameStateRestore = StateRestore; + WRAM = (uint8*)FCEU_gmalloc(8192); + SetupCartPRGMapping(0x10, WRAM, 8192, 1); + AddExState(WRAM, 8192, 0, "WRAM"); + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/116.cpp b/apps/fceux/src/boards/116.cpp new file mode 100644 index 00000000..555f6c48 --- /dev/null +++ b/apps/fceux/src/boards/116.cpp @@ -0,0 +1,324 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + * + * SL12 Protected 3-in-1 mapper hardware (VRC2, MMC3, MMC1) + * the same as 603-5052 board (TODO: add reading registers, merge) + * SL1632 2-in-1 protected board, similar to SL12 (TODO: find difference) + * + * Known PCB: + * + * Garou Densetsu Special (G0904.PCB, Huang-1, GAL dip: W conf.) + * Kart Fighter (008, Huang-1, GAL dip: W conf.) + * Somari (008, C5052-13, GAL dip: P conf., GK2-P/GK2-V maskroms) + * Somari (008, Huang-1, GAL dip: W conf., GK1-P/GK1-V maskroms) + * AV Mei Shao Nv Zhan Shi (aka AV Pretty Girl Fighting) (SL-12 PCB, Hunag-1, GAL dip: unk conf. SL-11A/SL-11B maskroms) + * Samurai Spirits (Full version) (Huang-1, GAL dip: unk conf. GS-2A/GS-4A maskroms) + * Contra Fighter (603-5052 PCB, C5052-3, GAL dip: unk conf. SC603-A/SCB603-B maskroms) + * + */ + +#include "mapinc.h" + +static uint8 mode; +static uint8 vrc2_chr[8], vrc2_prg[2], vrc2_mirr; +static uint8 mmc3_regs[10], mmc3_ctrl, mmc3_mirr; +static uint8 IRQCount, IRQLatch, IRQa; +static uint8 IRQReload; +static uint8 mmc1_regs[4], mmc1_buffer, mmc1_shift; + +static SFORMAT StateRegs[] = +{ + { &mode, 1, "MODE" }, + { vrc2_chr, 8, "VRCC" }, + { vrc2_prg, 2, "VRCP" }, + { &vrc2_mirr, 1, "VRCM" }, + { mmc3_regs, 10, "M3RG" }, + { &mmc3_ctrl, 1, "M3CT" }, + { &mmc3_mirr, 1, "M3MR" }, + { &IRQReload, 1, "IRQR" }, + { &IRQCount, 1, "IRQC" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQa, 1, "IRQA" }, + { mmc1_regs, 4, "M1RG" }, + { &mmc1_buffer, 1, "M1BF" }, + { &mmc1_shift, 1, "M1MR" }, + { 0 } +}; + +static void SyncPRG(void) { + switch (mode & 3) { + case 0: + setprg8(0x8000, vrc2_prg[0]); + setprg8(0xA000, vrc2_prg[1]); + setprg8(0xC000, ~1); + setprg8(0xE000, ~0); + break; + case 1: + { + uint32 swap = (mmc3_ctrl >> 5) & 2; + setprg8(0x8000, mmc3_regs[6 + swap]); + setprg8(0xA000, mmc3_regs[7]); + setprg8(0xC000, mmc3_regs[6 + (swap ^ 2)]); + setprg8(0xE000, mmc3_regs[9]); + break; + } + case 2: + case 3: + { + uint8 bank = mmc1_regs[3] & 0xF; + if (mmc1_regs[0] & 8) { + if (mmc1_regs[0] & 4) { + setprg16(0x8000, bank); + setprg16(0xC000, 0x0F); + } else { + setprg16(0x8000, 0); + setprg16(0xC000, bank); + } + } else + setprg32(0x8000, bank >> 1); + break; + } + } +} + +static void SyncCHR(void) { + uint32 base = (mode & 4) << 6; + switch (mode & 3) { + case 0: + setchr1(0x0000, base | vrc2_chr[0]); + setchr1(0x0400, base | vrc2_chr[1]); + setchr1(0x0800, base | vrc2_chr[2]); + setchr1(0x0c00, base | vrc2_chr[3]); + setchr1(0x1000, base | vrc2_chr[4]); + setchr1(0x1400, base | vrc2_chr[5]); + setchr1(0x1800, base | vrc2_chr[6]); + setchr1(0x1c00, base | vrc2_chr[7]); + break; + case 1: { + uint32 swap = (mmc3_ctrl & 0x80) << 5; + setchr1(0x0000 ^ swap, base | ((mmc3_regs[0]) & 0xFE)); + setchr1(0x0400 ^ swap, base | (mmc3_regs[0] | 1)); + setchr1(0x0800 ^ swap, base | ((mmc3_regs[1]) & 0xFE)); + setchr1(0x0c00 ^ swap, base | (mmc3_regs[1] | 1)); + setchr1(0x1000 ^ swap, base | mmc3_regs[2]); + setchr1(0x1400 ^ swap, base | mmc3_regs[3]); + setchr1(0x1800 ^ swap, base | mmc3_regs[4]); + setchr1(0x1c00 ^ swap, base | mmc3_regs[5]); + break; + } + case 2: + case 3: + if (mmc1_regs[0] & 0x10) { + setchr4(0x0000, mmc1_regs[1]); + setchr4(0x1000, mmc1_regs[2]); + } else + setchr8(mmc1_regs[1] >> 1); + break; + } +} + +static void SyncMIR(void) { + switch (mode & 3) { + case 0: { + setmirror((vrc2_mirr & 1) ^ 1); + break; + } + case 1: { + setmirror((mmc3_mirr & 1) ^ 1); + break; + } + case 2: + case 3: { + switch (mmc1_regs[0] & 3) { + case 0: setmirror(MI_0); break; + case 1: setmirror(MI_1); break; + case 2: setmirror(MI_V); break; + case 3: setmirror(MI_H); break; + } + break; + } + } +} + +static void Sync(void) { + SyncPRG(); + SyncCHR(); + SyncMIR(); +} + +static DECLFW(UNLSL12ModeWrite) { +// FCEU_printf("%04X:%02X\n",A,V); + if ((A & 0x4100) == 0x4100) { + mode = V; + if (A & 1) { // hacky hacky, there are two configuration modes on SOMARI HUANG-1 PCBs + // Solder pads with P1/P2 shorted called SOMARI P, + // Solder pads with W1/W2 shorted called SOMARI W + // Both identical 3-in-1 but W wanted MMC1 registers + // to be reset when switch to MMC1 mode P one - doesn't + // There is issue with W version of Somari at starting copyrights + mmc1_regs[0] = 0xc; + mmc1_regs[3] = 0; + mmc1_buffer = 0; + mmc1_shift = 0; + } + Sync(); + } +} + +static DECLFW(UNLSL12Write) { +// FCEU_printf("%04X:%02X\n",A,V); + switch (mode & 3) { + case 0: { + if ((A >= 0xB000) && (A <= 0xE003)) { + int32 ind = ((((A & 2) | (A >> 10)) >> 1) + 2) & 7; + int32 sar = ((A & 1) << 2); + vrc2_chr[ind] = (vrc2_chr[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar); + SyncCHR(); + } else + switch (A & 0xF000) { + case 0x8000: vrc2_prg[0] = V; SyncPRG(); break; + case 0xA000: vrc2_prg[1] = V; SyncPRG(); break; + case 0x9000: vrc2_mirr = V; SyncMIR(); break; + } + break; + } + case 1: { + switch (A & 0xE001) { + case 0x8000: { + uint8 old_ctrl = mmc3_ctrl; + mmc3_ctrl = V; + if ((old_ctrl & 0x40) != (mmc3_ctrl & 0x40)) + SyncPRG(); + if ((old_ctrl & 0x80) != (mmc3_ctrl & 0x80)) + SyncCHR(); + break; + } + case 0x8001: + mmc3_regs[mmc3_ctrl & 7] = V; + if ((mmc3_ctrl & 7) < 6) + SyncCHR(); + else + SyncPRG(); + break; + case 0xA000: + mmc3_mirr = V; + SyncMIR(); + break; + case 0xC000: + IRQLatch = V; + break; + case 0xC001: + IRQReload = 1; + break; + case 0xE000: + X6502_IRQEnd(FCEU_IQEXT); + IRQa = 0; + break; + case 0xE001: + IRQa = 1; + break; + } + break; + } + case 2: + case 3: { + if (V & 0x80) { + mmc1_regs[0] |= 0xc; + mmc1_buffer = mmc1_shift = 0; + SyncPRG(); + } else { + uint8 n = (A >> 13) - 4; + mmc1_buffer |= (V & 1) << (mmc1_shift++); + if (mmc1_shift == 5) { + mmc1_regs[n] = mmc1_buffer; + mmc1_buffer = mmc1_shift = 0; + switch (n) { + case 0: SyncMIR(); + case 2: SyncCHR(); + case 3: + case 1: SyncPRG(); + } + } + } + break; + } + } +} + +static void UNLSL12HBIRQ(void) { + if ((mode & 3) == 1) { + int32 count = IRQCount; + if (!count || IRQReload) { + IRQCount = IRQLatch; + IRQReload = 0; + } else + IRQCount--; + if (!IRQCount) { + if (IRQa) + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +static void UNLSL12Power(void) { + mode = 0; + vrc2_chr[0] = ~0; + vrc2_chr[1] = ~0; + vrc2_chr[2] = ~0; + vrc2_chr[3] = ~0; // W conf. of Somari wanted CHR3 has to be set to BB bank (or similar), but doesn't do that directly + vrc2_chr[4] = 4; + vrc2_chr[5] = 5; + vrc2_chr[6] = 6; + vrc2_chr[7] = 7; + vrc2_prg[0] = 0; + vrc2_prg[1] = 1; + vrc2_mirr = 0; + mmc3_regs[0] = 0; + mmc3_regs[1] = 2; + mmc3_regs[2] = 4; + mmc3_regs[3] = 5; + mmc3_regs[4] = 6; + mmc3_regs[5] = 7; + mmc3_regs[6] = ~3; + mmc3_regs[7] = ~2; + mmc3_regs[8] = ~1; + mmc3_regs[9] = ~0; + mmc3_ctrl = mmc3_mirr = IRQCount = IRQLatch = IRQa = 0; + mmc1_regs[0] = 0xc; + mmc1_regs[1] = 0; + mmc1_regs[2] = 0; + mmc1_regs[3] = 0; + mmc1_buffer = 0; + mmc1_shift = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0x7FFF, UNLSL12ModeWrite); + SetWriteHandler(0x8000, 0xFFFF, UNLSL12Write); +} + +void UNLSL12_Init(CartInfo *info) { + info->Power = UNLSL12Power; + GameHBIRQHook = UNLSL12HBIRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/117.cpp b/apps/fceux/src/boards/117.cpp new file mode 100644 index 00000000..3a50ce27 --- /dev/null +++ b/apps/fceux/src/boards/117.cpp @@ -0,0 +1,91 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 prgreg[4], chrreg[8], mirror; +static uint8 IRQa, IRQCount, IRQLatch; + +static SFORMAT StateRegs[] = +{ + { &IRQa, 1, "IRQA" }, + { &IRQCount, 1, "IRQC" }, + { &IRQLatch, 1, "IRQL" }, + { prgreg, 4, "PREG" }, + { chrreg, 8, "CREG" }, + { &mirror, 1, "MREG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x8000, prgreg[0]); + setprg8(0xa000, prgreg[1]); + setprg8(0xc000, prgreg[2]); + setprg8(0xe000, prgreg[3]); + int i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chrreg[i]); + setmirror(mirror ^ 1); +} + +static DECLFW(M117Write) { + if (A < 0x8004) { + prgreg[A & 3] = V; + Sync(); + } else if ((A >= 0xA000) && (A <= 0xA007)) { + chrreg[A & 7] = V; + Sync(); + } else switch (A) { + case 0xc001: IRQLatch = V; break; + case 0xc003: IRQCount = IRQLatch; IRQa |= 2; break; + case 0xe000: IRQa &= ~1; IRQa |= V & 1; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xc002: X6502_IRQEnd(FCEU_IQEXT); break; + case 0xd000: mirror = V & 1; + } +} + +static void M117Power(void) { + prgreg[0] = ~3; prgreg[1] = ~2; prgreg[2] = ~1; prgreg[3] = ~0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M117Write); +} + +static void M117IRQHook(void) { + if (IRQa == 3 && IRQCount) { + IRQCount--; + if (!IRQCount) { + IRQa &= 1; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper117_Init(CartInfo *info) { + info->Power = M117Power; + GameHBIRQHook = M117IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/120.cpp b/apps/fceux/src/boards/120.cpp new file mode 100644 index 00000000..aee1982a --- /dev/null +++ b/apps/fceux/src/boards/120.cpp @@ -0,0 +1,59 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, reg); + setprg32(0x8000, 2); + setchr8(0); +} + +static DECLFW(M120Write) { + if (A == 0x41FF) { + reg = V & 7; + Sync(); + } +} + +static void M120Power(void) { + reg = 0; + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0x5FFF, M120Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper120_Init(CartInfo *info) { + info->Power = M120Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/121.cpp b/apps/fceux/src/boards/121.cpp new file mode 100644 index 00000000..aa4e5c2c --- /dev/null +++ b/apps/fceux/src/boards/121.cpp @@ -0,0 +1,129 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007-2008 Mad Dumper, CaH4e3 + * + * 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 + * + * Panda prince pirate. + * MK4, MK6, A9711/A9713 board + * 6035052 seems to be the same too, but with prot array in reverse + * A9746 seems to be the same too, check + * 187 seems to be the same too, check (A98402 board) + * + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void Sync() { + switch (EXPREGS[5] & 0x3F) { + case 0x20: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break; + case 0x29: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break; + case 0x26: EXPREGS[7] = 0; EXPREGS[0] = EXPREGS[6]; break; + case 0x2B: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break; + case 0x2C: EXPREGS[7] = 1; if (EXPREGS[6]) EXPREGS[0] = EXPREGS[6]; break; + case 0x3C: + case 0x3F: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break; + case 0x28: EXPREGS[7] = 0; EXPREGS[1] = EXPREGS[6]; break; + case 0x2A: EXPREGS[7] = 0; EXPREGS[2] = EXPREGS[6]; break; + case 0x2F: break; + default: EXPREGS[5] = 0; break; + } +} + +static void M121CW(uint32 A, uint8 V) { + if (PRGsize[0] == CHRsize[0]) { // A9713 multigame extension hack! + setchr1(A, V | ((EXPREGS[3] & 0x80) << 1)); + } else { + if ((A & 0x1000) == ((MMC3_cmd & 0x80) << 5)) + setchr1(A, V | 0x100); + else + setchr1(A, V); + } +} + +static void M121PW(uint32 A, uint8 V) { + if (EXPREGS[5] & 0x3F) { +// FCEU_printf("prot banks: %02x %02x %02x %02x\n",V,EXPREGS[2],EXPREGS[1],EXPREGS[0]); + setprg8(A, (V & 0x1F) | ((EXPREGS[3] & 0x80) >> 2)); + setprg8(0xE000, (EXPREGS[0]) | ((EXPREGS[3] & 0x80) >> 2)); + setprg8(0xC000, (EXPREGS[1]) | ((EXPREGS[3] & 0x80) >> 2)); + setprg8(0xA000, (EXPREGS[2]) | ((EXPREGS[3] & 0x80) >> 2)); + } else { +// FCEU_printf("gen banks: %04x %02x\n",A,V); + setprg8(A, (V & 0x1F) | ((EXPREGS[3] & 0x80) >> 2)); + } +} + +static DECLFW(M121Write) { +// FCEU_printf("write: %04x:%04x\n",A&0xE003,V); + switch (A & 0xE003) { + case 0x8000: +// EXPREGS[5] = 0; +// FCEU_printf("gen: %02x\n",V); + MMC3_CMDWrite(A, V); + FixMMC3PRG(MMC3_cmd); + break; + case 0x8001: + EXPREGS[6] = ((V & 1) << 5) | ((V & 2) << 3) | ((V & 4) << 1) | ((V & 8) >> 1) | ((V & 0x10) >> 3) | ((V & 0x20) >> 5); +// FCEU_printf("bank: %02x (%02x)\n",V,EXPREGS[6]); + if (!EXPREGS[7]) Sync(); + MMC3_CMDWrite(A, V); + FixMMC3PRG(MMC3_cmd); + break; + case 0x8003: + EXPREGS[5] = V; +// EXPREGS[7] = 0; +// FCEU_printf("prot: %02x\n",EXPREGS[5]); + Sync(); + MMC3_CMDWrite(0x8000, V); + FixMMC3PRG(MMC3_cmd); + break; + } +} + +static uint8 prot_array[16] = { 0x83, 0x83, 0x42, 0x00 }; +static DECLFW(M121LoWrite) { + EXPREGS[4] = prot_array[V & 3]; // 0x100 bit in address seems to be switch arrays 0, 2, 2, 3 (Contra Fighter) + if ((A & 0x5180) == 0x5180) { // A9713 multigame extension + EXPREGS[3] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +// FCEU_printf("write: %04x:%04x\n",A,V); +} + +static DECLFR(M121Read) { +// FCEU_printf("read: %04x->\n",A,EXPREGS[0]); + return EXPREGS[4]; +} + +static void M121Power(void) { + EXPREGS[3] = 0x80; + EXPREGS[5] = 0; + GenMMC3Power(); + SetReadHandler(0x5000, 0x5FFF, M121Read); + SetWriteHandler(0x5000, 0x5FFF, M121LoWrite); + SetWriteHandler(0x8000, 0x9FFF, M121Write); +} + +void Mapper121_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 8, 0); + pwrap = M121PW; + cwrap = M121CW; + info->Power = M121Power; + AddExState(EXPREGS, 8, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/12in1.cpp b/apps/fceux/src/boards/12in1.cpp new file mode 100644 index 00000000..add67cc4 --- /dev/null +++ b/apps/fceux/src/boards/12in1.cpp @@ -0,0 +1,73 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + * + * 7-in-1 Darkwing Duck, Snake, MagicBlock (PCB marked as "12 in 1") + * 12-in-1 1991 New Star Co. Ltd. + * + */ + +#include "mapinc.h" + +static uint8 prgchr[2], ctrl; +static SFORMAT StateRegs[] = +{ + { prgchr, 2, "REGS" }, + { &ctrl, 1, "CTRL" }, + { 0 } +}; + +static void Sync(void) { + uint8 bank = (ctrl & 3) << 3; + setchr4(0x0000, (prgchr[0] >> 3) | (bank << 2)); + setchr4(0x1000, (prgchr[1] >> 3) | (bank << 2)); + if (ctrl & 8) { + setprg16(0x8000, bank | (prgchr[0] & 6) | 0); // actually, both 0 and 1 registers used, but they will switch each PA12 transition + setprg16(0xc000, bank | (prgchr[0] & 6) | 1); // if bits are different for both registers, so they must be programmed strongly the same! + } else { + setprg16(0x8000, bank | (prgchr[0] & 7)); + setprg16(0xc000, bank | 7 ); + } + setmirror(((ctrl & 4) >> 2) ^ 1); +} + +static DECLFW(BMC12IN1Write) { + switch (A & 0xE000) { + case 0xA000: prgchr[0] = V; Sync(); break; + case 0xC000: prgchr[1] = V; Sync(); break; + case 0xE000: ctrl = V & 0x0F; Sync(); break; + } +} + +static void BMC12IN1Power(void) { + prgchr[0] = prgchr[1] = ctrl = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, BMC12IN1Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMC12IN1_Init(CartInfo *info) { + info->Power = BMC12IN1Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/15.cpp b/apps/fceux/src/boards/15.cpp new file mode 100644 index 00000000..01f55cd5 --- /dev/null +++ b/apps/fceux/src/boards/15.cpp @@ -0,0 +1,117 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + */ + +#include "mapinc.h" + +static uint16 latchea; +static uint8 latched; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static SFORMAT StateRegs[] = +{ + { &latchea, 2, "AREG" }, + { &latched, 1, "DREG" }, + { 0 } +}; + +static void Sync(void) { + int i; + setmirror(((latched >> 6) & 1) ^ 1); + switch (latchea & 3) { + case 0: + for (i = 0; i < 4; i++) + setprg8(0x8000 + (i << 13), ((latched & 0x3F) << 1) + i); + break; + case 2: + for (i = 0; i < 4; i++) + setprg8(0x8000 + (i << 13), ((latched & 0x3F) << 1) + (latched >> 7)); + break; + case 1: + case 3: + for (i = 0; i < 4; i++) { + unsigned int b; + b = latched & 0x3F; + if (i >= 2 && !(latchea & 0x2)) + b = b | 0x07; + setprg8(0x8000 + (i << 13), (i & 1) + (b << 1)); + } + break; + } + setchr8(0); +} + +static DECLFW(M15Write) { + latchea = A; + latched = V; + // cah4e3 02.10.19 once again, there may be either two similar mapper 15 exist. the one for 110in1 or 168in1 carts with complex multi game features. + // and another implified version for subor/waixing chinese originals and hacks with no different modes, working only in mode 0 and which does not + // expect there is any CHR write protection. protecting CHR writes only for mode 3 fixes the problem, all roms may be run on the same source again. + if((latchea & 3) == 3) + SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0); + else + SetupCartCHRMapping(0, CHRptr[0], 0x2000, 1); + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +static void M15Power(void) { + latchea = 0x8000; + latched = 0; + setprg8r(0x10, 0x6000, 0); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0xFFFF, M15Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + Sync(); +} + +static void M15Reset(void) { + latchea = 0x8000; + latched = 0; + Sync(); +} + +static void M15Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void Mapper15_Init(CartInfo *info) { + info->Power = M15Power; + info->Reset = M15Reset; + info->Close = M15Close; + GameStateRestore = StateRestore; + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/151.cpp b/apps/fceux/src/boards/151.cpp new file mode 100644 index 00000000..2d9335f1 --- /dev/null +++ b/apps/fceux/src/boards/151.cpp @@ -0,0 +1,59 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 regs[8]; + +static SFORMAT StateRegs[] = +{ + { regs, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x8000, regs[0]); + setprg8(0xA000, regs[2]); + setprg8(0xC000, regs[4]); + setprg8(0xE000, ~0); + setchr4(0x0000, regs[6]); + setchr4(0x1000, regs[7]); +} + +static DECLFW(M151Write) { + regs[(A >> 12) & 7] = V; + Sync(); +} + +static void M151Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M151Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper151_Init(CartInfo *info) { + info->Power = M151Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/156.cpp b/apps/fceux/src/boards/156.cpp new file mode 100644 index 00000000..66939620 --- /dev/null +++ b/apps/fceux/src/boards/156.cpp @@ -0,0 +1,114 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 chrlo[8], chrhi[8], prg, mirr, mirrisused = 0; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { &prg, 1, "PREG" }, + { chrlo, 8, "CRGL" }, + { chrhi, 8, "CRGH" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + uint32 i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chrlo[i] | (chrhi[i] << 8)); + setprg8r(0x10, 0x6000, 0); + setprg16(0x8000, prg); + setprg16(0xC000, ~0); + if (mirrisused) + setmirror(mirr ^ 1); + else + setmirror(MI_0); +} + +static DECLFW(M156Write) { + switch (A) { + case 0xC000: + case 0xC001: + case 0xC002: + case 0xC003: chrlo[A & 3] = V; Sync(); break; + case 0xC004: + case 0xC005: + case 0xC006: + case 0xC007: chrhi[A & 3] = V; Sync(); break; + case 0xC008: + case 0xC009: + case 0xC00A: + case 0xC00B: chrlo[4 + (A & 3)] = V; Sync(); break; + case 0xC00C: + case 0xC00D: + case 0xC00E: + case 0xC00F: chrhi[4 + (A & 3)] = V; Sync(); break; + case 0xC010: prg = V; Sync(); break; + case 0xC014: mirr = V; mirrisused = 1; Sync(); break; + } +} + +static void M156Reset(void) { + uint32 i; + for (i = 0; i < 8; i++) { + chrlo[i] = 0; + chrhi[i] = 0; + } + prg = 0; + mirr = 0; + mirrisused = 0; +} + +static void M156Power(void) { + M156Reset(); + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0xC000, 0xCFFF, M156Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M156Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper156_Init(CartInfo *info) { + info->Reset = M156Reset; + info->Power = M156Power; + info->Close = M156Close; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/158B.cpp b/apps/fceux/src/boards/158B.cpp new file mode 100644 index 00000000..765c8fc2 --- /dev/null +++ b/apps/fceux/src/boards/158B.cpp @@ -0,0 +1,71 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2015 CaH4e3 + * + * 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 + * + * "Blood Of Jurassic" protected MMC3 based board (GD-98 Cart ID, 158B PCB ID) + * + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 lut[8] = { 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0F, 0x00 }; + +static void UNL158BPW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x80) { + uint32 bank = EXPREGS[0] & 7; + if(EXPREGS[0] & 0x20) { // 32Kb mode + setprg32(0x8000, bank >> 1); + } else { // 16Kb mode + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else { + setprg8(A, V & 0xF); + } +} + +static DECLFW(UNL158BProtWrite) { + EXPREGS[A & 7] = V; + switch(A & 7) { + case 0: + FixMMC3PRG(MMC3_cmd); + break; + case 7: + FCEU_printf("UNK PROT WRITE\n"); + break; + } + +} + +static DECLFR(UNL158BProtRead) { + return X.DB | lut[A & 7]; +} + +static void UNL158BPower(void) { + GenMMC3Power(); + SetWriteHandler(0x5000, 0x5FFF, UNL158BProtWrite); + SetReadHandler(0x5000, 0x5FFF, UNL158BProtRead); +} + +void UNL158B_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 0, 0); + pwrap = UNL158BPW; + info->Power = UNL158BPower; + AddExState(EXPREGS, 8, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/164.cpp b/apps/fceux/src/boards/164.cpp new file mode 100644 index 00000000..e7121ad2 --- /dev/null +++ b/apps/fceux/src/boards/164.cpp @@ -0,0 +1,232 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel 2006 CaH4e3 + * + * 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 + * + * It seems that 162/163/164 mappers are the same mapper with just different + * mapper modes enabled or disabled in software or hardware, need more nanjing + * carts + */ + +#include "mapinc.h" + +static uint8 laststrobe, trigger; +static uint8 reg[8]; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static writefunc pcmwrite; + +static void (*WSync)(void); + +static SFORMAT StateRegs[] = +{ + { &laststrobe, 1, "STB" }, + { &trigger, 1, "TRG" }, + { reg, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, (reg[0] << 4) | (reg[1] & 0xF)); + setchr8(0); +} + +static void StateRestore(int version) { + WSync(); +} + +static DECLFR(ReadLow) { + switch (A & 0x7700) { + case 0x5100: return reg[2] | reg[0] | reg[1] | (reg[3] ^ 0xff); break; + case 0x5500: + if (trigger) + return reg[2] | reg[1]; // Lei Dian Huang Bi Ka Qiu Chuan Shuo (NJ046) may broke other games + else + return 0; + } + return 4; +} + +static void M163HB(void) { + if (reg[1] & 0x80) { + if (scanline == 239) { + setchr4(0x0000, 0); + setchr4(0x1000, 0); + } else if (scanline == 127) { + setchr4(0x0000, 1); + setchr4(0x1000, 1); + } +/* + if(scanline>=127) // Hu Lu Jin Gang (NJ039) (Ch) [!] don't like it + { + setchr4(0x0000,1); + setchr4(0x1000,1); + } + else + { + setchr4(0x0000,0); + setchr4(0x1000,0); + } +*/ + } +} + +static DECLFW(Write) { + switch (A & 0x7300) { + case 0x5100: reg[0] = V; WSync(); break; + case 0x5000: reg[1] = V; WSync(); break; + case 0x5300: reg[2] = V; break; + case 0x5200: reg[3] = V; WSync(); break; + } +} + +static void Power(void) { + memset(reg, 0, 8); + reg[1] = 0xFF; + SetWriteHandler(0x5000, 0x5FFF, Write); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + WSync(); +} + +static void Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void Mapper164_Init(CartInfo *info) { + info->Power = Power; + info->Close = Close; + WSync = Sync; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +static DECLFW(Write2) { + if (A == 0x5101) { + if (laststrobe && !V) { + trigger ^= 1; + } + laststrobe = V; + } else if (A == 0x5100 && V == 6) //damn thoose protected games + setprg32(0x8000, 3); + else + switch (A & 0x7300) { + case 0x5200: reg[0] = V; WSync(); break; + case 0x5000: reg[1] = V; WSync(); if (!(reg[1] & 0x80) && (scanline < 128)) setchr8(0); /* setchr8(0); */ break; + case 0x5300: reg[2] = V; break; + case 0x5100: reg[3] = V; WSync(); break; + } +} + +static void Power2(void) { + memset(reg, 0, 8); + laststrobe = 1; + pcmwrite = GetWriteHandler(0x4011); + SetReadHandler(0x5000, 0x5FFF, ReadLow); + SetWriteHandler(0x5000, 0x5FFF, Write2); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + WSync(); +} + +void Mapper163_Init(CartInfo *info) { + info->Power = Power2; + info->Close = Close; + WSync = Sync; + GameHBIRQHook = M163HB; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +static void Sync3(void) { + setchr8(0); + setprg8r(0x10, 0x6000, 0); + switch (reg[3] & 7) { + case 0: + case 2: setprg32(0x8000, (reg[0] & 0xc) | (reg[1] & 2) | ((reg[2] & 0xf) << 4)); break; + case 1: + case 3: setprg32(0x8000, (reg[0] & 0xc) | (reg[2] & 0xf) << 4); break; + case 4: + case 6: setprg32(0x8000, (reg[0] & 0xe) | ((reg[1] >> 1) & 1) | ((reg[2] & 0xf) << 4)); break; + case 5: + case 7: setprg32(0x8000, (reg[0] & 0xf) | ((reg[2] & 0xf) << 4)); break; + } +} + +static DECLFW(Write3) { +// FCEU_printf("bs %04x %02x\n",A,V); + reg[(A >> 8) & 3] = V; + WSync(); +} + +static void Power3(void) { + reg[0] = 3; + reg[1] = 0; + reg[2] = 0; + reg[3] = 7; + SetWriteHandler(0x5000, 0x5FFF, Write3); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + WSync(); +} + +void UNLFS304_Init(CartInfo *info) { + info->Power = Power3; + info->Close = Close; + WSync = Sync3; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/168.cpp b/apps/fceux/src/boards/168.cpp new file mode 100644 index 00000000..c85b221e --- /dev/null +++ b/apps/fceux/src/boards/168.cpp @@ -0,0 +1,78 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg; +static uint8 *CHRRAM = NULL; +static uint32 CHRRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setchr4r(0x10, 0x0000, 0); + setchr4r(0x10, 0x1000, reg & 0x0f); + setprg16(0x8000, reg >> 6); + setprg16(0xc000, ~0); +} + +static DECLFW(M168Write) { + reg = V; + Sync(); +} + +static DECLFW(M168Dummy) { +} + +static void M168Power(void) { + reg = 0; + Sync(); + SetWriteHandler(0x4020, 0x7fff, M168Dummy); + SetWriteHandler(0xB000, 0xB000, M168Write); + SetWriteHandler(0xF000, 0xF000, M168Dummy); + SetWriteHandler(0xF080, 0xF080, M168Dummy); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void M168Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper168_Init(CartInfo *info) { + info->Power = M168Power; + info->Close = M168Close; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); + + CHRRAMSIZE = 8192 * 8; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); +} diff --git a/apps/fceux/src/boards/170.cpp b/apps/fceux/src/boards/170.cpp new file mode 100644 index 00000000..9a8a8e50 --- /dev/null +++ b/apps/fceux/src/boards/170.cpp @@ -0,0 +1,62 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg16(0x8000, 0); + setprg16(0xc000, ~0); + setchr8(0); +} + +static DECLFW(M170ProtW) { + reg = V << 1 & 0x80; +} + +static DECLFR(M170ProtR) { + return reg | (X.DB & 0x7F); +} + +static void M170Power(void) { + Sync(); + SetWriteHandler(0x6502, 0x6502, M170ProtW); + SetWriteHandler(0x7000, 0x7000, M170ProtW); + SetReadHandler(0x7001, 0x7001, M170ProtR); + SetReadHandler(0x7777, 0x7777, M170ProtR); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper170_Init(CartInfo *info) { + info->Power = M170Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/175.cpp b/apps/fceux/src/boards/175.cpp new file mode 100644 index 00000000..82786074 --- /dev/null +++ b/apps/fceux/src/boards/175.cpp @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg, delay, mirr; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setchr8(reg); + if (!delay) { + setprg16(0x8000, reg); + setprg8(0xC000, reg << 1); + } + setprg8(0xE000, (reg << 1) + 1); + setmirror(((mirr & 4) >> 2) ^ 1); +} + +static DECLFW(M175Write1) { + mirr = V; + delay = 1; + Sync(); +} + +static DECLFW(M175Write2) { + reg = V & 0x0F; + delay = 1; + Sync(); +} + +static DECLFR(M175Read) { + if (A == 0xFFFC) { + delay = 0; + Sync(); + } + return CartBR(A); +} + +static void M175Power(void) { + reg = mirr = delay = 0; + SetReadHandler(0x8000, 0xFFFF, M175Read); + SetWriteHandler(0x8000, 0x8000, M175Write1); + SetWriteHandler(0xA000, 0xA000, M175Write2); + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper175_Init(CartInfo *info) { + info->Power = M175Power; + GameStateRestore = StateRestore; + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/176.cpp b/apps/fceux/src/boards/176.cpp new file mode 100644 index 00000000..f29ad08f --- /dev/null +++ b/apps/fceux/src/boards/176.cpp @@ -0,0 +1,159 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * Copyright (C) 2012 FCEUX team + * + * 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 + */ + +#include "mapinc.h" + +extern uint32 ROM_size; + +static uint8 prg[4], chr, sbw, we_sram; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[]= +{ + {prg, 4, "PRG"}, + {&chr, 1, "CHR"}, + {&sbw, 1, "SBW"}, + {0} +}; + +static void Sync(void) +{ + setprg8r(0x10,0x6000,0); + setprg8(0x8000,prg[0]); + setprg8(0xA000,prg[1]); + setprg8(0xC000,prg[2]); + setprg8(0xE000,prg[3]); + + setchr8(chr); +} + +static DECLFW(M176Write_5001) +{ + printf("%04X = $%02X\n",A,V); + if(sbw) + { + prg[0] = V*4; + prg[1] = V*4+1; + prg[2] = V*4+2; + prg[3] = V*4+3; + } + Sync(); +} + +static DECLFW(M176Write_5010) +{ + printf("%04X = $%02X\n",A,V); + if(V == 0x24) sbw = 1; + Sync(); +} + +static DECLFW(M176Write_5011) +{ + printf("%04X = $%02X\n",A,V); + V >>= 1; + if(sbw) + { + prg[0] = V*4; + prg[1] = V*4+1; + prg[2] = V*4+2; + prg[3] = V*4+3; + } + Sync(); +} + +static DECLFW(M176Write_5FF1) +{ + printf("%04X = $%02X\n",A,V); + V >>= 1; + prg[0] = V*4; + prg[1] = V*4+1; + prg[2] = V*4+2; + prg[3] = V*4+3; + Sync(); +} + +static DECLFW(M176Write_5FF2) +{ + printf("%04X = $%02X\n",A,V); + chr = V; + Sync(); +} + +static DECLFW(M176Write_A001) +{ + we_sram = V & 0x03; +} + +static DECLFW(M176Write_WriteSRAM) +{ +// if(we_sram) + CartBW(A,V); +} + +static void M176Power(void) +{ + SetReadHandler(0x6000,0x7fff,CartBR); + SetWriteHandler(0x6000,0x7fff,M176Write_WriteSRAM); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0xA001,0xA001,M176Write_A001); + SetWriteHandler(0x5001,0x5001,M176Write_5001); + SetWriteHandler(0x5010,0x5010,M176Write_5010); + SetWriteHandler(0x5011,0x5011,M176Write_5011); + SetWriteHandler(0x5ff1,0x5ff1,M176Write_5FF1); + SetWriteHandler(0x5ff2,0x5ff2,M176Write_5FF2); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + + we_sram = 0; + sbw = 0; + prg[0] = 0; + prg[1] = 1; + prg[2] = (ROM_size-2)&63; + prg[3] = (ROM_size-1)&63; + Sync(); +} + + +static void M176Close(void) +{ + if(WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) +{ + Sync(); +} + +void Mapper176_Init(CartInfo *info) +{ + info->Power=M176Power; + info->Close=M176Close; + + GameStateRestore=StateRestore; + + WRAMSIZE=8192; + WRAM=(uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10,WRAM,WRAMSIZE,1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/177.cpp b/apps/fceux/src/boards/177.cpp new file mode 100644 index 00000000..0416060e --- /dev/null +++ b/apps/fceux/src/boards/177.cpp @@ -0,0 +1,81 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg; + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, reg & 0x1f); + setmirror(((reg & 0x20) >> 5) ^ 1); +} + +static DECLFW(M177Write) { + reg = V; + Sync(); +} + +static void M177Power(void) { + reg = 0; + Sync(); + SetReadHandler(0x6000, 0x7fff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M177Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M177Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper177_Init(CartInfo *info) { + info->Power = M177Power; + info->Close = M177Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/178.cpp b/apps/fceux/src/boards/178.cpp new file mode 100644 index 00000000..a9b7e3d3 --- /dev/null +++ b/apps/fceux/src/boards/178.cpp @@ -0,0 +1,199 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2013 CaH4e3 + * + * 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 + * + * DSOUNDV1/FL-TR8MA boards (32K WRAM, 8/16M), 178 mapper boards (8K WRAM, 4/8M) + * Various Education Cartridges + * + */ + +#include "mapinc.h" + +static uint8 reg[4]; + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +// Tennis with VR sensor, very simple behaviour +extern void GetMouseData(uint32 (&md)[3]); +static uint32 MouseData[3], click, lastclick; +static int32 SensorDelay; + +// highly experimental, not actually working, just curious if it hapen to work with some other decoder +// SND Registers +static uint8 pcm_enable = 0; + +static SFORMAT StateRegs[] = +{ + { reg, 4, "REGS" }, + { 0 } +}; + +//static int16 step_size[49] = { +// 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, +// 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, +// 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, +// 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, +// 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 +//}; //49 items +//static int32 step_adj[16] = { -1, -1, -1, -1, 2, 5, 7, 9, -1, -1, -1, -1, 2, 5, 7, 9 }; + +//decode stuff +//static int32 jedi_table[16 * 49]; +//static int32 acc = 0; //ADPCM accumulator, initial condition must be 0 +//static int32 decstep = 0; //ADPCM decoding step, initial condition must be 0 + +//static void jedi_table_init() { +// int step, nib; +// +// for (step = 0; step < 49; step++) { +// for (nib = 0; nib < 16; nib++) { +// int value = (2 * (nib & 0x07) + 1) * step_size[step] / 8; +// jedi_table[step * 16 + nib] = ((nib & 0x08) != 0) ? -value : value; +// } +// } +//} + +//static uint8 decode(uint8 code) { +// acc += jedi_table[decstep + code]; +// if ((acc & ~0x7ff) != 0) // acc is > 2047 +// acc |= ~0xfff; +// else acc &= 0xfff; +// decstep += step_adj[code & 7] * 16; +// if (decstep < 0) decstep = 0; +// if (decstep > 48 * 16) decstep = 48 * 16; +// return (acc >> 8) & 0xff; +//} + +static void Sync(void) { + uint32 sbank = reg[1] & 0x7; + uint32 bbank = reg[2]; + setchr8(0); + setprg8r(0x10, 0x6000, reg[3] & 3); + if (reg[0] & 2) { // UNROM mode + setprg16(0x8000, (bbank << 3) | sbank); + if (reg[0] & 4) + setprg16(0xC000, (bbank << 3) | 6 | (reg[1] & 1)); + else + setprg16(0xC000, (bbank << 3) | 7); + } else { // NROM mode + uint32 bank = (bbank << 3) | sbank; + if (reg[0] & 4) { + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } else + setprg32(0x8000, bank >> 1); + } + setmirror((reg[0] & 1) ^ 1); +} + +static DECLFW(M178Write) { + reg[A & 3] = V; +// FCEU_printf("cmd %04x:%02x\n", A, V); + Sync(); +} + +static DECLFW(M178WriteSnd) { + if (A == 0x5800) { + if (V & 0xF0) { + pcm_enable = 1; +// pcmwrite(0x4011, (V & 0xF) << 3); +// pcmwrite(0x4011, decode(V & 0xf)); + } else + pcm_enable = 0; + }// else +// FCEU_printf("misc %04x:%02x\n", A, V); +} + +static DECLFR(M178ReadSnd) { + if (A == 0x5800) + return (X.DB & 0xBF) | ((pcm_enable ^ 1) << 6); + else + return X.DB; +} + +static DECLFR(M178ReadSensor) { + X6502_IRQEnd(FCEU_IQEXT); // hacky-hacky, actual reg is 6000 and it clear IRQ while reading, but then I need another mapper lol + return 0x00; +} + +static void M178Power(void) { + reg[0] = reg[1] = reg[2] = reg[3] = SensorDelay = 0; + Sync(); +// pcmwrite = GetWriteHandler(0x4011); + SetWriteHandler(0x4800, 0x4fff, M178Write); + SetWriteHandler(0x5800, 0x5fff, M178WriteSnd); + SetReadHandler(0x5800, 0x5fff, M178ReadSnd); + SetReadHandler(0x5000, 0x5000, M178ReadSensor); + SetReadHandler(0x6000, 0x7fff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); + SetReadHandler(0x8000, 0xffff, CartBR); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M178SndClk(int a) { + SensorDelay += a; + if(SensorDelay > 0x32768) { + SensorDelay -= 32768; + GetMouseData (MouseData); + lastclick = click; + click = MouseData[2] & 1; // to prevent from continuos IRQ trigger if button is held. + // actual circuit is just a D-C-R edge detector for IR-sensor + // triggered by the active IR bat. + if(lastclick && !click) + X6502_IRQBegin(FCEU_IQEXT); + } + +// if (pcm_enable) { +// pcm_latch -= a; +// if (pcm_latch <= 0) { +// pcm_latch += pcm_clock; +// pcm_enable = 0; +// } +// } +} + +static void M178Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper178_Init(CartInfo *info) { + info->Power = M178Power; + info->Close = M178Close; + GameStateRestore = StateRestore; + MapIRQHook = M178SndClk; + +// jedi_table_init(); + + WRAMSIZE = 32768; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/18.cpp b/apps/fceux/src/boards/18.cpp new file mode 100644 index 00000000..a9ac4b67 --- /dev/null +++ b/apps/fceux/src/boards/18.cpp @@ -0,0 +1,134 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg[4], creg[8]; +static uint8 IRQa, mirr; +static int32 IRQCount, IRQLatch; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { preg, 4, "PREG" }, + { creg, 8, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { &IRQLatch, 4, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + int i; + for (i = 0; i < 8; i++) setchr1(i << 10, creg[i]); + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, ~0); + if (mirr & 2) + setmirror(MI_0); + else + setmirror(mirr & 1); +} + +static DECLFW(M18WriteIRQ) { + switch (A & 0xF003) { + case 0xE000: IRQLatch &= 0xFFF0; IRQLatch |= (V & 0x0f) << 0x0; break; + case 0xE001: IRQLatch &= 0xFF0F; IRQLatch |= (V & 0x0f) << 0x4; break; + case 0xE002: IRQLatch &= 0xF0FF; IRQLatch |= (V & 0x0f) << 0x8; break; + case 0xE003: IRQLatch &= 0x0FFF; IRQLatch |= (V & 0x0f) << 0xC; break; + case 0xF000: IRQCount = IRQLatch; break; + case 0xF001: IRQa = V & 1; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xF002: mirr = V & 3; Sync(); break; + } +} + +static DECLFW(M18WritePrg) { + uint32 i = ((A >> 1) & 1) | ((A - 0x8000) >> 11); + preg[i] &= (0xF0) >> ((A & 1) << 2); + preg[i] |= (V & 0xF) << ((A & 1) << 2); + Sync(); +} + +static DECLFW(M18WriteChr) { + uint32 i = ((A >> 1) & 1) | ((A - 0xA000) >> 11); + creg[i] &= (0xF0) >> ((A & 1) << 2); + creg[i] |= (V & 0xF) << ((A & 1) << 2); + Sync(); +} + +static void M18Power(void) { + IRQa = 0; + preg[0] = 0; + preg[1] = 1; + preg[2] = ~1; + preg[3] = ~0; + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0x9FFF, M18WritePrg); + SetWriteHandler(0xA000, 0xDFFF, M18WriteChr); + SetWriteHandler(0xE000, 0xFFFF, M18WriteIRQ); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M18IRQHook(int a) { + if (IRQa && IRQCount) { + IRQCount -= a; + if (IRQCount <= 0) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = 0; + IRQa = 0; + } + } +} + +static void M18Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper18_Init(CartInfo *info) { + info->Power = M18Power; + info->Close = M18Close; + MapIRQHook = M18IRQHook; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/183.cpp b/apps/fceux/src/boards/183.cpp new file mode 100644 index 00000000..9ba8c2c4 --- /dev/null +++ b/apps/fceux/src/boards/183.cpp @@ -0,0 +1,111 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * Gimmick Bootleg (VRC4 mapper) + */ + +#include "mapinc.h" + +static uint8 prg[4], chr[8], mirr; +static uint8 IRQCount; +static uint8 IRQPre; +static uint8 IRQa; + +static SFORMAT StateRegs[] = +{ + { prg, 4, "PRG" }, + { chr, 8, "CHR" }, + { &mirr, 1, "MIRR" }, + { &IRQCount, 1, "IRQC" }, + { &IRQPre, 1, "IRQP" }, + { &IRQa, 1, "IRQA" }, + { 0 } +}; + +static void SyncPrg(void) { + setprg8(0x6000, prg[3]); + setprg8(0x8000, prg[0]); + setprg8(0xA000, prg[1]); + setprg8(0xC000, prg[2]); + setprg8(0xE000, ~0); +} + +static void SyncMirr(void) { + switch (mirr) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static void SyncChr(void) { + int i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chr[i]); +} + +static void StateRestore(int version) { + SyncPrg(); + SyncChr(); + SyncMirr(); +} + +static DECLFW(M183Write) { + if ((A & 0xF800) == 0x6800) { + prg[3] = A & 0x3F; + SyncPrg(); + } else if (((A & 0xF80C) >= 0xB000) && ((A & 0xF80C) <= 0xE00C)) { + int index = (((A >> 11) - 6) | (A >> 3)) & 7; + chr[index] = (chr[index] & (0xF0 >> (A & 4))) | ((V & 0x0F) << (A & 4)); + SyncChr(); + } else switch (A & 0xF80C) { + case 0x8800: prg[0] = V; SyncPrg(); break; + case 0xA800: prg[1] = V; SyncPrg(); break; + case 0xA000: prg[2] = V; SyncPrg(); break; + case 0x9800: mirr = V & 3; SyncMirr(); break; + case 0xF000: IRQCount = ((IRQCount & 0xF0) | (V & 0xF)); break; + case 0xF004: IRQCount = ((IRQCount & 0x0F) | ((V & 0xF) << 4)); break; + case 0xF008: IRQa = V; if (!V) IRQPre = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xF00C: IRQPre = 16; break; + } +} + +static void M183IRQCounter(void) { + if (IRQa) { + IRQCount++; + if ((IRQCount - IRQPre) == 238) + X6502_IRQBegin(FCEU_IQEXT); + } +} + +static void M183Power(void) { + IRQPre = IRQCount = IRQa = 0; + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0xFFFF, M183Write); + SyncPrg(); + SyncChr(); +} + +void Mapper183_Init(CartInfo *info) { + info->Power = M183Power; + GameHBIRQHook = M183IRQCounter; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/185.cpp b/apps/fceux/src/boards/185.cpp new file mode 100644 index 00000000..756864ee --- /dev/null +++ b/apps/fceux/src/boards/185.cpp @@ -0,0 +1,107 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + */ + +#include "mapinc.h" + +static uint8 *DummyCHR = NULL; +static uint8 datareg; +static void (*Sync)(void); + + +static SFORMAT StateRegs[] = +{ + { &datareg, 1, "DREG" }, + { 0 } +}; + +// on off +//1 0x0F, 0xF0 - Bird Week +//2 0x33, 0x00 - B-Wings +//3 0x11, 0x00 - Mighty Bomb Jack +//4 0x22, 0x20 - Sansuu 1 Nen, Sansuu 2 Nen +//5 0xFF, 0x00 - Sansuu 3 Nen +//6 0x21, 0x13 - Spy vs Spy +//7 0x20, 0x21 - Seicross + +static void Sync185(void) { + // little dirty eh? ;_) + if ((datareg & 3) && (datareg != 0x13)) // 1, 2, 3, 4, 5, 6 + setchr8(0); + else + setchr8r(0x10, 0); +} + +static void Sync181(void) { + if (!(datareg & 1)) // 7 + setchr8(0); + else + setchr8r(0x10, 0); +} + +static DECLFW(MWrite) { + datareg = V; + Sync(); +} + +static void MPower(void) { + datareg = 0; + Sync(); + setprg16(0x8000, 0); + setprg16(0xC000, ~0); + SetWriteHandler(0x8000, 0xFFFF, MWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void MClose(void) { + if (DummyCHR) + FCEU_gfree(DummyCHR); + DummyCHR = NULL; +} + +static void MRestore(int version) { + Sync(); +} + +void Mapper185_Init(CartInfo *info) { + Sync = Sync185; + info->Power = MPower; + info->Close = MClose; + GameStateRestore = MRestore; + DummyCHR = (uint8*)FCEU_gmalloc(8192); + int x; + for (x = 0; x < 8192; x++) + DummyCHR[x] = 0xff; + SetupCartCHRMapping(0x10, DummyCHR, 8192, 0); + AddExState(StateRegs, ~0, 0, 0); +} + +void Mapper181_Init(CartInfo *info) { + Sync = Sync181; + info->Power = MPower; + info->Close = MClose; + GameStateRestore = MRestore; + DummyCHR = (uint8*)FCEU_gmalloc(8192); + int x; + for (x = 0; x < 8192; x++) + DummyCHR[x] = 0xff; + SetupCartCHRMapping(0x10, DummyCHR, 8192, 0); + AddExState(StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/186.cpp b/apps/fceux/src/boards/186.cpp new file mode 100644 index 00000000..045a0da4 --- /dev/null +++ b/apps/fceux/src/boards/186.cpp @@ -0,0 +1,122 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * Family Study Box by Fukutake Shoten + * + * REG[0] R dddddddd / W ww---sss + * ddd - TAPE DATA BYTE (ready when IRQ occurs) + * sss - BRAM hi-bank + * ww - PRAM bank + * REG[1] R 0123---- / W ----PPPP + * 0 - ? + * 1 - ? + * 2 - ? + * 3 - ? + * PPPP- PROM bank + * REG[2] R -?--R--- / W A-BC-DEF + * 4 - ? + * R - sb4x power supply status (active low) + * A - ? + * B - ? + * C - ? + * D - ? + * E - ? + * F - ? + * + * BRAM0 4400-4FFF, 3K bank 0 (32K SWRAM) [hardwired] + * BRAMB 5000-5FFF, 4K banks 1-7 (32K SWRAM) [REG[0] W -----sss] + * PRAMB 6000-7FFF, 8K banks 1-3 (32K PRAM) [REG[0] W ww------] + * PROMB 8000-BFFF, 16K banks 1-15 (256K PROM)[REG[1] W ----PPPP] + * PROM0 C000-FFFF, 16K bank 0 (256K PROM) [hardwired] + * + */ + +#include "mapinc.h" + +static uint8 SWRAM[3072]; +static uint8 *WRAM = NULL; +static uint8 regs[4]; + +static SFORMAT StateRegs[] = +{ + { regs, 4, "DREG" }, + { SWRAM, 3072, "SWRM" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0x10, 0x6000, regs[0] >> 6); + setprg16(0x8000, regs[1]); + setprg16(0xc000, 0); +} + +static DECLFW(M186Write) { + if (A & 0x4203) regs[A & 3] = V; + Sync(); +} + +static DECLFR(M186Read) { + switch (A) { + case 0x4200: return 0x00; break; + case 0x4201: return 0x00; break; + case 0x4202: return 0x40; break; + case 0x4203: return 0x00; break; + } + return 0xFF; +} + +static DECLFR(ASWRAM) { + return(SWRAM[A - 0x4400]); +} +static DECLFW(BSWRAM) { + SWRAM[A - 0x4400] = V; +} + +static void M186Power(void) { + setchr8(0); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0xFFFF, CartBW); + SetReadHandler(0x4200, 0x43FF, M186Read); + SetWriteHandler(0x4200, 0x43FF, M186Write); + SetReadHandler(0x4400, 0x4FFF, ASWRAM); + SetWriteHandler(0x4400, 0x4FFF, BSWRAM); + FCEU_CheatAddRAM(32, 0x6000, WRAM); + regs[0] = regs[1] = regs[2] = regs[3]; + Sync(); +} + +static void M186Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void M186Restore(int version) { + Sync(); +} + +void Mapper186_Init(CartInfo *info) { + info->Power = M186Power; + info->Close = M186Close; + GameStateRestore = M186Restore; + WRAM = (uint8*)FCEU_gmalloc(32768); + SetupCartPRGMapping(0x10, WRAM, 32768, 1); + AddExState(WRAM, 32768, 0, "WRAM"); + AddExState(StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/187.cpp b/apps/fceux/src/boards/187.cpp new file mode 100644 index 00000000..6e5baa2e --- /dev/null +++ b/apps/fceux/src/boards/187.cpp @@ -0,0 +1,84 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void M187CW(uint32 A, uint8 V) { + if ((A & 0x1000) == ((MMC3_cmd & 0x80) << 5)) + setchr1(A, V | 0x100); + else + setchr1(A, V); +} + +static void M187PW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x80) { + uint8 bank = EXPREGS[0] & 0x1F; + if (EXPREGS[0] & 0x20) { + if (EXPREGS[0] & 0x40) + setprg32(0x8000, bank >> 2); + else + setprg32(0x8000, bank >> 1); // hacky hacky! two mappers in one! need real hw carts to test + } else { + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else + setprg8(A, V & 0x3F); +} + +static DECLFW(M187Write8000) { + EXPREGS[1] = 1; + MMC3_CMDWrite(A, V); +} + +static DECLFW(M187Write8001) { + if (EXPREGS[1]) + MMC3_CMDWrite(A, V); +} + +static DECLFW(M187WriteLo) { + if ((A == 0x5000) || (A == 0x6000)) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + } +} + +static uint8 prot_data[4] = { 0x83, 0x83, 0x42, 0x00 }; +static DECLFR(M187Read) { + return prot_data[EXPREGS[1] & 3]; +} + +static void M187Power(void) { + EXPREGS[0] = EXPREGS[1] = 0; + GenMMC3Power(); + SetReadHandler(0x5000, 0x5FFF, M187Read); + SetWriteHandler(0x5000, 0x6FFF, M187WriteLo); + SetWriteHandler(0x8000, 0x8000, M187Write8000); + SetWriteHandler(0x8001, 0x8001, M187Write8001); +} + +void Mapper187_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + pwrap = M187PW; + cwrap = M187CW; + info->Power = M187Power; + AddExState(EXPREGS, 3, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/189.cpp b/apps/fceux/src/boards/189.cpp new file mode 100644 index 00000000..fb60b1b8 --- /dev/null +++ b/apps/fceux/src/boards/189.cpp @@ -0,0 +1,44 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void M189PW(uint32 A, uint8 V) { + setprg32(0x8000, EXPREGS[0] & 7); +} + +static DECLFW(M189Write) { + EXPREGS[0] = V | (V >> 4); //actually, there is a two versions of 189 mapper with hi or lo bits bankswitching. + FixMMC3PRG(MMC3_cmd); +} + +static void M189Power(void) { + EXPREGS[0] = EXPREGS[1] = 0; + GenMMC3Power(); + SetWriteHandler(0x4120, 0x7FFF, M189Write); +} + +void Mapper189_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + pwrap = M189PW; + info->Power = M189Power; + AddExState(EXPREGS, 2, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/190.cpp b/apps/fceux/src/boards/190.cpp new file mode 100644 index 00000000..85b47304 --- /dev/null +++ b/apps/fceux/src/boards/190.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright (C) 2017 FCEUX Team + * + * 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 + * + * Magic Kid GooGoo + */ + +#include "mapinc.h" + +static uint8 prgr, chrr[4]; +static uint8 *WRAM = NULL; + +static void Mapper190_Sync(void) { + setprg8r(0x10, 0x6000, 0); + + setprg16(0x8000, prgr); + setprg16(0xC000, 0); + setchr2(0x0000, chrr[0]); + setchr2(0x0800, chrr[1]); + setchr2(0x1000, chrr[2]); + setchr2(0x1800, chrr[3]); +} + +static DECLFW(Mapper190_Write89) { prgr = V&7; Mapper190_Sync(); } +static DECLFW(Mapper190_WriteCD) { prgr = 8|(V&7); Mapper190_Sync(); } + +static DECLFW(Mapper190_WriteAB) { + int bank = A&3; + chrr[bank] = V&63; + Mapper190_Sync(); +} + + +static void Mapper190_Power(void) { + FCEU_CheatAddRAM(0x2000 >> 10, 0x6000, WRAM); + + SetReadHandler(0x6000, 0xFFFF, CartBR); + + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0x9FFF, Mapper190_Write89); + SetWriteHandler(0xA000, 0xBFFF, Mapper190_WriteAB); + SetWriteHandler(0xC000, 0xDFFF, Mapper190_WriteCD); + Mapper190_Sync(); + + setmirror(MI_V); +} + +static void Mapper190_Close(void) { + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void Mapper190_Restore(int) { + Mapper190_Sync(); +} + + +void Mapper190_Init(CartInfo *info) { + info->Power = Mapper190_Power; + info->Close = Mapper190_Close; + GameStateRestore = Mapper190_Restore; + + WRAM = (uint8*)FCEU_gmalloc(0x2000); + SetupCartPRGMapping(0x10, WRAM, 0x2000, 1); + + chrr[0] = chrr[1] = chrr[2] = chrr[3] = prgr = 0; + + AddExState(&prgr, 1, 0, "PRGR"); + AddExState(chrr, 4, 0, "CHRR"); + AddExState(WRAM, 0x2000, 0, "WRAM"); +} diff --git a/apps/fceux/src/boards/193.cpp b/apps/fceux/src/boards/193.cpp new file mode 100644 index 00000000..12e96a16 --- /dev/null +++ b/apps/fceux/src/boards/193.cpp @@ -0,0 +1,71 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg[8]; +static uint8 mirror, cmd, bank; + +static SFORMAT StateRegs[] = +{ + { &cmd, 1, "CMD" }, + { &mirror, 1, "MIRR" }, + { &bank, 1, "BANK" }, + { reg, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setmirror(mirror ^ 1); + setprg8(0x8000, reg[3]); + setprg8(0xA000, 0xD); + setprg8(0xC000, 0xE); + setprg8(0xE000, 0xF); + setchr4(0x0000, reg[0] >> 2); + setchr2(0x1000, reg[1] >> 1); + setchr2(0x1800, reg[2] >> 1); +} + +static DECLFW(M193Write) { + reg[A & 3] = V; + Sync(); +} + +static void M193Power(void) { + bank = 0; + Sync(); + SetWriteHandler(0x6000, 0x6003, M193Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, CartBW); +} + +static void M193Reset(void) { +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper193_Init(CartInfo *info) { + info->Reset = M193Reset; + info->Power = M193Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/199.cpp b/apps/fceux/src/boards/199.cpp new file mode 100644 index 00000000..2a8ad051 --- /dev/null +++ b/apps/fceux/src/boards/199.cpp @@ -0,0 +1,97 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + * Dragon Ball Z 2 - Gekishin Freeza! (C) + * Dragon Ball Z Gaiden - Saiya Jin Zetsumetsu Keikaku (C) + * San Guo Zhi 2 (C) + * + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 *CHRRAM = NULL; +static uint32 CHRRAMSIZE; + +static void M199PW(uint32 A, uint8 V) { + setprg8(A, V); + setprg8(0xC000, EXPREGS[0]); + setprg8(0xE000, EXPREGS[1]); +} + +static void M199CW(uint32 A, uint8 V) { + setchr1r((V < 8) ? 0x10 : 0x00, A, V); + setchr1r((DRegBuf[0] < 8) ? 0x10 : 0x00, 0x0000, DRegBuf[0]); + setchr1r((EXPREGS[2] < 8) ? 0x10 : 0x00, 0x0400, EXPREGS[2]); + setchr1r((DRegBuf[1] < 8) ? 0x10 : 0x00, 0x0800, DRegBuf[1]); + setchr1r((EXPREGS[3] < 8) ? 0x10 : 0x00, 0x0c00, EXPREGS[3]); +} + +static void M199MW(uint8 V) { +// FCEU_printf("%02x\n",V); + switch (V & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(M199Write) { + if ((A == 0x8001) && (MMC3_cmd & 8)) { + EXPREGS[MMC3_cmd & 3] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else + if (A < 0xC000) + MMC3_CMDWrite(A, V); + else + MMC3_IRQWrite(A, V); +} + +static void M199Power(void) { + EXPREGS[0] = ~1; + EXPREGS[1] = ~0; + EXPREGS[2] = 1; + EXPREGS[3] = 3; + GenMMC3Power(); + SetWriteHandler(0x8000, 0xFFFF, M199Write); +} + +static void M199Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +void Mapper199_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M199CW; + pwrap = M199PW; + mwrap = M199MW; + info->Power = M199Power; + info->Close = M199Close; + + CHRRAMSIZE = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); + + AddExState(EXPREGS, 4, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/206.cpp b/apps/fceux/src/boards/206.cpp new file mode 100644 index 00000000..d013a8f1 --- /dev/null +++ b/apps/fceux/src/boards/206.cpp @@ -0,0 +1,78 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 cmd; +static uint8 DRegs[8]; + +static SFORMAT StateRegs[] = +{ + { &cmd, 1, "CMD" }, + { DRegs, 8, "DREG" }, + { 0 } +}; + +static void Sync(void) { + setchr2(0x0000, DRegs[0]); + setchr2(0x0800, DRegs[1]); + int x; + for (x = 0; x < 4; x++) + setchr1(0x1000 + (x << 10), DRegs[2 + x]); + setprg8(0x8000, DRegs[6]); + setprg8(0xa000, DRegs[7]); + setprg8(0xc000, ~1); + setprg8(0xe000, ~0); +} + +static void StateRestore(int version) { + Sync(); +} + +static DECLFW(M206Write) { + switch (A & 0x8001) { + case 0x8000: cmd = V & 0x07; break; + case 0x8001: + if (cmd <= 0x05) + V &= 0x3F; + else + V &= 0x0F; + if (cmd <= 0x01) V >>= 1; + DRegs[cmd & 0x07] = V; + Sync(); + break; + } +} + +static void M206Power(void) { + cmd = 0; + DRegs[6] = 0; + DRegs[7] = 1; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M206Write); +} + + +void Mapper206_Init(CartInfo *info) { + info->Power = M206Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/208.cpp b/apps/fceux/src/boards/208.cpp new file mode 100644 index 00000000..d581b1e8 --- /dev/null +++ b/apps/fceux/src/boards/208.cpp @@ -0,0 +1,78 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 lut[256] = { + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x49, 0x19, 0x09, 0x59, 0x49, 0x19, 0x09, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x51, 0x41, 0x11, 0x01, 0x51, 0x41, 0x11, 0x01, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x49, 0x19, 0x09, 0x59, 0x49, 0x19, 0x09, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x51, 0x41, 0x11, 0x01, 0x51, 0x41, 0x11, 0x01, + 0x00, 0x10, 0x40, 0x50, 0x00, 0x10, 0x40, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x18, 0x48, 0x58, 0x08, 0x18, 0x48, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x40, 0x50, 0x00, 0x10, 0x40, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x18, 0x48, 0x58, 0x08, 0x18, 0x48, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x58, 0x48, 0x18, 0x08, 0x58, 0x48, 0x18, 0x08, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x50, 0x40, 0x10, 0x00, 0x50, 0x40, 0x10, 0x00, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x58, 0x48, 0x18, 0x08, 0x58, 0x48, 0x18, 0x08, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x50, 0x40, 0x10, 0x00, 0x50, 0x40, 0x10, 0x00, + 0x01, 0x11, 0x41, 0x51, 0x01, 0x11, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x19, 0x49, 0x59, 0x09, 0x19, 0x49, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x11, 0x41, 0x51, 0x01, 0x11, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x19, 0x49, 0x59, 0x09, 0x19, 0x49, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static void M208PW(uint32 A, uint8 V) { + setprg32(0x8000, EXPREGS[5]); +} + +static DECLFW(M208Write) { + EXPREGS[5] = (V & 0x1) | ((V >> 3) & 0x2); + FixMMC3PRG(MMC3_cmd); +} + +static DECLFW(M208ProtWrite) { + if (A <= 0x57FF) + EXPREGS[4] = V; + else + EXPREGS[(A & 0x03)] = V ^ lut[EXPREGS[4]]; +} + +static DECLFR(M208ProtRead) { + return(EXPREGS[(A & 0x3)]); +} + +static void M208Power(void) { + EXPREGS[5] = 3; + GenMMC3Power(); + SetWriteHandler(0x4800, 0x4fff, M208Write); + SetWriteHandler(0x6800, 0x6fff, M208Write); + SetWriteHandler(0x5000, 0x5fff, M208ProtWrite); + SetReadHandler(0x5800, 0x5fff, M208ProtRead); + SetReadHandler(0x8000, 0xffff, CartBR); +} + +void Mapper208_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 0, 0); + pwrap = M208PW; + info->Power = M208Power; + AddExState(EXPREGS, 6, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/222.cpp b/apps/fceux/src/boards/222.cpp new file mode 100644 index 00000000..0db78c7b --- /dev/null +++ b/apps/fceux/src/boards/222.cpp @@ -0,0 +1,99 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * (VRC4 mapper) + * + */ + +#include "mapinc.h" + +static uint8 IRQCount; +static uint8 IRQa; +static uint8 prg_reg[2]; +static uint8 chr_reg[8]; +static uint8 mirr; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 1, "IRQC" }, + { &IRQa, 1, "IRQA" }, + { prg_reg, 2, "PRG" }, + { chr_reg, 8, "CHR" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void M222IRQ(void) { + if (IRQa) { + IRQCount++; + if (IRQCount >= 238) { + X6502_IRQBegin(FCEU_IQEXT); +// IRQa=0; + } + } +} + +static void Sync(void) { + setprg8(0x8000, prg_reg[0]); + setprg8(0xA000, prg_reg[1]); + int i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chr_reg[i]); + setmirror(mirr ^ 1); +} + +static DECLFW(M222Write) { + switch (A & 0xF003) { + case 0x8000: prg_reg[0] = V; break; + case 0x9000: mirr = V & 1; break; + case 0xA000: prg_reg[1] = V; break; + case 0xB000: chr_reg[0] = V; break; + case 0xB002: chr_reg[1] = V; break; + case 0xC000: chr_reg[2] = V; break; + case 0xC002: chr_reg[3] = V; break; + case 0xD000: chr_reg[4] = V; break; + case 0xD002: chr_reg[5] = V; break; + case 0xE000: chr_reg[6] = V; break; + case 0xE002: chr_reg[7] = V; break; +// case 0xF000: FCEU_printf("%04x:%02x %d\n",A,V,scanline); IRQa=V; if(!V)IRQPre=0; X6502_IRQEnd(FCEU_IQEXT); break; +// case 0xF001: FCEU_printf("%04x:%02x %d\n",A,V,scanline); IRQCount=V; break; +// case 0xF002: FCEU_printf("%04x:%02x %d\n",A,V,scanline); break; +// case 0xD001: IRQa=V; X6502_IRQEnd(FCEU_IQEXT); FCEU_printf("%04x:%02x %d\n",A,V,scanline); break; +// case 0xC001: IRQPre=16; FCEU_printf("%04x:%02x %d\n",A,V,scanline); break; + case 0xF000: IRQa = IRQCount = V; if (scanline < 240) IRQCount -= 8; else IRQCount += 4; X6502_IRQEnd(FCEU_IQEXT); break; + } + Sync(); +} + +static void M222Power(void) { + setprg16(0xC000, ~0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M222Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper222_Init(CartInfo *info) { + info->Power = M222Power; + GameHBIRQHook = M222IRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/225.cpp b/apps/fceux/src/boards/225.cpp new file mode 100644 index 00000000..fd008ea2 --- /dev/null +++ b/apps/fceux/src/boards/225.cpp @@ -0,0 +1,86 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 prot[4], prg, mode, chr, mirr; + +static SFORMAT StateRegs[] = +{ + { prot, 4, "PROT" }, + { &prg, 1, "PRG" }, + { &chr, 1, "CHR" }, + { &mode, 1, "MODE" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + if (mode) { + setprg16(0x8000, prg); + setprg16(0xC000, prg); + } else + setprg32(0x8000, prg >> 1); + setchr8(chr); + setmirror(mirr ^ 1); +} + +static DECLFW(M225Write) { + uint32 bank = (A >> 14) & 1; + mirr = (A >> 13) & 1; + mode = (A >> 12) & 1; + chr = (A & 0x3f) | (bank << 6); + prg = ((A >> 6) & 0x3f) | (bank << 6); + Sync(); +} + +static DECLFW(M225LoWrite) { +} + +static DECLFR(M225LoRead) { + return 0; +} + +static void M225Power(void) { + prg = 0; + mode = 0; + Sync(); + SetReadHandler(0x5000, 0x5FFF, M225LoRead); + SetWriteHandler(0x5000, 0x5FFF, M225LoWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M225Write); +} + +static void M225Reset(void) { + prg = 0; + mode = 0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper225_Init(CartInfo *info) { + info->Reset = M225Reset; + info->Power = M225Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/228.cpp b/apps/fceux/src/boards/228.cpp new file mode 100644 index 00000000..e64c6ace --- /dev/null +++ b/apps/fceux/src/boards/228.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 mram[4], vreg; +static uint16 areg; + +static SFORMAT StateRegs[] = +{ + { mram, 4, "MRAM" }, + { &areg, 2, "AREG" }, + { &vreg, 1, "VREG" }, + { 0 } +}; + +static void Sync(void) { + uint32 prgl, prgh, page = (areg >> 7) & 0x3F; + if ((page & 0x30) == 0x30) + page -= 0x10; + prgl = prgh = (page << 1) + (((areg >> 6) & 1) & ((areg >> 5) & 1)); + prgh += ((areg >> 5) & 1) ^ 1; + + setmirror(((areg >> 13) & 1) ^ 1); + setprg16(0x8000,prgl); + setprg16(0xc000,prgh); + setchr8(((vreg & 0x3) | ((areg & 0xF) << 2))); +} + +static DECLFW(M228RamWrite) { + mram[A & 3] = V & 0x0F; +} + +static DECLFR(M228RamRead) { + return mram[A & 3]; +} + +static DECLFW(M228Write) { + areg = A; + vreg = V; + Sync(); +} + +static void M228Reset(void) { + areg = 0x8000; + vreg = 0; + memset(mram, 0, sizeof(mram)); + Sync(); +} + +static void M228Power(void) { + M228Reset(); + SetReadHandler(0x5000,0x5FFF,M228RamRead); + SetWriteHandler(0x5000,0x5FFF,M228RamWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M228Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper228_Init(CartInfo *info) { + info->Reset = M228Reset; + info->Power = M228Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/230.cpp b/apps/fceux/src/boards/230.cpp new file mode 100644 index 00000000..0da975dd --- /dev/null +++ b/apps/fceux/src/boards/230.cpp @@ -0,0 +1,78 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * Copyright (C) 2009 qeed + * + * 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 + * + * 22 + Contra Reset based custom mapper... + * + */ + +#include "mapinc.h" + +static uint8 latche, reset; +static SFORMAT StateRegs[] = +{ + { &reset, 1, "RST" }, + { &latche, 1, "LATC" }, + { 0 } +}; + +static void Sync(void) { + if(reset) { + setprg16(0x8000, latche & 7); + setprg16(0xC000, 7); + setmirror(MI_V); + } else { + uint32 bank = (latche & 0x1F) + 8; + if (latche & 0x20) { + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } else + setprg32(0x8000, bank >> 1); + setmirror((latche >> 6) & 1); + } + setchr8(0); +} + +static DECLFW(M230Write) { + latche = V; + Sync(); +} + +static void M230Reset(void) { + reset ^= 1; + Sync(); +} + +static void M230Power(void) { + latche = reset = 0; + Sync(); + SetWriteHandler(0x8000, 0xFFFF, M230Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper230_Init(CartInfo *info) { + info->Power = M230Power; + info->Reset = M230Reset; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/232.cpp b/apps/fceux/src/boards/232.cpp new file mode 100644 index 00000000..86bb0d04 --- /dev/null +++ b/apps/fceux/src/boards/232.cpp @@ -0,0 +1,68 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 bank, preg; +static SFORMAT StateRegs[] = +{ + { &bank, 1, "BANK" }, + { &preg, 1, "PREG" }, + { 0 } +}; + +static void Sync(void) { +// uint32 bbank = (bank & 0x18) >> 1; + uint32 bbank = ((bank & 0x10) >> 2) | (bank & 8); // some dumps have bbanks swapped, if swap commands, + // then all roms can be played, but with some swapped + // games in menu. if not, some dumps are unplayable + // make hard dump for both cart types to check + setprg16(0x8000, bbank | (preg & 3)); + setprg16(0xC000, bbank | 3); + setchr8(0); +} + +static DECLFW(M232WriteBank) { + bank = V; + Sync(); +} + +static DECLFW(M232WritePreg) { + preg = V; + Sync(); +} + +static void M232Power(void) { + bank = preg = 0; + Sync(); + SetWriteHandler(0x8000, 0xBFFF, M232WriteBank); + SetWriteHandler(0xC000, 0xFFFF, M232WritePreg); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper232_Init(CartInfo *info) { + info->Power = M232Power; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/234.cpp b/apps/fceux/src/boards/234.cpp new file mode 100644 index 00000000..ef471238 --- /dev/null +++ b/apps/fceux/src/boards/234.cpp @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 bank, preg; +static SFORMAT StateRegs[] = +{ + { &bank, 1, "BANK" }, + { &preg, 1, "PREG" }, + { 0 } +}; + +static void Sync(void) { + if (bank & 0x40) { + setprg32(0x8000, (bank & 0xE) | (preg & 1)); + setchr8(((bank & 0xE) << 2) | ((preg >> 4) & 7)); + } else { + setprg32(0x8000, bank & 0xF); + setchr8(((bank & 0xF) << 2) | ((preg >> 4) & 3)); + } + setmirror((bank >> 7) ^ 1); +} + +DECLFR(M234ReadBank) { + uint8 r = CartBR(A); + if (!bank) { + bank = r; + Sync(); + } + return r; +} + +DECLFR(M234ReadPreg) { + uint8 r = CartBR(A); + preg = r; + Sync(); + return r; +} + +static void M234Reset(void) { + bank = preg = 0; + Sync(); +} + +static void M234Power(void) { + M234Reset(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0xFF80, 0xFF9F, M234ReadBank); + SetReadHandler(0xFFE8, 0xFFF7, M234ReadPreg); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper234_Init(CartInfo *info) { + info->Power = M234Power; + info->Reset = M234Reset; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/235.cpp b/apps/fceux/src/boards/235.cpp new file mode 100644 index 00000000..98ce11bb --- /dev/null +++ b/apps/fceux/src/boards/235.cpp @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint16 cmdreg; +static SFORMAT StateRegs[] = +{ + { &cmdreg, 2, "CREG" }, + { 0 } +}; + +static void Sync(void) { + if (cmdreg & 0x400) + setmirror(MI_0); + else + setmirror(((cmdreg >> 13) & 1) ^ 1); + if (cmdreg & 0x800) { + setprg16(0x8000, ((cmdreg & 0x300) >> 3) | ((cmdreg & 0x1F) << 1) | ((cmdreg >> 12) & 1)); + setprg16(0xC000, ((cmdreg & 0x300) >> 3) | ((cmdreg & 0x1F) << 1) | ((cmdreg >> 12) & 1)); + } else + setprg32(0x8000, ((cmdreg & 0x300) >> 4) | (cmdreg & 0x1F)); +} + +static DECLFW(M235Write) { + cmdreg = A; + Sync(); +} + +static void M235Power(void) { + setchr8(0); + SetWriteHandler(0x8000, 0xFFFF, M235Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); + cmdreg = 0; + Sync(); +} + +static void M235Restore(int version) { + Sync(); +} + +void Mapper235_Init(CartInfo *info) { + info->Power = M235Power; + GameStateRestore = M235Restore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/244.cpp b/apps/fceux/src/boards/244.cpp new file mode 100644 index 00000000..67c33694 --- /dev/null +++ b/apps/fceux/src/boards/244.cpp @@ -0,0 +1,77 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg, creg; +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { &creg, 1, "CREG" }, + { 0 } +}; + +static uint8 prg_perm[4][4] = { + { 0, 1, 2, 3, }, + { 3, 2, 1, 0, }, + { 0, 2, 1, 3, }, + { 3, 1, 2, 0, }, +}; + +static uint8 chr_perm[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7, }, + { 0, 2, 1, 3, 4, 6, 5, 7, }, + { 0, 1, 4, 5, 2, 3, 6, 7, }, + { 0, 4, 1, 5, 2, 6, 3, 7, }, + { 0, 4, 2, 6, 1, 5, 3, 7, }, + { 0, 2, 4, 6, 1, 3, 5, 7, }, + { 7, 6, 5, 4, 3, 2, 1, 0, }, + { 7, 6, 5, 4, 3, 2, 1, 0, }, +}; + +static void Sync(void) { + setprg32(0x8000, preg); + setchr8(creg); +} + +static DECLFW(M244Write) { + if (V & 8) + creg = chr_perm[(V >> 4) & 7][V & 7]; + else + preg = prg_perm[(V >> 4) & 3][V & 3]; + Sync(); +} + +static void M244Power(void) { + preg = creg = 0; + Sync(); + SetWriteHandler(0x8000, 0xFFFF, M244Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper244_Init(CartInfo *info) { + info->Power = M244Power; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/246.cpp b/apps/fceux/src/boards/246.cpp new file mode 100644 index 00000000..ec069deb --- /dev/null +++ b/apps/fceux/src/boards/246.cpp @@ -0,0 +1,87 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 regs[8]; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { regs, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg2r(0x10, 0x6800, 0); + setprg8(0x8000, regs[0]); + setprg8(0xA000, regs[1]); + setprg8(0xC000, regs[2]); + setprg8(0xE000, regs[3]); + setchr2(0x0000, regs[4]); + setchr2(0x0800, regs[5]); + setchr2(0x1000, regs[6]); + setchr2(0x1800, regs[7]); +} + +static DECLFW(M246Write) { + regs[A & 7] = V; + Sync(); +} + +static void M246Power(void) { + regs[0] = regs[1] = regs[2] = regs[3] = ~0; + Sync(); + SetWriteHandler(0x6000, 0x67FF, M246Write); + SetReadHandler(0x6800, 0x6FFF, CartBR); + SetWriteHandler(0x6800, 0x6FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M246Close(void) +{ + if(WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper246_Init(CartInfo *info) { + info->Power = M246Power; + info->Close = M246Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 2048; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/252.cpp b/apps/fceux/src/boards/252.cpp new file mode 100644 index 00000000..9962e088 --- /dev/null +++ b/apps/fceux/src/boards/252.cpp @@ -0,0 +1,135 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 creg[8], preg[2]; +static int32 IRQa, IRQCount, IRQClock, IRQLatch; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static uint8 *CHRRAM = NULL; +static uint32 CHRRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { creg, 8, "CREG" }, + { preg, 2, "PREG" }, + { &IRQa, 4, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { &IRQLatch, 4, "IRQL" }, + { &IRQClock, 4, "IRQK" }, + { 0 } +}; + +static void Sync(void) { + uint8 i; + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, preg[0]); + setprg8(0xa000, preg[1]); + setprg8(0xc000, ~1); + setprg8(0xe000, ~0); + for (i = 0; i < 8; i++) + if ((creg[i] == 6) || (creg[i] == 7)) + setchr1r(0x10, i << 10, creg[i] & 1); + else + setchr1(i << 10, creg[i]); +} + +static DECLFW(M252Write) { + if ((A >= 0xB000) && (A <= 0xEFFF)) { + uint8 ind = ((((A & 8) | (A >> 8)) >> 3) + 2) & 7; + uint8 sar = A & 4; + creg[ind] = (creg[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar); + Sync(); + } else + switch (A & 0xF00C) { + case 0x8000: + case 0x8004: + case 0x8008: + case 0x800C: preg[0] = V; Sync(); break; + case 0xA000: + case 0xA004: + case 0xA008: + case 0xA00C: preg[1] = V; Sync(); break; + case 0xF000: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0xF0; IRQLatch |= V & 0xF; break; + case 0xF004: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0x0F; IRQLatch |= V << 4; break; + case 0xF008: X6502_IRQEnd(FCEU_IQEXT); IRQClock = 0; IRQCount = IRQLatch; IRQa = V & 2; break; + } +} + +static void M252Power(void) { + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M252Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M252IRQ(int a) { + #define LCYCS 341 + if (IRQa) { + IRQClock += a * 3; + if (IRQClock >= LCYCS) { + while (IRQClock >= LCYCS) { + IRQClock -= LCYCS; + IRQCount++; + if (IRQCount & 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } + } + } +} + +static void M252Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + if (CHRRAM) + FCEU_gfree(CHRRAM); + WRAM = CHRRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper252_Init(CartInfo *info) { + info->Power = M252Power; + info->Close = M252Close; + MapIRQHook = M252IRQ; + + CHRRAMSIZE = 2048; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/253.cpp b/apps/fceux/src/boards/253.cpp new file mode 100644 index 00000000..3ed9bfec --- /dev/null +++ b/apps/fceux/src/boards/253.cpp @@ -0,0 +1,152 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 chrlo[8], chrhi[8], prg[2], mirr, vlock; +static int32 IRQa, IRQCount, IRQLatch, IRQClock; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static uint8 *CHRRAM = NULL; +static uint32 CHRRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { chrlo, 8, "CHRL" }, + { chrhi, 8, "CHRH" }, + { prg, 2, "PRGR" }, + { &mirr, 1, "MIRR" }, + { &vlock, 1, "VLCK" }, + { &IRQa, 4, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { &IRQLatch, 4, "IRQL" }, + { &IRQClock, 4, "IRQK" }, + { 0 } +}; + +static void Sync(void) { + uint8 i; + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, prg[0]); + setprg8(0xa000, prg[1]); + setprg8(0xc000, ~1); + setprg8(0xe000, ~0); + for (i = 0; i < 8; i++) { + uint32 chr = chrlo[i] | (chrhi[i] << 8); + if (((chrlo[i] == 4) || (chrlo[i] == 5)) && !vlock) + setchr1r(0x10, i << 10, chr & 1); + else + setchr1(i << 10, chr); + } + switch (mirr) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(M253Write) { + if ((A >= 0xB000) && (A <= 0xE00C)) { + uint8 ind = ((((A & 8) | (A >> 8)) >> 3) + 2) & 7; + uint8 sar = A & 4; + uint8 clo = (chrlo[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar); + chrlo[ind] = clo; + if (ind == 0) { + if (clo == 0xc8) + vlock = 0; + else if (clo == 0x88) + vlock = 1; + } + if (sar) + chrhi[ind] = V >> 4; + Sync(); + } else + switch (A) { + case 0x8010: prg[0] = V; Sync(); break; + case 0xA010: prg[1] = V; Sync(); break; + case 0x9400: mirr = V & 3; Sync(); break; + case 0xF000: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0xF0; IRQLatch |= V & 0xF; break; + case 0xF004: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0x0F; IRQLatch |= V << 4; break; + case 0xF008: X6502_IRQEnd(FCEU_IQEXT); IRQClock = 0; IRQCount = IRQLatch; IRQa = V & 2; break; + } +} + +static void M253Power(void) { + vlock = 0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M253Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M253Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + if (CHRRAM) + FCEU_gfree(CHRRAM); + WRAM = CHRRAM = NULL; +} + +static void M253IRQ(int a) { + #define LCYCS 341 + if (IRQa) { + IRQClock += a * 3; + if (IRQClock >= LCYCS) { + while (IRQClock >= LCYCS) { + IRQClock -= LCYCS; + IRQCount++; + if (IRQCount & 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper253_Init(CartInfo *info) { + info->Power = M253Power; + info->Close = M253Close; + MapIRQHook = M253IRQ; + GameStateRestore = StateRestore; + + CHRRAMSIZE = 2048; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/28.cpp b/apps/fceux/src/boards/28.cpp new file mode 100644 index 00000000..e476b3f2 --- /dev/null +++ b/apps/fceux/src/boards/28.cpp @@ -0,0 +1,215 @@ +/* + Copyright (C) 2012-2017 FCEUX team + + This file 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 file 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 the this software. If not, see . +*/ + +#include "mapinc.h" + +// http://wiki.nesdev.com/w/index.php/INES_Mapper_028 + +//config +static int prg_mask_16k; + +// state +uint8 reg; +uint8 chr; +uint8 prg; +uint8 mode; +uint8 outer; + +void SyncMirror() +{ + switch (mode & 3) + { + case 0: setmirror(MI_0); break; + case 1: setmirror(MI_1); break; + case 2: setmirror(MI_V); break; + case 3: setmirror(MI_H); break; + } +} + +void Mirror(uint8 value) +{ + if ((mode & 2) == 0) + { + mode &= 0xfe; + mode |= value >> 4 & 1; + } + SyncMirror(); +} + + +static void Sync() +{ + int prglo = 0; + int prghi = 0; + + int outb = outer << 1; + //this can probably be rolled up, but i have no motivation to do so + //until it's been tested + switch (mode & 0x3c) + { + //32K modes + case 0x00: + case 0x04: + prglo = outb; + prghi = outb | 1; + break; + case 0x10: + case 0x14: + prglo = (outb & ~2) | ((prg << 1) & 2); + prghi = (outb & ~2) | ((prg << 1) & 2) | 1; + break; + case 0x20: + case 0x24: + prglo = (outb & ~6) | ((prg << 1) & 6); + prghi = (outb & ~6) | ((prg << 1) & 6) | 1; + break; + case 0x30: + case 0x34: + prglo = (outb & ~14) | ((prg << 1) & 14); + prghi = (outb & ~14) | ((prg << 1) & 14) | 1; + break; + //bottom fixed modes + case 0x08: + prglo = outb; + prghi = outb | (prg & 1); + break; + case 0x18: + prglo = outb; + prghi = (outb & ~2) | (prg & 3); + break; + case 0x28: + prglo = outb; + prghi = (outb & ~6) | (prg & 7); + break; + case 0x38: + prglo = outb; + prghi = (outb & ~14) | (prg & 15); + break; + //top fixed modes + case 0x0c: + prglo = outb | (prg & 1); + prghi = outb | 1; + break; + case 0x1c: + prglo = (outb & ~2) | (prg & 3); + prghi = outb | 1; + break; + case 0x2c: + prglo = (outb & ~6) | (prg & 7); + prghi = outb | 1; + break; + case 0x3c: + prglo = (outb & ~14) | (prg & 15); + prghi = outb | 1; + break; + } + prglo &= prg_mask_16k; + prghi &= prg_mask_16k; + + setprg16(0x8000, prglo); + setprg16(0xC000, prghi); + setchr8(chr); +} + +static DECLFW(WriteEXP) +{ + uint8 value = V; + reg = value & 0x81; +} + +static DECLFW(WritePRG) +{ + uint8 value = V; + switch (reg) + { + case 0x00: + chr = value & 3; + Mirror(value); + Sync(); + break; + case 0x01: + prg = value & 15; + Mirror(value); + Sync(); + break; + case 0x80: + mode = value & 63; + SyncMirror(); + Sync(); + break; + case 0x81: + outer = value & 63; + Sync(); + break; + } +} + + + +static void M28Reset(void) +{ + outer = 63; + prg = 15; + Sync(); +} + + +static void M28Power(void) +{ + prg_mask_16k = PRGsize[0] - 1; + + //EXP + SetWriteHandler(0x5000,0x5FFF,WriteEXP); + + //PRG + SetWriteHandler(0x8000,0xFFFF,WritePRG); + SetReadHandler(0x8000,0xFFFF,CartBR); + + //WRAM + SetReadHandler(0x6000,0x7FFF,CartBR); + SetWriteHandler(0x6000,0x7FFF,CartBW); + + M28Reset(); +} + +static void M28Close(void) +{ +} + +static SFORMAT StateRegs[]= +{ + {®, 1, "REG"}, + {&chr, 1, "CHR"}, + {&prg, 1, "PRG"}, + {&mode, 1, "MODE"}, + {&outer, 1, "OUTR"}, + {0} +}; + +static void StateRestore(int version) +{ + Sync(); +} + +void Mapper28_Init(CartInfo* info) +{ + info->Power=M28Power; + info->Reset=M28Reset; + info->Close=M28Close; + GameStateRestore=StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/32.cpp b/apps/fceux/src/boards/32.cpp new file mode 100644 index 00000000..7dd91bb6 --- /dev/null +++ b/apps/fceux/src/boards/32.cpp @@ -0,0 +1,103 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg[2], creg[8], mirr; + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { preg, 4, "PREG" }, + { creg, 8, "CREG" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + uint16 swap = ((mirr & 2) << 13); + setmirror((mirr & 1) ^ 1); + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000 ^ swap, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000 ^ swap, ~1); + setprg8(0xE000, ~0); + uint8 i; + for (i = 0; i < 8; i++) + setchr1(i << 10, creg[i]); +} + +static DECLFW(M32Write0) { + preg[0] = V; + Sync(); +} + +static DECLFW(M32Write1) { + mirr = V; + Sync(); +} + +static DECLFW(M32Write2) { + preg[1] = V; + Sync(); +} + +static DECLFW(M32Write3) { + creg[A & 7] = V; + Sync(); +} + +static void M32Power(void) { + Sync(); + SetReadHandler(0x6000,0x7fff,CartBR); + SetWriteHandler(0x6000,0x7fff,CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0x8FFF, M32Write0); + SetWriteHandler(0x9000, 0x9FFF, M32Write1); + SetWriteHandler(0xA000, 0xAFFF, M32Write2); + SetWriteHandler(0xB000, 0xBFFF, M32Write3); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M32Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper32_Init(CartInfo *info) { + info->Power = M32Power; + info->Close = M32Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/33.cpp b/apps/fceux/src/boards/33.cpp new file mode 100644 index 00000000..daa6a773 --- /dev/null +++ b/apps/fceux/src/boards/33.cpp @@ -0,0 +1,117 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 is48; +static uint8 regs[8], mirr; +static uint8 IRQa; +static int16 IRQCount, IRQLatch; + +static SFORMAT StateRegs[] = +{ + { regs, 8, "PREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 2, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + setmirror(mirr); + setprg8(0x8000, regs[0]); + setprg8(0xA000, regs[1]); + setprg8(0xC000, ~1); + setprg8(0xE000, ~0); + setchr2(0x0000, regs[2]); + setchr2(0x0800, regs[3]); + setchr1(0x1000, regs[4]); + setchr1(0x1400, regs[5]); + setchr1(0x1800, regs[6]); + setchr1(0x1C00, regs[7]); +} + +static DECLFW(M33Write) { + A &= 0xF003; + switch(A) { + case 0x8000: regs[0] = V & 0x3F; if(!is48) mirr = ((V >> 6) & 1) ^ 1; Sync(); break; + case 0x8001: regs[1] = V & 0x3F; Sync(); break; + case 0x8002: regs[2] = V; Sync(); break; + case 0x8003: regs[3] = V; Sync(); break; + case 0xA000: regs[4] = V; Sync(); break; + case 0xA001: regs[5] = V; Sync(); break; + case 0xA002: regs[6] = V; Sync(); break; + case 0xA003: regs[7] = V; Sync(); break; + } +} + +static DECLFW(M48Write) { + switch (A & 0xF003) { + case 0xC000: IRQLatch = V; break; + case 0xC001: IRQCount = IRQLatch; break; + case 0xC003: IRQa = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xC002: IRQa = 1; break; + case 0xE000: mirr = ((V >> 6) & 1) ^ 1; Sync(); break; + } +} + +static void M33Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M33Write); +} + +static void M48Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xBFFF, M33Write); + SetWriteHandler(0xC000, 0xFFFF, M48Write); +} + +static void M48IRQ(void) { + if (IRQa) { + IRQCount++; + if (IRQCount == 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper33_Init(CartInfo *info) { + is48 = 0; + info->Power = M33Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper48_Init(CartInfo *info) { + is48 = 1; + info->Power = M48Power; + GameHBIRQHook = M48IRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/34.cpp b/apps/fceux/src/boards/34.cpp new file mode 100644 index 00000000..5c27b00a --- /dev/null +++ b/apps/fceux/src/boards/34.cpp @@ -0,0 +1,93 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * Many-in-one hacked mapper crap. + * + * Original BNROM is actually AxROM variations without mirroring control, + * and haven't SRAM on-board, so it must be removed from here + * + * Difficult banking is what NINA board doing, most hacks for 34 mapper are + * NINA hacks, so this is actually 34 mapper + * + */ + +#include "mapinc.h" + +static uint8 regs[3]; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { regs, 3, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, regs[0]); + setchr4(0x0000, regs[1]); + setchr4(0x1000, regs[2]); +} + +static DECLFW(M34Write) { + if (A >= 0x8000) + regs[0] = V; + else + switch (A) { + case 0x7ffd: regs[0] = V; break; + case 0x7ffe: regs[1] = V; break; + case 0x7fff: regs[2] = V; break; + } + Sync(); +} + +static void M34Power(void) { + regs[0] = regs[1] = 0; + regs[2] = 1; + Sync(); + SetReadHandler(0x6000, 0x7ffc, CartBR); + SetWriteHandler(0x6000, 0x7ffc, CartBW); + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x7ffd, 0xffff, M34Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M34Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper34_Init(CartInfo *info) { + info->Power = M34Power; + info->Close = M34Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/36.cpp b/apps/fceux/src/boards/36.cpp new file mode 100644 index 00000000..dea977eb --- /dev/null +++ b/apps/fceux/src/boards/36.cpp @@ -0,0 +1,69 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * TXC/Micro Genius simplified mapper + */ + +#include "mapinc.h" + +static uint8 latche, mirr; + +static SFORMAT StateRegs[] = +{ + { &latche, 1, "LATC" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, latche >> 4); + setchr8(latche & 0xf); +} + +static DECLFW(M36Write) { + + switch((A>>12)&7) { // need to 4-in-1 MGC-26 BMC, doesnt break other games though + case 0: mirr = MI_V; setmirror(mirr); break; + case 4: mirr = MI_H; setmirror(mirr); break; + } + latche = V; + Sync(); +} + +static DECLFR(M36Read) { + return latche; // Need by Strike Wolf, being simplified mapper, this cart still uses some TCX mapper features andrely on it +} + +static void M36Power(void) { + latche = 0; + Sync(); + SetReadHandler(0x4100, 0x4100, M36Read); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFE, M36Write); // Actually, BUS conflict there preventing from triggering the wrong banks +} + +static void M36Restore(int version) { + Sync(); +} + +void Mapper36_Init(CartInfo *info) { + info->Power = M36Power; + GameStateRestore = M36Restore; + AddExState(StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/3d-block.cpp b/apps/fceux/src/boards/3d-block.cpp new file mode 100644 index 00000000..2a8b62cb --- /dev/null +++ b/apps/fceux/src/boards/3d-block.cpp @@ -0,0 +1,95 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg[4], IRQa; +static int16 IRQCount, IRQPause; + +static int16 Count = 0x0000; + +static SFORMAT StateRegs[] = +{ + { reg, 4, "REGS" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, 0); + setchr8(0); +} + +//#define Count 0x1800 +#define Pause 0x010 + +static DECLFW(UNL3DBlockWrite) { + switch (A) { +//4800 32 +//4900 37 +//4a00 01 +//4e00 18 + case 0x4800: reg[0] = V; break; + case 0x4900: reg[1] = V; break; + case 0x4a00: reg[2] = V; break; + case 0x4e00: reg[3] = V; IRQCount = Count; IRQPause = Pause; IRQa = 1; X6502_IRQEnd(FCEU_IQEXT); break; + } +} + +static void UNL3DBlockPower(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4800, 0x4E00, UNL3DBlockWrite); +} + +static void UNL3DBlockReset(void) { + Count += 0x10; + FCEU_printf("Count=%04x\n", Count); +} + +static void UNL3DBlockIRQHook(int a) { + if (IRQa) { + if (IRQCount > 0) { + IRQCount -= a; + } else { + if (IRQPause > 0) { + IRQPause -= a; + X6502_IRQBegin(FCEU_IQEXT); + } else { + IRQCount = Count; + IRQPause = Pause; + X6502_IRQEnd(FCEU_IQEXT); + } + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void UNL3DBlock_Init(CartInfo *info) { + info->Power = UNL3DBlockPower; + info->Reset = UNL3DBlockReset; + MapIRQHook = UNL3DBlockIRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/40.cpp b/apps/fceux/src/boards/40.cpp new file mode 100644 index 00000000..5cae497d --- /dev/null +++ b/apps/fceux/src/boards/40.cpp @@ -0,0 +1,86 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg; +static uint32 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 4, "IRQC" }, + { &IRQa, 4, "IRQA" }, + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, ~1); + setprg8(0x8000, ~3); + setprg8(0xa000, ~2); + setprg8(0xc000, reg); + setprg8(0xe000, ~0); + setchr8(0); +} + +static DECLFW(M40Write) { + switch (A & 0xe000) { + case 0x8000: IRQa = 0; IRQCount = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xa000: IRQa = 1; break; + case 0xe000: reg = V & 7; Sync(); break; + } +} + +static void M40Power(void) { + reg = 0; + Sync(); + SetReadHandler(0x6000, 0xffff, CartBR); + SetWriteHandler(0x8000, 0xffff, M40Write); +} + +static void M40Reset(void) { +} + +static void M40IRQHook(int a) { + if (IRQa) { + if (IRQCount < 4096) + IRQCount += a; + else{ + IRQa = 0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper40_Init(CartInfo *info) { + info->Reset = M40Reset; + info->Power = M40Power; + MapIRQHook = M40IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/41.cpp b/apps/fceux/src/boards/41.cpp new file mode 100644 index 00000000..d0d90839 --- /dev/null +++ b/apps/fceux/src/boards/41.cpp @@ -0,0 +1,69 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 mainreg, chrreg, mirror; + +static SFORMAT StateRegs[] = +{ + { &mainreg, 1, "MREG" }, + { &chrreg, 1, "CREG" }, + { &mirror, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, mainreg & 7); + setchr8(chrreg); + setmirror(mirror); +} + +static DECLFW(M41Write0) { + mainreg = A & 0xFF; + mirror = ((A >> 5) & 1) ^ 1; + chrreg = (chrreg & 3) | ((A >> 1) & 0xC); + Sync(); +} + +static DECLFW(M41Write1) { + if (mainreg & 0x4) { + chrreg = (chrreg & 0xC) | (A & 3); + Sync(); + } +} + +static void M41Power(void) { + mainreg = chrreg = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x67FF, M41Write0); + SetWriteHandler(0x8000, 0xFFFF, M41Write1); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper41_Init(CartInfo *info) { + info->Power = M41Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/411120-c.cpp b/apps/fceux/src/boards/411120-c.cpp new file mode 100644 index 00000000..ccc490e8 --- /dev/null +++ b/apps/fceux/src/boards/411120-c.cpp @@ -0,0 +1,64 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2008 CaH4e3 + * + * 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 + */ + +// actually cart ID is 811120-C, sorry ;) K-3094 - another ID + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 reset_flag = 0; + +static void BMC411120CCW(uint32 A, uint8 V) { + setchr1(A, V | ((EXPREGS[0] & 3) << 7)); +} + +static void BMC411120CPW(uint32 A, uint8 V) { + if (EXPREGS[0] & (8 | reset_flag)) + setprg32(0x8000, ((EXPREGS[0] >> 4) & 3) | (0x0C)); + else + setprg8(A, (V & 0x0F) | ((EXPREGS[0] & 3) << 4)); +} + +static DECLFW(BMC411120CLoWrite) { + EXPREGS[0] = A; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void BMC411120CReset(void) { + EXPREGS[0] = 0; + reset_flag ^= 4; + MMC3RegReset(); +} + +static void BMC411120CPower(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, BMC411120CLoWrite); +} + +void BMC411120C_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 8, 0); + pwrap = BMC411120CPW; + cwrap = BMC411120CCW; + info->Power = BMC411120CPower; + info->Reset = BMC411120CReset; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/42.cpp b/apps/fceux/src/boards/42.cpp new file mode 100644 index 00000000..87608574 --- /dev/null +++ b/apps/fceux/src/boards/42.cpp @@ -0,0 +1,84 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 preg, creg, mirr; +static uint32 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { &creg, 1, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQCount, 4, "IRQC" }, + { &IRQa, 4, "IRQA" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, preg); + setprg32(0x8000, ~0); + setchr8(creg); + setmirror(mirr); +} + +static DECLFW(M42Write) { + switch (A & 0xE003) { + case 0x8000: creg = V; Sync(); break; + case 0xE000: preg = V & 0x0F; Sync(); break; + case 0xE001: mirr = ((V >> 3) & 1 ) ^ 1; Sync(); break; + case 0xE002: IRQa = V & 2; if (!IRQa) IRQCount = 0; X6502_IRQEnd(FCEU_IQEXT); break; + } +} + +static void M42Power(void) { + preg = 0; + mirr = 1; // Ai Senshi Nicol actually has fixed mirroring, but mapper forcing it's default value now + Sync(); + SetReadHandler(0x6000, 0xffff, CartBR); + SetWriteHandler(0x6000, 0xffff, M42Write); +} + +static void M42IRQHook(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount >= 32768) IRQCount -= 32768; + if (IRQCount >= 24576) + X6502_IRQBegin(FCEU_IQEXT); + else + X6502_IRQEnd(FCEU_IQEXT); + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper42_Init(CartInfo *info) { + info->Power = M42Power; + MapIRQHook = M42IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/43.cpp b/apps/fceux/src/boards/43.cpp new file mode 100644 index 00000000..396f80e2 --- /dev/null +++ b/apps/fceux/src/boards/43.cpp @@ -0,0 +1,91 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg, swap; +static uint32 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 4, "IRQC" }, + { &IRQa, 4, "IRQA" }, + { ®, 1, "REG" }, + { &swap, 1, "SWAP" }, + { 0 } +}; + +static void Sync(void) { + setprg4(0x5000, 8 << 1); // Only YS-612 advanced version + setprg8(0x6000, swap?0:2); + setprg8(0x8000, 1); + setprg8(0xa000, 0); + setprg8(0xc000, reg); + setprg8(0xe000, swap?8:9); // hard dump for mr.Mary is 128K, + // bank 9 is the last 2K ok bank 8 repeated 4 times, then till the end of 128K + // instead used bank A, containing some CHR data, ines rom have unused banks removed, + // and bank A moved to the bank 9 place for compatibility with other crappy dumps + setchr8(0); +} + +static DECLFW(M43Write) { +// int transo[8]={4,3,4,4,4,7,5,6}; + int transo[8] = { 4, 3, 5, 3, 6, 3, 7, 3 }; // According to hardware tests + switch (A & 0xf1ff) { + case 0x4022: reg = transo[V & 7]; Sync(); break; + case 0x4120: swap = V & 1; Sync(); break; + case 0x8122: // hacked version + case 0x4122: IRQa = V & 1; X6502_IRQEnd(FCEU_IQEXT); IRQCount = 0; break; // original version + } +} + +static void M43Power(void) { + reg = swap = 0; + Sync(); + SetReadHandler(0x5000, 0xffff, CartBR); + SetWriteHandler(0x4020, 0xffff, M43Write); +} + +static void M43Reset(void) { +} + +static void M43IRQHook(int a) { + IRQCount += a; + if (IRQa) + if (IRQCount >= 4096) { + IRQa = 0; + X6502_IRQBegin(FCEU_IQEXT); + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper43_Init(CartInfo *info) { + info->Reset = M43Reset; + info->Power = M43Power; + MapIRQHook = M43IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/46.cpp b/apps/fceux/src/boards/46.cpp new file mode 100644 index 00000000..04e03122 --- /dev/null +++ b/apps/fceux/src/boards/46.cpp @@ -0,0 +1,69 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg0, reg1; + +static SFORMAT StateRegs[] = +{ + { ®0, 1, "REG0" }, + { ®1, 1, "REG1" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, (reg1 & 1) + ((reg0 & 0xF) << 1)); + setchr8(((reg1 >> 4) & 7) + ((reg0 & 0xF0) >> 1)); +} + +static DECLFW(M46Write0) { + reg0 = V; + Sync(); +} + +static DECLFW(M46Write1) { + reg1 = V; + Sync(); +} + +static void M46Power(void) { + reg0 = reg1 = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, M46Write0); + SetWriteHandler(0x8000, 0xFFFF, M46Write1); +} + +static void M46Reset(void) { + reg0 = reg1 = 0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper46_Init(CartInfo *info) { + info->Power = M46Power; + info->Reset = M46Reset; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/50.cpp b/apps/fceux/src/boards/50.cpp new file mode 100644 index 00000000..40a8e42a --- /dev/null +++ b/apps/fceux/src/boards/50.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg; +static uint32 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 4, "IRQC" }, + { &IRQa, 4, "IRQA" }, + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, 0xF); + setprg8(0x8000, 0x8); + setprg8(0xa000, 0x9); + setprg8(0xc000, reg); + setprg8(0xe000, 0xB); + setchr8(0); +} + +static DECLFW(M50Write) { + switch (A & 0xD160) { + case 0x4120: IRQa = V & 1; if (!IRQa) IRQCount = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x4020: reg = ((V & 1) << 2) | ((V & 2) >> 1) | ((V & 4) >> 1) | (V & 8); Sync(); break; + } +} + +static void M50Power(void) { + reg = 0; + Sync(); + SetReadHandler(0x6000, 0xffff, CartBR); + SetWriteHandler(0x4020, 0x5fff, M50Write); +} + +static void M50Reset(void) { +} + +static void M50IRQHook(int a) { + if (IRQa) { + if (IRQCount < 4096) + IRQCount += a; + else{ + IRQa = 0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper50_Init(CartInfo *info) { + info->Reset = M50Reset; + info->Power = M50Power; + MapIRQHook = M50IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/51.cpp b/apps/fceux/src/boards/51.cpp new file mode 100644 index 00000000..02d693c3 --- /dev/null +++ b/apps/fceux/src/boards/51.cpp @@ -0,0 +1,84 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 bank, mode; +static SFORMAT StateRegs[] = +{ + { &bank, 1, "BANK" }, + { &mode, 1, "MODE" }, + { 0 } +}; + +static void Sync(void) { + if (mode & 2) { + setprg8(0x6000, ((bank & 7) << 2) | 0x23); + setprg16(0x8000, (bank << 1) | 0); + setprg16(0xC000, (bank << 1) | 1); + } else { + setprg8(0x6000, ((bank & 4) << 2) | 0x2F); + setprg16(0x8000, (bank << 1) | (mode >> 4)); + setprg16(0xC000, ((bank & 0xC) << 1) | 7); + } + if (mode == 0x12) + setmirror(MI_H); + else + setmirror(MI_V); + setchr8(0); +} + +static DECLFW(M51WriteMode) { + mode = V & 0x12; + Sync(); +} + +static DECLFW(M51WriteBank) { + bank = V & 0x0F; + if (A & 0x4000) + mode = (mode & 2) | (V & 0x10); + Sync(); +} + +static void M51Power(void) { + bank = 0; + mode = 2; + Sync(); + SetWriteHandler(0x6000, 0x7FFF, M51WriteMode); + SetWriteHandler(0x8000, 0xFFFF, M51WriteBank); + SetReadHandler(0x6000, 0xFFFF, CartBR); +} + +static void M51Reset(void) { + bank = 0; + mode = 2; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper51_Init(CartInfo *info) { + info->Power = M51Power; + info->Reset = M51Reset; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/57.cpp b/apps/fceux/src/boards/57.cpp new file mode 100644 index 00000000..4d2b5105 --- /dev/null +++ b/apps/fceux/src/boards/57.cpp @@ -0,0 +1,84 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + */ + +#include "mapinc.h" + +static uint8 prg_reg; +static uint8 chr_reg; +static uint8 hrd_flag; + +static SFORMAT StateRegs[] = +{ + { &hrd_flag, 1, "DPSW" }, + { &prg_reg, 1, "PRG" }, + { &chr_reg, 1, "CHR" }, + { 0 } +}; + +static void Sync(void) { + if (prg_reg & 0x80) + setprg32(0x8000, prg_reg >> 6); + else{ + setprg16(0x8000, (prg_reg >> 5) & 3); + setprg16(0xC000, (prg_reg >> 5) & 3); + } + setmirror((prg_reg & 8) >> 3); + setchr8((chr_reg & 3) | (prg_reg & 7) | ((prg_reg & 0x10) >> 1)); +} + +static DECLFR(M57Read) { + return hrd_flag; +} + +static DECLFW(M57Write) { + if ((A & 0x8800) == 0x8800) + prg_reg = V; + else + chr_reg = V; + Sync(); +} + +static void M57Power(void) { + prg_reg = 0; + chr_reg = 0; + hrd_flag = 0; + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M57Write); + SetReadHandler(0x6000, 0x6000, M57Read); + Sync(); +} + +static void M57Reset() { + hrd_flag++; + hrd_flag &= 3; + FCEU_printf("Select Register = %02x\n", hrd_flag); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper57_Init(CartInfo *info) { + info->Power = M57Power; + info->Reset = M57Reset; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/603-5052.cpp b/apps/fceux/src/boards/603-5052.cpp new file mode 100644 index 00000000..cf13a449 --- /dev/null +++ b/apps/fceux/src/boards/603-5052.cpp @@ -0,0 +1,44 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 lut[4] = { 0x00, 0x02, 0x02, 0x03 }; + +static DECLFW(UNL6035052ProtWrite) { + EXPREGS[0] = lut[V & 3]; +} + +static DECLFR(UNL6035052ProtRead) { + return EXPREGS[0]; +} + +static void UNL6035052Power(void) { + GenMMC3Power(); + SetWriteHandler(0x4020, 0x7FFF, UNL6035052ProtWrite); + SetReadHandler(0x4020, 0x7FFF, UNL6035052ProtRead); +} + +void UNL6035052_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 0, 0); + info->Power = UNL6035052Power; + AddExState(EXPREGS, 6, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/62.cpp b/apps/fceux/src/boards/62.cpp new file mode 100644 index 00000000..0dc2a63d --- /dev/null +++ b/apps/fceux/src/boards/62.cpp @@ -0,0 +1,69 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 bank; +static uint16 mode; +static SFORMAT StateRegs[] = +{ + { &bank, 1, "BANK" }, + { &mode, 2, "MODE" }, + { 0 } +}; + +static void Sync(void) { + setchr8(((mode & 0x1F) << 2) | (bank & 0x03)); + if (mode & 0x20) { + setprg16(0x8000, (mode & 0x40) | ((mode >> 8) & 0x3F)); + setprg16(0xc000, (mode & 0x40) | ((mode >> 8) & 0x3F)); + } else + setprg32(0x8000, ((mode & 0x40) | ((mode >> 8) & 0x3F)) >> 1); + setmirror(((mode >> 7) & 1) ^ 1); +} + +static DECLFW(M62Write) { + mode = A & 0x3FFF; + bank = V & 3; + Sync(); +} + +static void M62Power(void) { + bank = mode = 0; + Sync(); + SetWriteHandler(0x8000, 0xFFFF, M62Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void M62Reset(void) { + bank = mode = 0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper62_Init(CartInfo *info) { + info->Power = M62Power; + info->Reset = M62Reset; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/65.cpp b/apps/fceux/src/boards/65.cpp new file mode 100644 index 00000000..fbfd6677 --- /dev/null +++ b/apps/fceux/src/boards/65.cpp @@ -0,0 +1,105 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg[3], creg[8], mirr; +static uint8 IRQa; +static int16 IRQCount, IRQLatch; + +static SFORMAT StateRegs[] = +{ + { preg, 3, "PREG" }, + { creg, 8, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 2, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + setmirror(mirr); + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, ~0); + setchr1(0x0000, creg[0]); + setchr1(0x0400, creg[1]); + setchr1(0x0800, creg[2]); + setchr1(0x0C00, creg[3]); + setchr1(0x1000, creg[4]); + setchr1(0x1400, creg[5]); + setchr1(0x1800, creg[6]); + setchr1(0x1C00, creg[7]); + setmirror(mirr); +} + +static DECLFW(M65Write) { + switch (A) { + case 0x8000: preg[0] = V; Sync(); break; + case 0xA000: preg[1] = V; Sync(); break; + case 0xC000: preg[2] = V; Sync(); break; + case 0x9001: mirr = ((V >> 7) & 1) ^ 1; Sync(); break; + case 0x9003: IRQa = V & 0x80; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x9004: IRQCount = IRQLatch; break; + case 0x9005: IRQLatch &= 0x00FF; IRQLatch |= V << 8; break; + case 0x9006: IRQLatch &= 0xFF00; IRQLatch |= V; break; + case 0xB000: creg[0] = V; Sync(); break; + case 0xB001: creg[1] = V; Sync(); break; + case 0xB002: creg[2] = V; Sync(); break; + case 0xB003: creg[3] = V; Sync(); break; + case 0xB004: creg[4] = V; Sync(); break; + case 0xB005: creg[5] = V; Sync(); break; + case 0xB006: creg[6] = V; Sync(); break; + case 0xB007: creg[7] = V; Sync(); break; + } +} + +static void M65Power(void) { + preg[2] = ~1; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M65Write); +} + +void M65IRQ(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount < -4) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + IRQCount = -1; + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper65_Init(CartInfo *info) { + info->Power = M65Power; + MapIRQHook = M65IRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/67.cpp b/apps/fceux/src/boards/67.cpp new file mode 100644 index 00000000..4e29edbf --- /dev/null +++ b/apps/fceux/src/boards/67.cpp @@ -0,0 +1,106 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg, creg[4], mirr, suntoggle = 0; +static uint8 IRQa; +static int16 IRQCount, IRQLatch; + +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { &suntoggle, 1, "STOG" }, + { creg, 4, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 2, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + setmirror(mirr); + setprg16(0x8000, preg); + setprg16(0xC000, ~0); + setchr2(0x0000, creg[0]); + setchr2(0x0800, creg[1]); + setchr2(0x1000, creg[2]); + setchr2(0x1800, creg[3]); + switch (mirr) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(M67Write) { + switch (A & 0xF800) { + case 0x8800: creg[0] = V; Sync(); break; + case 0x9800: creg[1] = V; Sync(); break; + case 0xA800: creg[2] = V; Sync(); break; + case 0xB800: creg[3] = V; Sync(); break; + case 0xC000: + case 0xC800: + IRQCount &= 0xFF << (suntoggle << 3); + IRQCount |= V << ((suntoggle ^ 1) << 3); + suntoggle ^= 1; + break; + case 0xD800: + suntoggle = 0; + IRQa = V & 0x10; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xE800: mirr = V & 3; Sync(); break; + case 0xF800: preg = V; Sync(); break; + } +} + +static void M67Power(void) { + suntoggle = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M67Write); +} + +void M67IRQ(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount <= 0) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + IRQCount = -1; + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper67_Init(CartInfo *info) { + info->Power = M67Power; + MapIRQHook = M67IRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/68.cpp b/apps/fceux/src/boards/68.cpp new file mode 100644 index 00000000..710f4437 --- /dev/null +++ b/apps/fceux/src/boards/68.cpp @@ -0,0 +1,164 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 chr_reg[4]; +static uint8 kogame, prg_reg, nt1, nt2, mirr; + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE, count; + +static SFORMAT StateRegs[] = +{ + { &nt1, 1, "NT1" }, + { &nt2, 1, "NT2" }, + { &mirr, 1, "MIRR" }, + { &prg_reg, 1, "PRG" }, + { &kogame, 1, "KGME" }, + { &count, 4, "CNT" }, + { chr_reg, 4, "CHR" }, + { 0 } +}; + +static void M68NTfix(void) { + if ((!UNIFchrrama) && (mirr & 0x10)) { + PPUNTARAM = 0; + switch (mirr & 3) { + case 0: + vnapage[0] = vnapage[2] = CHRptr[0] + (((nt1 | 128) & CHRmask1[0]) << 10); + vnapage[1] = vnapage[3] = CHRptr[0] + (((nt2 | 128) & CHRmask1[0]) << 10); + break; + case 1: + vnapage[0] = vnapage[1] = CHRptr[0] + (((nt1 | 128) & CHRmask1[0]) << 10); + vnapage[2] = vnapage[3] = CHRptr[0] + (((nt2 | 128) & CHRmask1[0]) << 10); + break; + case 2: + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = CHRptr[0] + (((nt1 | 128) & CHRmask1[0]) << 10); + break; + case 3: + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = CHRptr[0] + (((nt2 | 128) & CHRmask1[0]) << 10); + break; + } + } else + switch (mirr & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static void Sync(void) { + setchr2(0x0000, chr_reg[0]); + setchr2(0x0800, chr_reg[1]); + setchr2(0x1000, chr_reg[2]); + setchr2(0x1800, chr_reg[3]); + setprg8r(0x10, 0x6000, 0); + setprg16r((PRGptr[1]) ? kogame : 0, 0x8000, prg_reg); + setprg16(0xC000, ~0); +} + +static DECLFR(M68Read) { + if (!(kogame & 8)) { + count++; + if (count == 1784) + setprg16r(0, 0x8000, prg_reg); + } + return CartBR(A); +} + +static DECLFW(M68WriteLo) { + if (!V) { + count = 0; + setprg16r((PRGptr[1]) ? kogame : 0, 0x8000, prg_reg); + } + CartBW(A, V); +} + +static DECLFW(M68WriteCHR) { + chr_reg[(A >> 12) & 3] = V; + Sync(); +} + +static DECLFW(M68WriteNT1) { + nt1 = V; + M68NTfix(); +} + +static DECLFW(M68WriteNT2) { + nt2 = V; + M68NTfix(); +} + +static DECLFW(M68WriteMIR) { + mirr = V; + M68NTfix(); +} + +static DECLFW(M68WriteROM) { + prg_reg = V & 7; + kogame = ((V >> 3) & 1) ^ 1; + Sync(); +} + +static void M68Power(void) { + prg_reg = 0; + kogame = 0; + Sync(); + M68NTfix(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xBFFF, M68Read); + SetReadHandler(0xC000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xBFFF, M68WriteCHR); + SetWriteHandler(0xC000, 0xCFFF, M68WriteNT1); + SetWriteHandler(0xD000, 0xDFFF, M68WriteNT2); + SetWriteHandler(0xE000, 0xEFFF, M68WriteMIR); + SetWriteHandler(0xF000, 0xFFFF, M68WriteROM); + SetWriteHandler(0x6000, 0x6000, M68WriteLo); + SetWriteHandler(0x6001, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M68Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); + M68NTfix(); +} + +void Mapper68_Init(CartInfo *info) { + info->Power = M68Power; + info->Close = M68Close; + GameStateRestore = StateRestore; + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/69.cpp b/apps/fceux/src/boards/69.cpp new file mode 100644 index 00000000..bc256211 --- /dev/null +++ b/apps/fceux/src/boards/69.cpp @@ -0,0 +1,282 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 cmdreg, preg[4], creg[8], mirr; +static uint8 IRQa; +static int32 IRQCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { &cmdreg, 1, "CMDR" }, + { preg, 4, "PREG" }, + { creg, 8, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { 0 } +}; + +static void Sync(void) { + uint8 i; + if ((preg[3] & 0xC0) == 0xC0) + setprg8r(0x10, 0x6000, preg[3] & 0x3F); + else + setprg8(0x6000, preg[3] & 0x3F); + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, ~0); + for (i = 0; i < 8; i++) + setchr1(i << 10, creg[i]); + switch (mirr & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(M69WRAMWrite) { + if ((preg[3] & 0xC0) == 0xC0) + CartBW(A, V); +} + +static DECLFR(M69WRAMRead) { + if ((preg[3] & 0xC0) == 0x40) + return X.DB; + else + return CartBR(A); +} + +static DECLFW(M69Write0) { + cmdreg = V & 0xF; +} + +static DECLFW(M69Write1) { + switch (cmdreg) { + case 0x0: creg[0] = V; Sync(); break; + case 0x1: creg[1] = V; Sync(); break; + case 0x2: creg[2] = V; Sync(); break; + case 0x3: creg[3] = V; Sync(); break; + case 0x4: creg[4] = V; Sync(); break; + case 0x5: creg[5] = V; Sync(); break; + case 0x6: creg[6] = V; Sync(); break; + case 0x7: creg[7] = V; Sync(); break; + case 0x8: preg[3] = V; Sync(); break; + case 0x9: preg[0] = V; Sync(); break; + case 0xA: preg[1] = V; Sync(); break; + case 0xB: preg[2] = V; Sync(); break; + case 0xC: mirr = V & 3; Sync();break; + case 0xD: IRQa = V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xE: IRQCount &= 0xFF00; IRQCount |= V; break; + case 0xF: IRQCount &= 0x00FF; IRQCount |= V << 8; break; + } +} + +// SUNSOFT-5/FME-7 Sound + +static void AYSound(int Count); +static void AYSoundHQ(void); +static void DoAYSQ(int x); +static void DoAYSQHQ(int x); + +static uint8 sndcmd, sreg[14]; +static int32 vcount[3]; +static int32 dcount[3]; +static int CAYBC[3]; + +static SFORMAT SStateRegs[] = +{ + { &sndcmd, 1, "SCMD" }, + { sreg, 14, "SREG" }, + { 0 } +}; + +static DECLFW(M69SWrite0) { + sndcmd = V % 14; +} + +static DECLFW(M69SWrite1) { + int x; + GameExpSound.Fill = AYSound; + GameExpSound.HiFill = AYSoundHQ; + if (FSettings.SndRate) + switch (sndcmd) { + case 0: + case 1: + case 8: if (FSettings.soundq >= 1) DoAYSQHQ(0); else DoAYSQ(0); break; + case 2: + case 3: + case 9: if (FSettings.soundq >= 1) DoAYSQHQ(1); else DoAYSQ(1); break; + case 4: + case 5: + case 10: if (FSettings.soundq >= 1) DoAYSQHQ(2); else DoAYSQ(2); break; + case 7: + for (x = 0; x < 2; x++) + if (FSettings.soundq >= 1) DoAYSQHQ(x); else DoAYSQ(x); + break; + } + sreg[sndcmd] = V; +} + +static void DoAYSQ(int x) { + int32 freq = ((sreg[x << 1] | ((sreg[(x << 1) + 1] & 15) << 8)) + 1) << (4 + 17); + int32 amp = (sreg[0x8 + x] & 15) << 2; + int32 start, end; + int V; + + amp += amp >> 1; + + start = CAYBC[x]; + end = (SOUNDTS << 16) / soundtsinc; + if (end <= start) return; + CAYBC[x] = end; + + if (amp && !(sreg[0x7] & (1 << x))) + for (V = start; V < end; V++) { + if (dcount[x]) + Wave[V >> 4] += amp; + vcount[x] -= nesincsize; + while (vcount[x] <= 0) { + dcount[x] ^= 1; + vcount[x] += freq; + } + } +} + +static void DoAYSQHQ(int x) { + uint32 V; + int32 freq = ((sreg[x << 1] | ((sreg[(x << 1) + 1] & 15) << 8)) + 1) << 4; + int32 amp = (sreg[0x8 + x] & 15) << 6; + + amp += amp >> 1; + + if (!(sreg[0x7] & (1 << x))) { + for (V = CAYBC[x]; V < SOUNDTS; V++) { + if (dcount[x]) + WaveHi[V] += amp; + vcount[x]--; + if (vcount[x] <= 0) { + dcount[x] ^= 1; + vcount[x] = freq; + } + } + } + CAYBC[x] = SOUNDTS; +} + +static void AYSound(int Count) { + int x; + DoAYSQ(0); + DoAYSQ(1); + DoAYSQ(2); + for (x = 0; x < 3; x++) + CAYBC[x] = Count; +} + +static void AYSoundHQ(void) { + DoAYSQHQ(0); + DoAYSQHQ(1); + DoAYSQHQ(2); +} + +static void AYHiSync(int32 ts) { + int x; + + for (x = 0; x < 3; x++) + CAYBC[x] = ts; +} + +void Mapper69_ESI(void) { + GameExpSound.RChange = Mapper69_ESI; + GameExpSound.HiSync = AYHiSync; + memset(dcount, 0, sizeof(dcount)); + memset(vcount, 0, sizeof(vcount)); + memset(CAYBC, 0, sizeof(CAYBC)); + AddExState(&SStateRegs, ~0, 0, 0); +} + +// SUNSOFT-5/FME-7 Sound + +static void M69Power(void) { + cmdreg = sndcmd = 0; + IRQCount = 0xFFFF; + IRQa = 0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, M69WRAMRead); + SetWriteHandler(0x6000, 0x7FFF, M69WRAMWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0x9FFF, M69Write0); + SetWriteHandler(0xA000, 0xBFFF, M69Write1); + SetWriteHandler(0xC000, 0xDFFF, M69SWrite0); + SetWriteHandler(0xE000, 0xFFFF, M69SWrite1); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M69Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void M69IRQHook(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount <= 0) { + X6502_IRQBegin(FCEU_IQEXT); IRQa = 0; IRQCount = 0xFFFF; + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper69_Init(CartInfo *info) { + info->Power = M69Power; + info->Close = M69Close; + MapIRQHook = M69IRQHook; + if(info->ines2) + WRAMSIZE = info->wram_size + info->battery_wram_size; + else + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + GameStateRestore = StateRestore; + Mapper69_ESI(); + AddExState(&StateRegs, ~0, 0, 0); +} + +void NSFAY_Init(void) { + sndcmd = 0; + SetWriteHandler(0xC000, 0xDFFF, M69SWrite0); + SetWriteHandler(0xE000, 0xFFFF, M69SWrite1); + Mapper69_ESI(); +} diff --git a/apps/fceux/src/boards/71.cpp b/apps/fceux/src/boards/71.cpp new file mode 100644 index 00000000..37c05aab --- /dev/null +++ b/apps/fceux/src/boards/71.cpp @@ -0,0 +1,64 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg, mirr; + +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg16(0x8000, preg); + setprg16(0xC000, ~0); + setchr8(0); + if(mirr) + setmirror(mirr); +} + +static DECLFW(M71Write) { + if ((A & 0xF000) == 0x9000) + mirr = MI_0 + ((V >> 4) & 1); // 2-in-1, some carts are normal hardwire V/H mirror, some uses mapper selectable 0/1 mirror + else + preg = V; + Sync(); +} + +static void M71Power(void) { + mirr = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M71Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper71_Init(CartInfo *info) { + info->Power = M71Power; + GameStateRestore = StateRestore; + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/72.cpp b/apps/fceux/src/boards/72.cpp new file mode 100644 index 00000000..de5ec91d --- /dev/null +++ b/apps/fceux/src/boards/72.cpp @@ -0,0 +1,64 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * Moero!! Pro Tennis have ADPCM codec on-board, PROM isn't dumped, emulation isn't + * possible just now. + */ + +#include "mapinc.h" + +static uint8 preg, creg; + +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { &creg, 1, "CREG" }, + { 0 } +}; + +static void Sync(void) { + setprg16(0x8000, preg); + setprg16(0xC000, ~0); + setchr8(creg); +} + +static DECLFW(M72Write) { + if (V & 0x80) + preg = V & 0xF; + if (V & 0x40) + creg = V & 0xF; + Sync(); +} + +static void M72Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0xFFFF, M72Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper72_Init(CartInfo *info) { + info->Power = M72Power; + GameStateRestore = StateRestore; + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/77.cpp b/apps/fceux/src/boards/77.cpp new file mode 100644 index 00000000..bbba3e8f --- /dev/null +++ b/apps/fceux/src/boards/77.cpp @@ -0,0 +1,75 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 latche; + +static uint8 *CHRRAM=NULL; +static uint32 CHRRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { &latche, 1, "LATC" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, latche & 7); + setchr2(0x0000, latche >> 4); + setchr2r(0x10, 0x0800, 2); + setchr4r(0x10, 0x1000, 0); +} + +static DECLFW(M77Write) { + latche = V; + Sync(); +} + +static void M77Power(void) { + latche = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M77Write); +} + +static void M77Close(void) +{ + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper77_Init(CartInfo *info) { + info->Power = M77Power; + info->Close = M77Close; + GameStateRestore = StateRestore; + + CHRRAMSIZE = 6 * 1024; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/79.cpp b/apps/fceux/src/boards/79.cpp new file mode 100644 index 00000000..14d9ac79 --- /dev/null +++ b/apps/fceux/src/boards/79.cpp @@ -0,0 +1,61 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 creg, preg; +static SFORMAT StateRegs[] = +{ + { &creg, 1, "CREG" }, + { &preg, 1, "PREG" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, preg); + setchr8(creg); +} + +static DECLFW(M79Write) { + if ((A < 0x8000) && ((A ^ 0x4100) == 0)) { + preg = (V >> 3) & 1; + } + creg = V & 7; + Sync(); +} + +static void M79Power(void) { + preg = ~0; + Sync(); + SetWriteHandler(0x4100, 0x5FFF, M79Write); + SetWriteHandler(0x8000, 0xFFFF, M79Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper79_Init(CartInfo *info) { + info->Power = M79Power; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/80.cpp b/apps/fceux/src/boards/80.cpp new file mode 100644 index 00000000..b99132c2 --- /dev/null +++ b/apps/fceux/src/boards/80.cpp @@ -0,0 +1,193 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg[3], creg[6], isExMirr; +static uint8 mirr, cmd, wram_enable, wram[256]; +static uint8 mcache[8]; +static uint32 lastppu; + +static SFORMAT StateRegs80[] = +{ + { preg, 3, "PREG" }, + { creg, 6, "CREG" }, + { wram, 256, "WRAM" }, + { &mirr, 1, "MIRR" }, + { &wram_enable, 1, "WRME" }, + { 0 } +}; + +static SFORMAT StateRegs95[] = +{ + { &cmd, 1, "CMDR" }, + { preg, 3, "PREG" }, + { creg, 6, "CREG" }, + { mcache, 8, "MCCH" }, + { &lastppu, 4, "LPPU" }, + { 0 } +}; + +static SFORMAT StateRegs207[] = +{ + { preg, 3, "PREG" }, + { creg, 6, "CREG" }, + { mcache, 8, "MCCH" }, + { &lastppu, 4, "LPPU" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, ~0); + setchr2(0x0000, (creg[0] >> 1) & 0x3F); + setchr2(0x0800, (creg[1] >> 1) & 0x3F); + setchr1(0x1000, creg[2]); + setchr1(0x1400, creg[3]); + setchr1(0x1800, creg[4]); + setchr1(0x1C00, creg[5]); + if (isExMirr) { + setmirror(MI_0 + mcache[lastppu]); + } else + setmirror(mirr); +} + +static DECLFW(M80RamWrite) { + if(wram_enable == 0xA3) + wram[A & 0xFF] = V; +} + +static DECLFR(M80RamRead) { + if(wram_enable == 0xA3) + return wram[A & 0xFF]; + else + return 0xFF; +} + +static DECLFW(M80Write) { + switch (A) { + case 0x7EF0: creg[0] = V; mcache[0] = mcache[1] = V >> 7; Sync(); break; + case 0x7EF1: creg[1] = V; mcache[2] = mcache[3] = V >> 7; Sync(); break; + case 0x7EF2: creg[2] = V; mcache[4] = V >> 7; Sync(); break; + case 0x7EF3: creg[3] = V; mcache[5] = V >> 7; Sync(); break; + case 0x7EF4: creg[4] = V; mcache[6] = V >> 7; Sync(); break; + case 0x7EF5: creg[5] = V; mcache[7] = V >> 7; Sync(); break; + case 0x7EF6: mirr = V & 1; Sync(); break; + case 0x7EF8: wram_enable = V; break; + case 0x7EFA: + case 0x7EFB: preg[0] = V; Sync(); break; + case 0x7EFC: + case 0x7EFD: preg[1] = V; Sync(); break; + case 0x7EFE: + case 0x7EFF: preg[2] = V; Sync(); break; + } +} + +static DECLFW(M95Write) { + switch (A & 0xF001) { + case 0x8000: cmd = V; break; + case 0x8001: + switch (cmd & 0x07) { + case 0: creg[0] = V & 0x1F; mcache[0] = mcache[1] = (V >> 5) & 1; Sync(); break; + case 1: creg[1] = V & 0x1F; mcache[2] = mcache[3] = (V >> 5) & 1; Sync(); break; + case 2: creg[2] = V & 0x1F; mcache[4] = (V >> 5) & 1; Sync(); break; + case 3: creg[3] = V & 0x1F; mcache[5] = (V >> 5) & 1; Sync(); break; + case 4: creg[4] = V & 0x1F; mcache[6] = (V >> 5) & 1; Sync(); break; + case 5: creg[5] = V & 0x1F; mcache[7] = (V >> 5) & 1; Sync(); break; + case 6: preg[0] = V; Sync(); break; + case 7: preg[1] = V; Sync(); break; + } + Sync(); + } +} + +static void MExMirrPPU(uint32 A) { + static int8 lastmirr = -1, curmirr; + if (A < 0x2000) { + lastppu = A >> 10; + curmirr = mcache[lastppu]; + if (curmirr != lastmirr) { + setmirror(MI_0 + curmirr); + lastmirr = curmirr; + } + } +} + +static void M80Power(void) { + wram_enable = 0xFF; + Sync(); + SetReadHandler(0x7F00, 0x7FFF, M80RamRead); + SetWriteHandler(0x7F00, 0x7FFF, M80RamWrite); + SetWriteHandler(0x7EF0, 0x7EFF, M80Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void M207Power(void) { + mcache[0] = mcache[1] = mcache[2] = mcache[3] = 0; + mcache[4] = mcache[5] = mcache[6] = mcache[7] = 0; + Sync(); + SetWriteHandler(0x7EF0, 0x7EFF, M80Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void M95Power(void) { + preg[2] = ~1; + mcache[0] = mcache[1] = mcache[2] = mcache[3] = 0; + mcache[4] = mcache[5] = mcache[6] = mcache[7] = 0; + Sync(); + SetWriteHandler(0x8000, 0xFFFF, M95Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper80_Init(CartInfo *info) { + isExMirr = 0; + info->Power = M80Power; + GameStateRestore = StateRestore; + + if (info->battery) { + info->SaveGame[0] = wram; + info->SaveGameLen[0] = 256; + } + + AddExState(&StateRegs80, ~0, 0, 0); +} + +void Mapper95_Init(CartInfo *info) { + isExMirr = 1; + info->Power = M95Power; + PPU_hook = MExMirrPPU; + GameStateRestore = StateRestore; + AddExState(&StateRegs95, ~0, 0, 0); +} + +void Mapper207_Init(CartInfo *info) { + isExMirr = 1; + info->Power = M207Power; + PPU_hook = MExMirrPPU; + GameStateRestore = StateRestore; + AddExState(&StateRegs207, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/80013-B.cpp b/apps/fceux/src/boards/80013-B.cpp new file mode 100644 index 00000000..1f376ea9 --- /dev/null +++ b/apps/fceux/src/boards/80013-B.cpp @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2017 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 bios_prg, rom_prg, rom_mode, mirror; + +static SFORMAT StateRegs[] = +{ + { &bios_prg, 1, "BREG" }, + { &rom_prg, 1, "RREG" }, + { &rom_mode, 1, "RMODE" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + if(rom_mode&2) { + setprg16r(0,0x8000,(bios_prg&0xF)|(rom_prg&0x70)); + } else { + setprg16r(1,0x8000,bios_prg&3); + } + setprg16r(0,0xC000,rom_prg&0x7F); + setmirror(((bios_prg>>4)&1)^1); +} + +static DECLFW(BMC80013BWrite) { + uint8 reg = (A>>13)&3; + if(reg == 0) { + bios_prg = V; + } else { + rom_prg = V; + rom_mode = reg; + } + Sync(); +} + +static void BMC80013BPower(void) { + bios_prg=rom_prg=rom_mode=mirror=0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, BMC80013BWrite); +} + +static void BMC80013BReset(void) { + bios_prg=rom_prg=rom_mode=mirror=0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMC80013B_Init(CartInfo *info) { + info->Reset = BMC80013BReset; + info->Power = BMC80013BPower; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/8157.cpp b/apps/fceux/src/boards/8157.cpp new file mode 100644 index 00000000..287be082 --- /dev/null +++ b/apps/fceux/src/boards/8157.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * GG1 boards, similar to T-262, with no Data latch + * + */ + +#include "mapinc.h" + +static uint16 cmdreg; +static uint8 reset; +static SFORMAT StateRegs[] = +{ + { &reset, 1, "REST" }, + { &cmdreg, 2, "CREG" }, + { 0 } +}; + +static void Sync(void) { + uint32 base = ((cmdreg & 0x060) | ((cmdreg & 0x100) >> 1)) >> 2; + uint32 bank = (cmdreg & 0x01C) >> 2; + uint32 lbank = (cmdreg & 0x200) ? 7 : ((cmdreg & 0x80) ? bank : 0); + if (PRGptr[1]) { + setprg16r(base >> 3, 0x8000, bank); // for versions with split ROMs + setprg16r(base >> 3, 0xC000, lbank); + } else { + setprg16(0x8000, base | bank); + setprg16(0xC000, base | lbank); + } + setmirror(((cmdreg & 2) >> 1) ^ 1); +} + +static DECLFR(UNL8157Read) { + if ((cmdreg & 0x100) && (PRGsize[0] < (1024 * 1024))) { + A = (A & 0xFFF0) + reset; + } + return CartBR(A); +} + +static DECLFW(UNL8157Write) { + cmdreg = A; + Sync(); +} + +static void UNL8157Power(void) { + setchr8(0); + SetWriteHandler(0x8000, 0xFFFF, UNL8157Write); + SetReadHandler(0x8000, 0xFFFF, UNL8157Read); + cmdreg = reset = 0; + Sync(); +} + +static void UNL8157Reset(void) { + cmdreg = reset = 0; + reset++; + reset &= 0x1F; + Sync(); +} + +static void UNL8157Restore(int version) { + Sync(); +} + +void UNL8157_Init(CartInfo *info) { + info->Power = UNL8157Power; + info->Reset = UNL8157Reset; + GameStateRestore = UNL8157Restore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/82.cpp b/apps/fceux/src/boards/82.cpp new file mode 100644 index 00000000..a1a68b66 --- /dev/null +++ b/apps/fceux/src/boards/82.cpp @@ -0,0 +1,98 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * Taito X1-017 board, battery backed + * + */ + +#include "mapinc.h" + +static uint8 regs[9], ctrl; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { regs, 9, "REGS" }, + { &ctrl, 1, "CTRL" }, + { 0 } +}; + +static void Sync(void) { + uint32 swap = ((ctrl & 2) << 11); + setchr2(0x0000 ^ swap, regs[0] >> 1); + setchr2(0x0800 ^ swap, regs[1] >> 1); + setchr1(0x1000 ^ swap, regs[2]); + setchr1(0x1400 ^ swap, regs[3]); + setchr1(0x1800 ^ swap, regs[4]); + setchr1(0x1c00 ^ swap, regs[5]); + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, regs[6]); + setprg8(0xA000, regs[7]); + setprg8(0xC000, regs[8]); + setprg8(0xE000, ~0); + setmirror(ctrl & 1); +} + +static DECLFW(M82Write) { + if (A <= 0x7ef5) + regs[A & 7] = V; + else + switch (A) { + case 0x7ef6: ctrl = V & 3; break; + case 0x7efa: regs[6] = V >> 2; break; + case 0x7efb: regs[7] = V >> 2; break; + case 0x7efc: regs[8] = V >> 2; break; + } + Sync(); +} + +static void M82Power(void) { + Sync(); + SetReadHandler(0x6000, 0xffff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); + SetWriteHandler(0x7ef0, 0x7efc, M82Write); // external WRAM might end at $73FF + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M82Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper82_Init(CartInfo *info) { + info->Power = M82Power; + info->Close = M82Close; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/8237.cpp b/apps/fceux/src/boards/8237.cpp new file mode 100644 index 00000000..38077690 --- /dev/null +++ b/apps/fceux/src/boards/8237.cpp @@ -0,0 +1,173 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + * + * Super Game (Sugar Softec) protected mapper + * Pocahontas 2 (Unl) [U][!], etc. + * TODO: 9in1 LION KING HANGS! + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 cmdin; + +static uint8 regperm[8][8] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 2, 6, 1, 7, 3, 4, 5 }, + { 0, 5, 4, 1, 7, 2, 6, 3 }, // unused + { 0, 6, 3, 7, 5, 2, 4, 1 }, + { 0, 2, 5, 3, 6, 1, 7, 4 }, + { 0, 1, 2, 3, 4, 5, 6, 7 }, // empty + { 0, 1, 2, 3, 4, 5, 6, 7 }, // empty + { 0, 1, 2, 3, 4, 5, 6, 7 }, // empty +}; + +static uint8 adrperm[8][8] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 3, 2, 0, 4, 1, 5, 6, 7 }, + { 0, 1, 2, 3, 4, 5, 6, 7 }, // unused + { 5, 0, 1, 2, 3, 7, 6, 4 }, + { 3, 1, 0, 5, 2, 4, 6, 7 }, + { 0, 1, 2, 3, 4, 5, 6, 7 }, // empty + { 0, 1, 2, 3, 4, 5, 6, 7 }, // empty + { 0, 1, 2, 3, 4, 5, 6, 7 }, // empty +}; + +static void UNL8237CW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x40) + setchr1(A, ((EXPREGS[1] & 0xc) << 6) | (V & 0x7F) | ((EXPREGS[1] & 0x20) << 2)); + else + setchr1(A, ((EXPREGS[1] & 0xc) << 6) | V); +} + +static void UNL8237PW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x40) { + uint8 sbank = (EXPREGS[1] & 0x10); + if (EXPREGS[0] & 0x80) { + uint8 bank = ((EXPREGS[1] & 3) << 4) | (EXPREGS[0] & 0x7) | (sbank >> 1); + if (EXPREGS[0] & 0x20) + setprg32(0x8000, bank >> 1); + else{ + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else + setprg8(A, ((EXPREGS[1] & 3) << 5) | (V & 0x0F) | sbank); + } else { + if (EXPREGS[0] & 0x80) { + uint8 bank = ((EXPREGS[1] & 3) << 4) | (EXPREGS[0] & 0xF); + if (EXPREGS[0] & 0x20) + setprg32(0x8000, bank >> 1); + else{ + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else + setprg8(A, ((EXPREGS[1] & 3) << 5) | (V & 0x1F)); + } +} + +static void UNL8237ACW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x40) + setchr1(A, ((EXPREGS[1] & 0xE) << 7) | (V & 0x7F) | ((EXPREGS[1] & 0x20) << 2)); + else + setchr1(A, ((EXPREGS[1] & 0xE) << 7) | V); +} + +static void UNL8237APW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x40) { + uint8 sbank = (EXPREGS[1] & 0x10); + if (EXPREGS[0] & 0x80) { + uint8 bank = ((EXPREGS[1] & 3) << 4) | ((EXPREGS[1] & 8) << 3) | (EXPREGS[0] & 0x7) | (sbank >> 1); + if (EXPREGS[0] & 0x20) { +// FCEU_printf("8000:%02X\n",bank>>1); + setprg32(0x8000, bank >> 1); + } else { +// FCEU_printf("8000-C000:%02X\n",bank); + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else { +// FCEU_printf("%04x:%02X\n",A,((EXPREGS[1]&3)<<5)|((EXPREGS[1]&8)<<4)|(V&0x0F)|sbank); + setprg8(A, ((EXPREGS[1] & 3) << 5) | ((EXPREGS[1] & 8) << 4) | (V & 0x0F) | sbank); + } + } else { + if (EXPREGS[0] & 0x80) { + uint8 bank = ((EXPREGS[1] & 3) << 4) | ((EXPREGS[1] & 8) << 3) | (EXPREGS[0] & 0xF); + if (EXPREGS[0] & 0x20) { +// FCEU_printf("8000:%02X\n",(bank>>1)&0x07); + setprg32(0x8000, bank >> 1); + } else { +// FCEU_printf("8000-C000:%02X\n",bank&0x0F); + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else { +// FCEU_printf("%04X:%02X\n",A,(((EXPREGS[1]&3)<<5)|((EXPREGS[1]&8)<<4)|(V&0x1F))&0x1F); + setprg8(A, ((EXPREGS[1] & 3) << 5) | ((EXPREGS[1] & 8) << 4) | (V & 0x1F)); + } + } +} +static DECLFW(UNL8237Write) { + uint8 dat = V; + uint8 adr = adrperm[EXPREGS[2]][((A >> 12) & 6) | (A & 1)]; + uint16 addr = (adr & 1) | ((adr & 6) << 12) | 0x8000; + if (adr < 4) { + if (!adr) + dat = (dat & 0xC0) | (regperm[EXPREGS[2]][dat & 7]); + MMC3_CMDWrite(addr, dat); + } else + MMC3_IRQWrite(addr, dat); +} + +static DECLFW(UNL8237ExWrite) { + switch (A) { + case 0x5000: EXPREGS[0] = V; FixMMC3PRG(MMC3_cmd); break; + case 0x5001: EXPREGS[1] = V; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); break; + case 0x5007: EXPREGS[2] = V; break; + } +} + +static void UNL8237Power(void) { + EXPREGS[0] = EXPREGS[2] = 0; + EXPREGS[1] = 3; + GenMMC3Power(); + SetWriteHandler(0x8000, 0xFFFF, UNL8237Write); + SetWriteHandler(0x5000, 0x7FFF, UNL8237ExWrite); +} + +void UNL8237_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + cwrap = UNL8237CW; + pwrap = UNL8237PW; + info->Power = UNL8237Power; + AddExState(EXPREGS, 3, 0, "EXPR"); + AddExState(&cmdin, 1, 0, "CMDI"); +} + +void UNL8237A_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + cwrap = UNL8237ACW; + pwrap = UNL8237APW; + info->Power = UNL8237Power; + AddExState(EXPREGS, 3, 0, "EXPR"); + AddExState(&cmdin, 1, 0, "CMDI"); +} diff --git a/apps/fceux/src/boards/830118C.cpp b/apps/fceux/src/boards/830118C.cpp new file mode 100644 index 00000000..f04d9dbf --- /dev/null +++ b/apps/fceux/src/boards/830118C.cpp @@ -0,0 +1,68 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2008 CaH4e3 + * + * 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 + */ + +// M-022 MMC3 based 830118C T-106 4M + 4M + +#include "mapinc.h" +#include "mmc3.h" + +static void BMC830118CCW(uint32 A, uint8 V) { + setchr1(A, (V & 0x7F) | ((EXPREGS[0] & 0x0c) << 5)); +} + +static void BMC830118CPW(uint32 A, uint8 V) { + if ((EXPREGS[0] & 0x0C) == 0x0C) { + if (A == 0x8000) { + setprg8(A, (V & 0x0F) | ((EXPREGS[0] & 0x0c) << 2)); + setprg8(0xC000, (V & 0x0F) | 0x32); + } else if (A == 0xA000) { + setprg8(A, (V & 0x0F) | ((EXPREGS[0] & 0x0c) << 2)); + setprg8(0xE000, (V & 0x0F) | 0x32); + } + } else { + setprg8(A, (V & 0x0F) | ((EXPREGS[0] & 0x0c) << 2)); + } +} + +static DECLFW(BMC830118CLoWrite) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void BMC830118CReset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +static void BMC830118CPower(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6800, 0x68FF, BMC830118CLoWrite); +} + +void BMC830118C_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 8, 0); + pwrap = BMC830118CPW; + cwrap = BMC830118CCW; + info->Power = BMC830118CPower; + info->Reset = BMC830118CReset; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/88.cpp b/apps/fceux/src/boards/88.cpp new file mode 100644 index 00000000..7341f251 --- /dev/null +++ b/apps/fceux/src/boards/88.cpp @@ -0,0 +1,83 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg[8]; +static uint8 mirror, cmd, is154; + +static SFORMAT StateRegs[] = +{ + { &cmd, 1, "CMD" }, + { &mirror, 1, "MIRR" }, + { reg, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setchr2(0x0000, reg[0] >> 1); + setchr2(0x0800, reg[1] >> 1); + setchr1(0x1000, reg[2] | 0x40); + setchr1(0x1400, reg[3] | 0x40); + setchr1(0x1800, reg[4] | 0x40); + setchr1(0x1C00, reg[5] | 0x40); + setprg8(0x8000, reg[6]); + setprg8(0xA000, reg[7]); + setprg8(0xC000, ~1); + setprg8(0xE000, ~0); +} + +static void MSync(void) { + if (is154) setmirror(MI_0 + (mirror & 1)); +} + +static DECLFW(M88Write) { + switch (A & 0x8001) { + case 0x8000: cmd = V & 7; mirror = V >> 6; MSync(); break; + case 0x8001: reg[cmd] = V; Sync(); break; + } +} + +static void M88Power(void) { + reg[0] = reg[1] = reg[2] = reg[3] = reg[4] = reg[5] = reg[6] = reg[7] = 0; + Sync(); + MSync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M88Write); +} + +static void StateRestore(int version) { + Sync(); + MSync(); +} + +void Mapper88_Init(CartInfo *info) { + is154 = 0; + info->Power = M88Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper154_Init(CartInfo *info) { + is154 = 1; + info->Power = M88Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/8in1.cpp b/apps/fceux/src/boards/8in1.cpp new file mode 100644 index 00000000..3e2f9640 --- /dev/null +++ b/apps/fceux/src/boards/8in1.cpp @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2016 CaH4e3 + * + * 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 + * + * 8-in-1 Rockin' Kats, Snake, (PCB marked as "8 in 1"), similar to 12IN1, + * but with MMC3 on board, all games are hacked the same, Snake is buggy too! + * + * no reset-citcuit, so selected game can be reset, but to change it you must use power + * + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void BMC8IN1CW(uint32 A, uint8 V) { + setchr1(A, ((EXPREGS[0] & 0xC) << 5) | (V & 0x7F)); +} + +static void BMC8IN1PW(uint32 A, uint8 V) { + if(EXPREGS[0] & 0x10) { // MMC3 mode + setprg8(A, ((EXPREGS[0] & 0xC) << 2) | (V & 0xF)); + } else { + setprg32(0x8000, EXPREGS[0] & 0xF); + } +} + +static DECLFW(BMC8IN1Write) { + if(A & 0x1000) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else { + if(A < 0xC000) + MMC3_CMDWrite(A, V); + else + MMC3_IRQWrite(A, V); + } +} + +static void BMC8IN1Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x8000, 0xFFFF, BMC8IN1Write); +} + +void BMC8IN1_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 0, 0); + cwrap = BMC8IN1CW; + pwrap = BMC8IN1PW; + info->Power = BMC8IN1Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/90.cpp b/apps/fceux/src/boards/90.cpp new file mode 100644 index 00000000..7c873d26 --- /dev/null +++ b/apps/fceux/src/boards/90.cpp @@ -0,0 +1,507 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +//#define DEBUG90 + +// Mapper 090 is simpliest mapper hardware and have not extended nametable control and latched chr banks in 4k mode +// Mapper 209 much compicated hardware with decribed above features disabled by default and switchable by command +// Mapper 211 the same mapper 209 but with forced nametable control + +static int is209; +static int is211; + +static uint8 IRQMode; // from $c001 +static uint8 IRQPre; // from $c004 +static uint8 IRQPreSize; // from $c007 +static uint8 IRQCount; // from $c005 +static uint8 IRQXOR; // Loaded from $C006 +static uint8 IRQa; // $c002, $c003, and $c000 + +static uint8 mul[2]; +static uint8 regie; + +static uint8 tkcom[4]; +static uint8 prgb[4]; +static uint8 chrlow[8]; +static uint8 chrhigh[8]; + +static uint8 chr[2]; + +static uint16 names[4]; +static uint8 tekker; + +static SFORMAT Tek_StateRegs[] = { + { &IRQMode, 1, "IRQM" }, + { &IRQPre, 1, "IRQP" }, + { &IRQPreSize, 1, "IRQR" }, + { &IRQCount, 1, "IRQC" }, + { &IRQXOR, 1, "IRQX" }, + { &IRQa, 1, "IRQA" }, + { mul, 2, "MUL" }, + { ®ie, 1, "REGI" }, + { tkcom, 4, "TKCO" }, + { prgb, 4, "PRGB" }, + { chr, 2, "CLTC" }, + { chrlow, 4, "CHRL" }, + { chrhigh, 8, "CHRH" }, + { &names[0], 2 | FCEUSTATE_RLSB, "NMS0" }, + { &names[1], 2 | FCEUSTATE_RLSB, "NMS1" }, + { &names[2], 2 | FCEUSTATE_RLSB, "NMS2" }, + { &names[3], 2 | FCEUSTATE_RLSB, "NMS3" }, + { &tekker, 1, "TEKR" }, + { 0 } +}; + +static void mira(void) +{ + if((tkcom[0]&0x20&&is209)||is211) + { + int x; + if(tkcom[0]&0x40) // Name tables are ROM-only + { + for(x=0;x<4;x++) + setntamem(CHRptr[0]+(((names[x])&CHRmask1[0])<<10),0,x); + } + else // Name tables can be RAM or ROM. + { + for(x=0;x<4;x++) + { + if((tkcom[1]&0x80)==(names[x]&0x80)) // RAM selected. + setntamem(NTARAM+((names[x]&0x1)<<10),1,x); + else + setntamem(CHRptr[0]+(((names[x])&CHRmask1[0])<<10),0,x); + } + } + } + else + { + switch(tkcom[1]&3) + { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } + } +} + +static void tekprom(void) +{ + uint32 bankmode=((tkcom[3]&6)<<5); + switch(tkcom[0]&7) + { + case 00: if(tkcom[0]&0x80) + setprg8(0x6000,(((prgb[3]<<2)+3)&0x3F)|bankmode); + setprg32(0x8000,0x0F|((tkcom[3]&6)<<3)); + break; + case 01: if(tkcom[0]&0x80) + setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode); + setprg16(0x8000,(prgb[1]&0x1F)|((tkcom[3]&6)<<4)); + setprg16(0xC000,0x1F|((tkcom[3]&6)<<4)); + break; + case 03: // bit reversion + case 02: if(tkcom[0]&0x80) + setprg8(0x6000,(prgb[3]&0x3F)|bankmode); + setprg8(0x8000,(prgb[0]&0x3F)|bankmode); + setprg8(0xa000,(prgb[1]&0x3F)|bankmode); + setprg8(0xc000,(prgb[2]&0x3F)|bankmode); + setprg8(0xe000,0x3F|bankmode); + break; + case 04: if(tkcom[0]&0x80) + setprg8(0x6000,(((prgb[3]<<2)+3)&0x3F)|bankmode); + setprg32(0x8000,(prgb[3]&0x0F)|((tkcom[3]&6)<<3)); + break; + case 05: if(tkcom[0]&0x80) + setprg8(0x6000,(((prgb[3]<<1)+1)&0x3F)|bankmode); + setprg16(0x8000,(prgb[1]&0x1F)|((tkcom[3]&6)<<4)); + setprg16(0xC000,(prgb[3]&0x1F)|((tkcom[3]&6)<<4)); + break; + case 07: // bit reversion + case 06: if(tkcom[0]&0x80) + setprg8(0x6000,(prgb[3]&0x3F)|bankmode); + setprg8(0x8000,(prgb[0]&0x3F)|bankmode); + setprg8(0xa000,(prgb[1]&0x3F)|bankmode); + setprg8(0xc000,(prgb[2]&0x3F)|bankmode); + setprg8(0xe000,(prgb[3]&0x3F)|bankmode); + break; + } +} + +static void tekvrom(void) +{ + int x, bank=0, mask=0xFFFF; + if(!(tkcom[3]&0x20)) + { + bank=(tkcom[3]&1)|((tkcom[3]&0x18)>>2); + switch (tkcom[0]&0x18) + { + case 0x00: bank<<=5; mask=0x1F; break; + case 0x08: bank<<=6; mask=0x3F; break; + case 0x10: bank<<=7; mask=0x7F; break; + case 0x18: bank<<=8; mask=0xFF; break; + } + } + switch(tkcom[0]&0x18) + { + case 0x00: // 8KB + setchr8(((chrlow[0]|(chrhigh[0]<<8))&mask)|bank); + break; + case 0x08: // 4KB +// for(x=0;x<8;x+=4) +// setchr4(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank); + setchr4(0x0000,((chrlow[chr[0]]|(chrhigh[chr[0]]<<8))&mask)|bank); + setchr4(0x1000,((chrlow[chr[1]]|(chrhigh[chr[1]]<<8))&mask)|bank); + break; + case 0x10: // 2KB + for(x=0;x<8;x+=2) + setchr2(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank); + break; + case 0x18: // 1KB + for(x=0;x<8;x++) + setchr1(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank); + break; + } +} + +static DECLFW(M90TekWrite) +{ + switch(A&0x5C03) + { + case 0x5800: mul[0]=V; break; + case 0x5801: mul[1]=V; break; + case 0x5803: regie=V; break; + } +} + +static DECLFR(M90TekRead) +{ + switch(A&0x5C03) + { + case 0x5800: return (mul[0]*mul[1]); + case 0x5801: return((mul[0]*mul[1])>>8); + case 0x5803: return (regie); + default: return tekker; + } + return(0xff); +} + +static DECLFW(M90PRGWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); + prgb[A&3]=V; + tekprom(); +} + +static DECLFW(M90CHRlowWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); + chrlow[A&7]=V; + tekvrom(); +} + +static DECLFW(M90CHRhiWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); + chrhigh[A&7]=V; + tekvrom(); +} + +static DECLFW(M90NTWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); + if(A&4) + { + names[A&3]&=0x00FF; + names[A&3]|=V<<8; + } + else + { + names[A&3]&=0xFF00; + names[A&3]|=V; + } + mira(); +} + +static DECLFW(M90IRQWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); + switch(A&7) + { + case 00: //FCEU_printf("%s IRQ (C000)\n",V&1?"Enable":"Disable"); + IRQa=V&1;if(!(V&1)) X6502_IRQEnd(FCEU_IQEXT);break; + case 02: //FCEU_printf("Disable IRQ (C002) scanline=%d\n", scanline); + IRQa=0;X6502_IRQEnd(FCEU_IQEXT);break; + case 03: //FCEU_printf("Enable IRQ (C003) scanline=%d\n", scanline); + IRQa=1;break; + case 01: IRQMode=V; + // FCEU_printf("IRQ Count method: "); + // switch (IRQMode&3) + // { + // case 00: FCEU_printf("M2 cycles\n");break; + // case 01: FCEU_printf("PPU A12 toggles\n");break; + // case 02: FCEU_printf("PPU reads\n");break; + // case 03: FCEU_printf("Writes to CPU space\n");break; + // } + // FCEU_printf("Counter prescaler size: %s\n",(IRQMode&4)?"3 bits":"8 bits"); + // FCEU_printf("Counter prescaler size adjust: %s\n",(IRQMode&8)?"Used C007":"Normal Operation"); + // if((IRQMode>>6)==2) FCEU_printf("Counter Down\n"); + // else if((IRQMode>>6)==1) FCEU_printf("Counter Up\n"); + // else FCEU_printf("Counter Stopped\n"); + break; + case 04: //FCEU_printf("Pre Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR); + IRQPre=V^IRQXOR;break; + case 05: //FCEU_printf("Main Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR); + IRQCount=V^IRQXOR;break; + case 06: //FCEU_printf("Xor Value: %d\n",V); + IRQXOR=V;break; + case 07: //if(!(IRQMode&8)) FCEU_printf("C001 is clear, no effect applied\n"); + // else if(V==0xFF) FCEU_printf("Prescaler is changed for 12bits\n"); + // else FCEU_printf("Counter Stopped\n"); + IRQPreSize=V;break; + } +} + +static DECLFW(M90ModeWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); + tkcom[A&3]=V; + tekprom(); + tekvrom(); + mira(); + +#ifdef DEBUG90 + switch (A&3) + { + case 00: FCEU_printf("Main Control Register:\n"); + FCEU_printf(" PGR Banking mode: %d\n",V&7); + FCEU_printf(" CHR Banking mode: %d\n",(V>>3)&3); + FCEU_printf(" 6000-7FFF addresses mapping: %s\n",(V&0x80)?"Yes":"No"); + FCEU_printf(" Nametable control: %s\n",(V&0x20)?"Enabled":"Disabled"); + if(V&0x20) + FCEU_printf(" Nametable can be: %s\n",(V&0x40)?"ROM Only":"RAM or ROM"); + break; + case 01: FCEU_printf("Mirroring mode: "); + switch (V&3) + { + case 0: FCEU_printf("Vertical\n");break; + case 1: FCEU_printf("Horizontal\n");break; + case 2: FCEU_printf("Nametable 0 only\n");break; + case 3: FCEU_printf("Nametable 1 only\n");break; + } + FCEU_printf("Mirroring flag: %s\n",(V&0x80)?"On":"Off"); + break; + case 02: if((((tkcom[0])>>5)&3)==1) + FCEU_printf("Nametable ROM/RAM select mode: %d\n",V>>7); + break; + case 03: + FCEU_printf("CHR Banking mode: %s\n",(V&0x20)?"Entire CHR ROM":"256Kb Switching mode"); + if(!(V&0x20)) FCEU_printf("256K CHR bank number: %02x\n",(V&1)|((V&0x18)>>2)); + FCEU_printf("512K PRG bank number: %d\n",(V&6)>>1); + FCEU_printf("CHR Bank mirroring: %s\n",(V&0x80)?"Swapped":"Normal operate"); + } +#endif +} + +static DECLFW(M90DummyWrite) +{ +// FCEU_printf("bs %04x %02x\n",A,V); +} + +static void CCL(void) +{ + if((IRQMode>>6) == 1) // Count Up + { + IRQCount++; + if((IRQCount == 0) && IRQa) + { + X6502_IRQBegin(FCEU_IQEXT); + } + } + else if((IRQMode>>6) == 2) // Count down + { + IRQCount--; + if((IRQCount == 0xFF) && IRQa) + { + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void ClockCounter(void) +{ + uint8 premask; + + if(IRQMode & 0x4) + premask = 0x7; + else + premask = 0xFF; + if((IRQMode>>6) == 1) // Count up + { + IRQPre++; + if((IRQPre & premask) == 0) CCL(); + } + else if((IRQMode>>6) == 2) // Count down + { + IRQPre--; + if((IRQPre & premask) == premask) CCL(); + } +} + +void CPUWrap(int a) +{ + int x; + if((IRQMode&3)==0) for(x=0;x>8; + if(h<0x20&&((h&0x0F)==0xF)) + { + l=A&0xF0; + if(l==0xD0) + { + chr[(h&0x10)>>4]=((h&0x10)>>2); + tekvrom(); + } + else if(l==0xE0) + { + chr[(h&0x10)>>4]=((h&0x10)>>2)|2; + tekvrom(); + } + } + } + else + { + chr[0]=0; + chr[1]=4; + } +} + +static void togglie() +{ + tekker+=0x40; + tekker&=0xC0; + FCEU_printf("tekker=%02x\n",tekker); + memset(tkcom,0x00,sizeof(tkcom)); + memset(prgb,0xff,sizeof(prgb)); + tekprom(); + tekvrom(); +} + +static void M90Restore(int version) +{ + tekprom(); + tekvrom(); + mira(); +} + +static void M90Power(void) +{ + SetWriteHandler(0x5000,0x5fff,M90TekWrite); + SetWriteHandler(0x8000,0x8ff0,M90PRGWrite); + SetWriteHandler(0x9000,0x9fff,M90CHRlowWrite); + SetWriteHandler(0xA000,0xAfff,M90CHRhiWrite); + SetWriteHandler(0xB000,0xBfff,M90NTWrite); + SetWriteHandler(0xC000,0xCfff,M90IRQWrite); + SetWriteHandler(0xD000,0xD5ff,M90ModeWrite); + SetWriteHandler(0xE000,0xFfff,M90DummyWrite); + + + SetReadHandler(0x5000,0x5fff,M90TekRead); + SetReadHandler(0x6000,0xffff,CartBR); + + mul[0]=mul[1]=regie=0xFF; + + memset(tkcom,0x00,sizeof(tkcom)); + memset(prgb,0xff,sizeof(prgb)); + memset(chrlow,0xff,sizeof(chrlow)); + memset(chrhigh,0xff,sizeof(chrhigh)); + memset(names,0x00,sizeof(names)); + + if(is211) + tekker=0xC0; + else + tekker=0x00; + + tekprom(); + tekvrom(); +} + + +void Mapper90_Init(CartInfo *info) +{ + is211=0; + is209=0; + info->Reset=togglie; + info->Power=M90Power; + PPU_hook=M90PPU; + MapIRQHook=CPUWrap; + GameHBIRQHook2=SLWrap; + GameStateRestore=M90Restore; + AddExState(Tek_StateRegs, ~0, 0, 0); +} + +void Mapper209_Init(CartInfo *info) +{ + is211=0; + is209=1; + info->Reset=togglie; + info->Power=M90Power; + PPU_hook=M90PPU; + MapIRQHook=CPUWrap; + GameHBIRQHook2=SLWrap; + GameStateRestore=M90Restore; + AddExState(Tek_StateRegs, ~0, 0, 0); +} + +void Mapper211_Init(CartInfo *info) +{ + is211=1; + info->Reset=togglie; + info->Power=M90Power; + PPU_hook=M90PPU; + MapIRQHook=CPUWrap; + GameHBIRQHook2=SLWrap; + GameStateRestore=M90Restore; + AddExState(Tek_StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/91.cpp b/apps/fceux/src/boards/91.cpp new file mode 100644 index 00000000..d1ed61b3 --- /dev/null +++ b/apps/fceux/src/boards/91.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 cregs[4], pregs[2]; +static uint8 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { cregs, 4, "CREG" }, + { pregs, 2, "PREG" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 1, "IRQC" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x8000, pregs[0]); + setprg8(0xa000, pregs[1]); + setprg8(0xc000, ~1); + setprg8(0xe000, ~0); + setchr2(0x0000, cregs[0]); + setchr2(0x0800, cregs[1]); + setchr2(0x1000, cregs[2]); + setchr2(0x1800, cregs[3]); +} + +static DECLFW(M91Write0) { + cregs[A & 3] = V; + Sync(); +} + +static DECLFW(M91Write1) { + switch (A & 3) { + case 0: + case 1: pregs[A & 1] = V; Sync(); break; + case 2: IRQa = IRQCount = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 3: IRQa = 1; X6502_IRQEnd(FCEU_IQEXT); break; + } +} + +static void M91Power(void) { + Sync(); + SetWriteHandler(0x6000, 0x6fff, M91Write0); + SetWriteHandler(0x7000, 0x7fff, M91Write1); + SetReadHandler(0x8000, 0xffff, CartBR); +} + +static void M91IRQHook(void) { + if (IRQCount < 8 && IRQa) { + IRQCount++; + if (IRQCount >= 8) { + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper91_Init(CartInfo *info) { + info->Power = M91Power; + GameHBIRQHook = M91IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/96.cpp b/apps/fceux/src/boards/96.cpp new file mode 100644 index 00000000..08c42d6b --- /dev/null +++ b/apps/fceux/src/boards/96.cpp @@ -0,0 +1,70 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Xodnizel + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg, ppulatch; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { &ppulatch, 1, "PPUL" }, + { 0 } +}; + +static void Sync(void) { + setmirror(MI_0); + setprg32(0x8000, reg & 3); + setchr4(0x0000, (reg & 4) | ppulatch); + setchr4(0x1000, (reg & 4) | 3); +} + +static DECLFW(M96Write) { + reg = V; + Sync(); +} + +static void M96Hook(uint32 A) { + if ((A & 0x3000) == 0x2000) { + ppulatch = (A >> 8) & 3; + Sync(); + } +} + +static void M96Power(void) { + reg = ppulatch = 0; + Sync(); + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x8000, 0xffff, M96Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper96_Init(CartInfo *info) { + info->Power = M96Power; + PPU_hook = M96Hook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + diff --git a/apps/fceux/src/boards/99.cpp b/apps/fceux/src/boards/99.cpp new file mode 100644 index 00000000..b9df3405 --- /dev/null +++ b/apps/fceux/src/boards/99.cpp @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 latch; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static writefunc old4016; + +static SFORMAT StateRegs[] = +{ + { &latch, 1, "LATC" }, + { 0 } +}; + +static void Sync(void) { + setchr8((latch >> 2) & 1); + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, 0); + setprg8(0x8000, latch & 4); /* Special for VS Gumshoe */ +} + +static DECLFW(M99Write) { + latch = V; + Sync(); + old4016(A, V); +} + +static void M99Power(void) { + latch = 0; + Sync(); + old4016 = GetWriteHandler(0x4016); + SetWriteHandler(0x4016, 0x4016, M99Write); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M99Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper99_Init(CartInfo *info) { + info->Power = M99Power; + info->Close = M99Close; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/BMW8544.cpp b/apps/fceux/src/boards/BMW8544.cpp new file mode 100644 index 00000000..5c1f6133 --- /dev/null +++ b/apps/fceux/src/boards/BMW8544.cpp @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2015 CaH4e3 + * + * 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 + * + * "Dragon Fighter" protected MMC3 based custom mapper board + * mostly hacky implementation, I can't verify if this mapper can read a RAM of the + * console or watches the bus writes somehow. + * + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void UNLBMW8544PW(uint32 A, uint8 V) { + if(A == 0x8000) + setprg8(A,EXPREGS[0] & 0x1F); // the real hardware has this bank overrided with it's own register, + else // but MMC3 prg swap still works and you can actually change bank C000 at the same time if use 0x46 cmd + setprg8(A,V); +} + +static void UNLBMW8544CW(uint32 A, uint8 V) { + if(A == 0x0000) + setchr2(0x0000,(V >> 1) ^ EXPREGS[1]); + else if (A == 0x0800) + setchr2(0x0800,(V >> 1) | ((EXPREGS[2] & 0x40) << 1)); + else if (A == 0x1000) + setchr4(0x1000, EXPREGS[2] & 0x3F); + +} + +static DECLFW(UNLBMW8544ProtWrite) { + if(!(A & 1)) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + } +} + +static DECLFR(UNLBMW8544ProtRead) { + if(!fceuindbg) { + if(!(A & 1)) { + if((EXPREGS[0] & 0xE0) == 0xC0) { + EXPREGS[1] = ARead[0x6a](0x6a); // program can latch some data from the BUS, but I can't say how exactly, + } else { // without more euipment and skills ;) probably here we can try to get any write + EXPREGS[2] = ARead[0xff](0xff); // before the read operation + } + FixMMC3CHR(MMC3_cmd & 0x7F); // there are more different behaviour of the board isn't used by game itself, so unimplemented here and + } // actually will break the current logic ;) + } + return 0; +} + +static void UNLBMW8544Power(void) { + GenMMC3Power(); + SetWriteHandler(0x6000, 0x6FFF, UNLBMW8544ProtWrite); + SetReadHandler(0x6000, 0x6FFF, UNLBMW8544ProtRead); +} + +void UNLBMW8544_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 0, 0); + pwrap = UNLBMW8544PW; + cwrap = UNLBMW8544CW; + info->Power = UNLBMW8544Power; + AddExState(EXPREGS, 3, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/F-15.cpp b/apps/fceux/src/boards/F-15.cpp new file mode 100644 index 00000000..7e18c7be --- /dev/null +++ b/apps/fceux/src/boards/F-15.cpp @@ -0,0 +1,62 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2015 CaH4e3 + * + * 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 + * + * BMC F-15 PCB (256+266 MMC3 based, with 16/32Kb banking discrete logic) + * 150-in-1 Unchaied Melody FIGHT version with system test (START+SELECT) + * + * CHR - MMC3 stock regs + * PRG - MMC3 regs disabled, area 6000-7FFF used instead + * 011xxxxxxxxxxxxx addr mask, + * ----APPp reg bits mask + * A - higher 128K PRG bank select/32K bank mode override + * PP - bank number in 32K mode + * PPp - bank number in 16K mode + * initial state of extra regs is undefined, A001 enables/disables the 6000 area + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void BMCF15PW(uint32 A, uint8 V) { + uint32 bank = EXPREGS[0] & 0xF; + uint32 mode = (EXPREGS[0] & 8) >> 3; + uint32 mask = ~(mode); + setprg16(0x8000, (bank & mask)); + setprg16(0xC000, (bank & mask) | mode); +} + +static DECLFW(BMCF15Write) { + if (A001B & 0x80) { + EXPREGS[0] = V & 0xF; + FixMMC3PRG(MMC3_cmd); + } +} + +static void BMCF15Power(void) { + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, BMCF15Write); + SetWriteHandler(0x6000, 0x7FFF, BMCF15Write); +} + +void BMCF15_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + pwrap = BMCF15PW; + info->Power = BMCF15Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/__dummy_mapper.cpp b/apps/fceux/src/boards/__dummy_mapper.cpp new file mode 100644 index 00000000..505f9169 --- /dev/null +++ b/apps/fceux/src/boards/__dummy_mapper.cpp @@ -0,0 +1,100 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2013 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg[8]; +static uint8 IRQa; +static int16 IRQCount, IRQLatch; +/* +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static uint8 *CHRRAM = NULL; +static uint32 CHRRAMSIZE; +*/ + +static SFORMAT StateRegs[] = +{ + { reg, 8, "REGS" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 2, "IRQL" }, + { 0 } +}; + +static void Sync(void) { +} + +static DECLFW(MNNNWrite) { +} + +static void MNNNPower(void) { +// SetReadHandler(0x6000, 0x7fff, CartBR); +// SetWriteHandler(0x6000, 0x7fff, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, MNNNWrite); +} + +static void MNNNReset(void) { +} + +/* +static void MNNNClose(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + if (CHRRAM) + FCEU_gfree(CHRRAM); + WRAM = CHRRAM = NULL; +} +*/ + +static void MNNNIRQHook() { + X6502_IRQBegin(FCEU_IQEXT); +} + +static void StateRestore(int version) { + Sync(); +} + +void MapperNNN_Init(CartInfo *info) { + info->Reset = MNNNReset; + info->Power = MNNNPower; +// info->Close = MNNNClose; + GameHBIRQHook = MNNNIRQHook; + GameStateRestore = StateRestore; +/* + CHRRAMSIZE = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); +*/ +/* + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } +*/ + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/a9746.cpp b/apps/fceux/src/boards/a9746.cpp new file mode 100644 index 00000000..321ac8aa --- /dev/null +++ b/apps/fceux/src/boards/a9746.cpp @@ -0,0 +1,75 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static DECLFW(UNLA9746Write) { +// FCEU_printf("write raw %04x:%02x\n",A,V); + switch (A & 0xE003) { + case 0x8000: EXPREGS[1] = V; EXPREGS[0] = 0; break; + case 0x8002: EXPREGS[0] = V; EXPREGS[1] = 0; break; + case 0x8001: + { + uint8 bits_rev = ((V & 0x20) >> 5) | ((V & 0x10) >> 3) | ((V & 0x08) >> 1) | ((V & 0x04) << 1); + switch (EXPREGS[0]) { + case 0x26: setprg8(0x8000, bits_rev); break; + case 0x25: setprg8(0xA000, bits_rev); break; + case 0x24: setprg8(0xC000, bits_rev); break; + case 0x23: setprg8(0xE000, bits_rev); break; + } + switch (EXPREGS[1]) { + case 0x0a: + case 0x08: EXPREGS[2] = (V << 4); break; + case 0x09: setchr1(0x0000, EXPREGS[2] | (V >> 1)); break; + case 0x0b: setchr1(0x0400, EXPREGS[2] | (V >> 1) | 1); break; + case 0x0c: + case 0x0e: EXPREGS[2] = (V << 4); break; + case 0x0d: setchr1(0x0800, EXPREGS[2] | (V >> 1)); break; + case 0x0f: setchr1(0x0c00, EXPREGS[2] | (V >> 1) | 1); break; + case 0x10: + case 0x12: EXPREGS[2] = (V << 4); break; + case 0x11: setchr1(0x1000, EXPREGS[2] | (V >> 1)); break; + case 0x14: + case 0x16: EXPREGS[2] = (V << 4); break; + case 0x15: setchr1(0x1400, EXPREGS[2] | (V >> 1)); break; + case 0x18: + case 0x1a: EXPREGS[2] = (V << 4); break; + case 0x19: setchr1(0x1800, EXPREGS[2] | (V >> 1)); break; + case 0x1c: + case 0x1e: EXPREGS[2] = (V << 4); break; + case 0x1d: setchr1(0x1c00, EXPREGS[2] | (V >> 1)); break; + } + } + break; + } +} + +static void UNLA9746Power(void) { + GenMMC3Power(); + SetWriteHandler(0x8000, 0xbfff, UNLA9746Write); +} + +void UNLA9746_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 0, 0); + info->Power = UNLA9746Power; + AddExState(EXPREGS, 6, 0, "EXPR"); +} + diff --git a/apps/fceux/src/boards/ac-08.cpp b/apps/fceux/src/boards/ac-08.cpp new file mode 100644 index 00000000..2c32fda2 --- /dev/null +++ b/apps/fceux/src/boards/ac-08.cpp @@ -0,0 +1,68 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg, mirr; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, reg); + setprg32r(1, 0x8000, 0); + setchr8(0); + setmirror(mirr); +} + +static DECLFW(AC08Mirr) { + mirr = ((V & 8) >> 3) ^ 1; + Sync(); +} + +static DECLFW(AC08Write) { + if (A == 0x8001) // Green Berret bank switching is only 100x xxxx xxxx xxx1 mask + reg = (V >> 1) & 0xf; + else + reg = V & 0xf; // Sad But True, 2-in-1 mapper, Green Berret need value shifted left one byte, Castlevania doesn't + Sync(); +} + +static void AC08Power(void) { + reg = 0; + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x4025, 0x4025, AC08Mirr); + SetWriteHandler(0x8000, 0xFFFF, AC08Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void AC08_Init(CartInfo *info) { + info->Power = AC08Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/addrlatch.cpp b/apps/fceux/src/boards/addrlatch.cpp new file mode 100644 index 00000000..7633bce8 --- /dev/null +++ b/apps/fceux/src/boards/addrlatch.cpp @@ -0,0 +1,523 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint16 latche, latcheinit; +static uint16 addrreg0, addrreg1; +static uint8 dipswitch; +static void (*WSync)(void); +static readfunc defread; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static DECLFW(LatchWrite) { + latche = A; + WSync(); +} + +static void LatchReset(void) { + latche = latcheinit; + WSync(); +} + +static void LatchPower(void) { + latche = latcheinit; + WSync(); + if (WRAM) { + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + } else + SetReadHandler(0x6000, 0xFFFF, defread); + SetWriteHandler(addrreg0, addrreg1, LatchWrite); +} + +static void LatchClose(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + WSync(); +} + +static void Latch_Init(CartInfo *info, void (*proc)(void), readfunc func, uint16 linit, uint16 adr0, uint16 adr1, uint8 wram) { + latcheinit = linit; + addrreg0 = adr0; + addrreg1 = adr1; + WSync = proc; + if (func != NULL) + defread = func; + else + defread = CartBROB; + info->Power = LatchPower; + info->Reset = LatchReset; + info->Close = LatchClose; + if (wram) { + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + } + GameStateRestore = StateRestore; + AddExState(&latche, 2, 0, "LATC"); +} + +//------------------ BMCD1038 --------------------------- + +static void BMCD1038Sync(void) { + if (latche & 0x80) { + setprg16(0x8000, (latche & 0x70) >> 4); + setprg16(0xC000, (latche & 0x70) >> 4); + } else + setprg32(0x8000, (latche & 0x60) >> 5); + setchr8(latche & 7); + setmirror(((latche & 8) >> 3) ^ 1); +} + +static DECLFR(BMCD1038Read) { + if (latche & 0x100) + return dipswitch; + else + return CartBR(A); +} + +static void BMCD1038Reset(void) { + dipswitch++; + dipswitch &= 3; +} + +void BMCD1038_Init(CartInfo *info) { + Latch_Init(info, BMCD1038Sync, BMCD1038Read, 0x0000, 0x8000, 0xFFFF, 0); + info->Reset = BMCD1038Reset; + AddExState(&dipswitch, 1, 0, "DIPSW"); +} + +//------------------ UNL43272 --------------------------- +// mapper much complex, including 16K bankswitching +static void UNL43272Sync(void) { + if ((latche & 0x81) == 0x81) { + setprg32(0x8000, (latche & 0x38) >> 3); + } else + FCEU_printf("unrecognized command %04!\n", latche); + setchr8(0); + setmirror(0); +} + +static DECLFR(UNL43272Read) { + if (latche & 0x400) + return CartBR(A & 0xFE); + else + return CartBR(A); +} + +static void UNL43272Reset(void) { + latche = 0; + UNL43272Sync(); +} + +void UNL43272_Init(CartInfo *info) { + Latch_Init(info, UNL43272Sync, UNL43272Read, 0x0081, 0x8000, 0xFFFF, 0); + info->Reset = UNL43272Reset; + AddExState(&dipswitch, 1, 0, "DIPSW"); +} + +//------------------ Map 058 --------------------------- + +static void BMCGK192Sync(void) { + if (latche & 0x40) { + setprg16(0x8000, latche & 7); + setprg16(0xC000, latche & 7); + } else + setprg32(0x8000, (latche >> 1) & 3); + setchr8((latche >> 3) & 7); + setmirror(((latche & 0x80) >> 7) ^ 1); +} + +void BMCGK192_Init(CartInfo *info) { + Latch_Init(info, BMCGK192Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 059 --------------------------- +// One more forgotten mapper +static void M59Sync(void) { + setprg32(0x8000, (latche >> 4) & 7); + setchr8(latche & 0x7); + setmirror((latche >> 3) & 1); +} + +static DECLFR(M59Read) { + if (latche & 0x100) + return 0; + else + return CartBR(A); +} + +void Mapper59_Init(CartInfo *info) { + Latch_Init(info, M59Sync, M59Read, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 061 --------------------------- +static void M61Sync(void) { + if (((latche & 0x10) << 1) ^ (latche & 0x20)) { + setprg16(0x8000, ((latche & 0xF) << 1) | (((latche & 0x20) >> 4))); + setprg16(0xC000, ((latche & 0xF) << 1) | (((latche & 0x20) >> 4))); + } else + setprg32(0x8000, latche & 0xF); + setchr8(0); + setmirror(((latche >> 7) & 1) ^ 1); +} + +void Mapper61_Init(CartInfo *info) { + Latch_Init(info, M61Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 092 --------------------------- +// Another two-in-one mapper, two Jaleco carts uses similar +// hardware, but with different wiring. +// Original code provided by LULU +// Additionally, PCB contains DSP extra sound chip, used for voice samples (unemulated) + +static void M92Sync(void) { + uint8 reg = latche & 0xF0; + setprg16(0x8000, 0); + if (latche >= 0x9000) { + switch (reg) { + case 0xD0: setprg16(0xc000, latche & 15); break; + case 0xE0: setchr8(latche & 15); break; + } + } else { + switch (reg) { + case 0xB0: setprg16(0xc000, latche & 15); break; + case 0x70: setchr8(latche & 15); break; + } + } +} + +void Mapper92_Init(CartInfo *info) { + Latch_Init(info, M92Sync, NULL, 0x80B0, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 200 --------------------------- + +static void M200Sync(void) { + setprg16(0x8000, latche & 7); + setprg16(0xC000, latche & 7); + setchr8(latche & 7); + setmirror((latche & 8) >> 3); +} + +void Mapper200_Init(CartInfo *info) { + Latch_Init(info, M200Sync, NULL, 0xFFFF, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 201 --------------------------- + +static void M201Sync(void) { + if (latche & 8) { + setprg32(0x8000, latche & 3); + setchr8(latche & 3); + } else { + setprg32(0x8000, 0); + setchr8(0); + } +} + +void Mapper201_Init(CartInfo *info) { + Latch_Init(info, M201Sync, NULL, 0xFFFF, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 202 --------------------------- + +static void M202Sync(void) { + // According to more carefull hardware tests and PCB study + int32 mirror = latche & 1; + int32 bank = (latche >> 1) & 0x7; + int32 select = (mirror & (bank >> 2)); + setprg16(0x8000, select ? (bank & 6) | 0 : bank); + setprg16(0xc000, select ? (bank & 6) | 1 : bank); + setmirror(mirror ^ 1); + setchr8(bank); +} + +void Mapper202_Init(CartInfo *info) { + Latch_Init(info, M202Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 204 --------------------------- + +static void M204Sync(void) { + int32 tmp2 = latche & 0x6; + int32 tmp1 = tmp2 + ((tmp2 == 0x6) ? 0 : (latche & 1)); + setprg16(0x8000, tmp1); + setprg16(0xc000, tmp2 + ((tmp2 == 0x6) ? 1 : (latche & 1))); + setchr8(tmp1); + setmirror(((latche >> 4) & 1) ^ 1); +} + +void Mapper204_Init(CartInfo *info) { + Latch_Init(info, M204Sync, NULL, 0xFFFF, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 212 --------------------------- + +static DECLFR(M212Read) { + uint8 ret = CartBROB(A); + if ((A & 0xE010) == 0x6000) + ret |= 0x80; + return ret; +} + +static void M212Sync(void) { + if (latche & 0x4000) { + setprg32(0x8000, (latche >> 1) & 3); + } else { + setprg16(0x8000, latche & 7); + setprg16(0xC000, latche & 7); + } + setchr8(latche & 7); + setmirror(((latche >> 3) & 1) ^ 1); +} + +void Mapper212_Init(CartInfo *info) { + Latch_Init(info, M212Sync, M212Read, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 213 --------------------------- + +static void M213Sync(void) { + if(latche & 0x40) { + setprg16(0x8000, (latche & 7)); + setprg16(0xC000, (latche & 7)); + } else { + setprg32(0x8000, (latche >> 1) & 3); + } + setchr8((latche >> 3) & 7); + setmirror(((latche & 1)^((latche >> 6) & 1)) ^ 1); +} + +void Mapper213_Init(CartInfo *info) { + Latch_Init(info, M213Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 214 --------------------------- + +static void M214Sync(void) { + setprg16(0x8000, (latche >> 2) & 3); + setprg16(0xC000, (latche >> 2) & 3); + setchr8(latche & 3); +} + +void Mapper214_Init(CartInfo *info) { + Latch_Init(info, M214Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 217 --------------------------- + +static void M217Sync(void) { + setprg32(0x8000, (latche >> 2) & 3); + setchr8(latche & 7); +} + +void Mapper217_Init(CartInfo *info) { + Latch_Init(info, M217Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 227 --------------------------- + +static void M227Sync(void) { + uint32 S = latche & 1; + uint32 p = ((latche >> 2) & 0x1F) + ((latche & 0x100) >> 3); + uint32 L = (latche >> 9) & 1; + +// ok, according to nesdev wiki (refrenced to the nesdev dumping thread) there is a CHR write protection bit7. +// however, this bit clearly determined a specific PRG layout for some game but does not meant to have additional +// functionality. as I see from the menu code, it disables the chr writing before run an actual game. +// this fix here makes happy both waixing rpgs and multigame menus at once. can't veryfy it on a hardware +// but if I find some i'll definitly do this. + + if ((latche & 0xF000) == 0xF000) + SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0); + else + SetupCartCHRMapping(0, CHRptr[0], 0x2000, 1); + + if ((latche >> 7) & 1) { + if (S) { + setprg32(0x8000, p >> 1); + } else { + setprg16(0x8000, p); + setprg16(0xC000, p); + } + } else { + if (S) { + if (L) { + setprg16(0x8000, p & 0x3E); + setprg16(0xC000, p | 7); + } else { + setprg16(0x8000, p & 0x3E); + setprg16(0xC000, p & 0x38); + } + } else { + if (L) { + setprg16(0x8000, p); + setprg16(0xC000, p | 7); + } else { + setprg16(0x8000, p); + setprg16(0xC000, p & 0x38); + } + } + } + + setmirror(((latche >> 1) & 1) ^ 1); + setchr8(0); + setprg8r(0x10, 0x6000, 0); +} + +void Mapper227_Init(CartInfo *info) { + Latch_Init(info, M227Sync, NULL, 0x0000, 0x8000, 0xFFFF, 1); +} + +//------------------ Map 229 --------------------------- + +static void M229Sync(void) { + setchr8(latche); + if (!(latche & 0x1e)) + setprg32(0x8000, 0); + else { + setprg16(0x8000, latche & 0x1F); + setprg16(0xC000, latche & 0x1F); + } + setmirror(((latche >> 5) & 1) ^ 1); +} + +void Mapper229_Init(CartInfo *info) { + Latch_Init(info, M229Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 231 --------------------------- + +static void M231Sync(void) { + setchr8(0); + if (latche & 0x20) + setprg32(0x8000, (latche >> 1) & 0x0F); + else { + setprg16(0x8000, latche & 0x1E); + setprg16(0xC000, latche & 0x1E); + } + setmirror(((latche >> 7) & 1) ^ 1); +} + +void Mapper231_Init(CartInfo *info) { + Latch_Init(info, M231Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//------------------ Map 242 --------------------------- + +static void M242Sync(void) { + setchr8(0); + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, (latche >> 3) & 0xf); + setmirror(((latche >> 1) & 1) ^ 1); +} + +void Mapper242_Init(CartInfo *info) { + Latch_Init(info, M242Sync, NULL, 0x0000, 0x8000, 0xFFFF, 1); +} + +//------------------ 190in1 --------------------------- + +static void BMC190in1Sync(void) { + setprg16(0x8000, (latche >> 2) & 7); + setprg16(0xC000, (latche >> 2) & 7); + setchr8((latche >> 2) & 7); + setmirror((latche & 1) ^ 1); +} + +void BMC190in1_Init(CartInfo *info) { + Latch_Init(info, BMC190in1Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//-------------- BMC810544-C-A1 ------------------------ + +static void BMC810544CA1Sync(void) { + uint32 bank = latche >> 7; + if (latche & 0x40) + setprg32(0x8000, bank); + else { + setprg16(0x8000, (bank << 1) | ((latche >> 5) & 1)); + setprg16(0xC000, (bank << 1) | ((latche >> 5) & 1)); + } + setchr8(latche & 0x0f); + setmirror(((latche >> 4) & 1) ^ 1); +} + +void BMC810544CA1_Init(CartInfo *info) { + Latch_Init(info, BMC810544CA1Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//-------------- BMCNTD-03 ------------------------ + +static void BMCNTD03Sync(void) { + // 1PPP Pmcc spxx xccc + // 1000 0000 0000 0000 v + // 1001 1100 0000 0100 h + // 1011 1010 1100 0100 + uint32 prg = ((latche >> 10) & 0x1e); + uint32 chr = ((latche & 0x0300) >> 5) | (latche & 7); + if (latche & 0x80) { + setprg16(0x8000, prg | ((latche >> 6) & 1)); + setprg16(0xC000, prg | ((latche >> 6) & 1)); + } else + setprg32(0x8000, prg >> 1); + setchr8(chr); + setmirror(((latche >> 10) & 1) ^ 1); +} + +void BMCNTD03_Init(CartInfo *info) { + Latch_Init(info, BMCNTD03Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} + +//-------------- BMCG-146 ------------------------ + +static void BMCG146Sync(void) { + setchr8(0); + if (latche & 0x800) { // UNROM mode + setprg16(0x8000, (latche & 0x1F) | (latche & ((latche & 0x40) >> 6))); + setprg16(0xC000, (latche & 0x18) | 7); + } else { + if (latche & 0x40) { // 16K mode + setprg16(0x8000, latche & 0x1F); + setprg16(0xC000, latche & 0x1F); + } else { + setprg32(0x8000, (latche >> 1) & 0x0F); + } + } + setmirror(((latche & 0x80) >> 7) ^ 1); +} + +void BMCG146_Init(CartInfo *info) { + Latch_Init(info, BMCG146Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); +} diff --git a/apps/fceux/src/boards/ax5705.cpp b/apps/fceux/src/boards/ax5705.cpp new file mode 100644 index 00000000..82a4824b --- /dev/null +++ b/apps/fceux/src/boards/ax5705.cpp @@ -0,0 +1,116 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * Super Bros. Pocker Mali (VRC4 mapper) + */ + +#include "mapinc.h" + +static uint8 IRQCount; //, IRQPre; +static uint8 IRQa; +static uint8 prg_reg[2]; +static uint8 chr_reg[8]; +static uint8 mirr; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 1, "IRQC" }, + { &IRQa, 1, "IRQA" }, + { prg_reg, 2, "PRG" }, + { chr_reg, 8, "CHR" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +/* +static void UNLAX5705IRQ(void) +{ + if(IRQa) + { + IRQCount++; + if(IRQCount>=238) + { + X6502_IRQBegin(FCEU_IQEXT); +// IRQa=0; + } + } +}*/ + +static void Sync(void) { + setprg8(0x8000, prg_reg[0]); + setprg8(0xA000, prg_reg[1]); + setprg8(0xC000, ~1); + setprg8(0xE000, ~0); + int i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chr_reg[i]); + setmirror(mirr ^ 1); +} + +static DECLFW(UNLAX5705Write) { +// if((A>=0xA008)&&(A<=0xE003)) +// { +// int ind=(((A>>11)-6)|(A&1))&7; +// int sar=((A&2)<<1); +// chr_reg[ind]=(chr_reg[ind]&(0xF0>>sar))|((V&0x0F)<> 2) | (V & 5); break; // EPROM dump have mixed PRG and CHR banks, data lines to mapper seems to be mixed + case 0x8008: mirr = V & 1; break; + case 0xA000: prg_reg[1] = ((V & 2) << 2) | ((V & 8) >> 2) | (V & 5); break; + case 0xA008: chr_reg[0] = (chr_reg[0] & 0xF0) | (V & 0x0F); break; + case 0xA009: chr_reg[0] = (chr_reg[0] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xA00A: chr_reg[1] = (chr_reg[1] & 0xF0) | (V & 0x0F); break; + case 0xA00B: chr_reg[1] = (chr_reg[1] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xC000: chr_reg[2] = (chr_reg[2] & 0xF0) | (V & 0x0F); break; + case 0xC001: chr_reg[2] = (chr_reg[2] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xC002: chr_reg[3] = (chr_reg[3] & 0xF0) | (V & 0x0F); break; + case 0xC003: chr_reg[3] = (chr_reg[3] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xC008: chr_reg[4] = (chr_reg[4] & 0xF0) | (V & 0x0F); break; + case 0xC009: chr_reg[4] = (chr_reg[4] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xC00A: chr_reg[5] = (chr_reg[5] & 0xF0) | (V & 0x0F); break; + case 0xC00B: chr_reg[5] = (chr_reg[5] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xE000: chr_reg[6] = (chr_reg[6] & 0xF0) | (V & 0x0F); break; + case 0xE001: chr_reg[6] = (chr_reg[6] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; + case 0xE002: chr_reg[7] = (chr_reg[7] & 0xF0) | (V & 0x0F); break; + case 0xE003: chr_reg[7] = (chr_reg[7] & 0x0F) | ((((V & 4) >> 1) | ((V & 2) << 1) | (V & 0x09)) << 4); break; +// case 0x800A: X6502_IRQEnd(FCEU_IQEXT); IRQa=0; break; +// case 0xE00B: X6502_IRQEnd(FCEU_IQEXT); IRQa=IRQCount=V; /*if(scanline<240) IRQCount-=8; else IRQCount+=4;*/ break; + } + Sync(); +} + +static void UNLAX5705Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLAX5705Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLAX5705_Init(CartInfo *info) { + info->Power = UNLAX5705Power; +// GameHBIRQHook=UNLAX5705IRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/bandai.cpp b/apps/fceux/src/boards/bandai.cpp new file mode 100644 index 00000000..24043a99 --- /dev/null +++ b/apps/fceux/src/boards/bandai.cpp @@ -0,0 +1,465 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * Copyright (C) 2011 FCEUX team + * + * 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 + * + * Bandai mappers + * + */ + +#include "mapinc.h" + +static uint8 reg[16], is153, x24c02; +static uint8 IRQa; +static int16 IRQCount, IRQLatch; + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { reg, 16, "REGS" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 2, "IRQL" }, // need for Famicom Jump II - Saikyou no 7 Nin (J) [!] + { 0 } +}; + +// x24C0x interface + +#define X24C0X_STANDBY 0 +#define X24C0X_ADDRESS 1 +#define X24C0X_WORD 2 +#define X24C0X_READ 3 +#define X24C0X_WRITE 4 + +static uint8 x24c0x_data[256], x24c0x_state; +static uint8 x24c0x_addr, x24c0x_word, x24c0x_latch, x24c0x_bitcount; +static uint8 x24c0x_sda, x24c0x_scl, x24c0x_out, x24c0x_oe; + +static SFORMAT x24c0xStateRegs[] = +{ + { &x24c0x_addr, 1, "ADDR" }, + { &x24c0x_word, 1, "WORD" }, + { &x24c0x_latch, 1, "LATC" }, + { &x24c0x_bitcount, 1, "BITC" }, + { &x24c0x_sda, 1, "SDA" }, + { &x24c0x_scl, 1, "SCL" }, + { &x24c0x_out, 1, "OUT" }, + { &x24c0x_oe, 1, "OE" }, + { &x24c0x_state, 1, "STAT" }, + { 0 } +}; + +static void x24c0x_init() { + x24c0x_addr = x24c0x_word = x24c0x_latch = x24c0x_bitcount = x24c0x_sda = x24c0x_scl = x24c0x_oe = 0; + x24c0x_state = X24C0X_STANDBY; +} + +static void x24c0x_write(uint8 data) { + uint8 sda = (data >> 6) & 1; + uint8 scl = (data >> 5) & 1; + x24c0x_oe = (data >> 7); + + if(x24c0x_scl && scl) { + if(x24c0x_sda && !sda) { // START + x24c0x_state = X24C0X_ADDRESS; + x24c0x_bitcount = 0; + x24c0x_addr = 0; + } else if(!x24c0x_sda && sda) { //STOP + x24c0x_state = X24C0X_STANDBY; + } + } else if(!x24c0x_scl && scl) { // RISING EDGE + switch(x24c0x_state) { + case X24C0X_ADDRESS: + if(x24c0x_bitcount < 7) { + x24c0x_addr <<= 1; + x24c0x_addr |= sda; + } else { + if(!x24c02) // X24C01 mode + x24c0x_word = x24c0x_addr; + if(sda) { // READ COMMAND + x24c0x_state = X24C0X_READ; + } else { // WRITE COMMAND + if(x24c02) // X24C02 mode + x24c0x_state = X24C0X_WORD; + else + x24c0x_state = X24C0X_WRITE; + } + } + x24c0x_bitcount++; + break; + case X24C0X_WORD: + if(x24c0x_bitcount == 8) { // ACK + x24c0x_word = 0; + x24c0x_out = 0; + } else { // WORD ADDRESS INPUT + x24c0x_word <<= 1; + x24c0x_word |= sda; + if(x24c0x_bitcount == 16) { // END OF ADDRESS INPUT + x24c0x_bitcount = 7; + x24c0x_state = X24C0X_WRITE; + } + } + x24c0x_bitcount++; + break; + case X24C0X_READ: + if (x24c0x_bitcount == 8) { // ACK + x24c0x_out = 0; + x24c0x_latch = x24c0x_data[x24c0x_word]; + x24c0x_bitcount = 0; + } else { // REAL OUTPUT + x24c0x_out = x24c0x_latch >> 7; + x24c0x_latch <<= 1; + x24c0x_bitcount++; + if(x24c0x_bitcount == 8) { + x24c0x_word++; + x24c0x_word &= 0xff; + } + } + break; + case X24C0X_WRITE: + if (x24c0x_bitcount == 8) { // ACK + x24c0x_out = 0; + x24c0x_latch = 0; + x24c0x_bitcount = 0; + } else { // REAL INPUT + x24c0x_latch <<= 1; + x24c0x_latch |= sda; + x24c0x_bitcount++; + if(x24c0x_bitcount == 8) { + x24c0x_data[x24c0x_word] = x24c0x_latch; + x24c0x_word++; + x24c0x_word &= 0xff; + } + } + break; + } + } + + x24c0x_sda = sda; + x24c0x_scl = scl; +} + +static uint8 x24c0x_read() { + return x24c0x_out << 4; +} + +// + +static void Sync(void) { + if (is153) { + int base = (reg[0] & 1) << 4; + setchr8(0); + setprg16(0x8000, (reg[8] & 0x0F) | base); + setprg16(0xC000, 0x0F | base); + } else { + int i; + for (i = 0; i < 8; i++) setchr1(i << 10, reg[i]); + setprg16(0x8000, reg[8]); + setprg16(0xC000, ~0); + } + switch (reg[9] & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(BandaiWrite) { + A &= 0x0F; + if (A < 0x0A) { + reg[A & 0x0F] = V; + Sync(); + } else + switch (A) { + case 0x0A: X6502_IRQEnd(FCEU_IQEXT); IRQa = V & 1; IRQCount = IRQLatch; break; + case 0x0B: IRQLatch &= 0xFF00; IRQLatch |= V; break; + case 0x0C: IRQLatch &= 0xFF; IRQLatch |= V << 8; break; + case 0x0D: x24c0x_write(V); break; + } +} + +static DECLFR(BandaiRead) { + return (X.DB & 0xEF) | x24c0x_read(); +} + +static void BandaiIRQHook(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount < 0) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + IRQCount = -1; + } + } +} + +static void BandaiPower(void) { + IRQa = 0; + x24c0x_init(); + Sync(); + SetReadHandler(0x6000, 0x7FFF, BandaiRead); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0xFFFF, BandaiWrite); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper16_Init(CartInfo *info) { + x24c02 = 1; + is153 = 0; + info->Power = BandaiPower; + MapIRQHook = BandaiIRQHook; + + info->battery = 1; + info->SaveGame[0] = x24c0x_data; + info->SaveGameLen[0] = 256; + AddExState(x24c0x_data, 256, 0, "DATA"); + + GameStateRestore = StateRestore; + AddExState(&x24c0xStateRegs, ~0, 0, 0); + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper159_Init(CartInfo *info) { + x24c02 = 0; + is153 = 0; + info->Power = BandaiPower; + MapIRQHook = BandaiIRQHook; + + info->battery = 1; + info->SaveGame[0] = x24c0x_data; + info->SaveGameLen[0] = 128; + AddExState(x24c0x_data, 128, 0, "DATA"); + + GameStateRestore = StateRestore; + AddExState(&x24c0xStateRegs, ~0, 0, 0); + AddExState(&StateRegs, ~0, 0, 0); +} + +// Famicom jump 2: +// 0-7: Lower bit of data selects which 256KB PRG block is in use. +// This seems to be a hack on the developers' part, so I'll make emulation +// of it a hack(I think the current PRG block would depend on whatever the +// lowest bit of the CHR bank switching register that corresponds to the +// last CHR address read). + +static void M153Power(void) { + Sync(); + setprg8r(0x10, 0x6000, 0); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, BandaiWrite); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + + +static void M153Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void Mapper153_Init(CartInfo *info) { + is153 = 1; + info->Power = M153Power; + info->Close = M153Close; + MapIRQHook = BandaiIRQHook; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +// Datach Barcode Battler + +static uint8 BarcodeData[256]; +static int BarcodeReadPos; +static int BarcodeCycleCount; +static uint32 BarcodeOut; + +int FCEUI_DatachSet(const uint8 *rcode) { + int prefix_parity_type[10][6] = { + { 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 1, 1 }, { 0, 0, 1, 1, 0, 1 }, { 0, 0, 1, 1, 1, 0 }, + { 0, 1, 0, 0, 1, 1 }, { 0, 1, 1, 0, 0, 1 }, { 0, 1, 1, 1, 0, 0 }, { 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, 1, 0 }, { 0, 1, 1, 0, 1, 0 } + }; + int data_left_odd[10][7] = { + { 0, 0, 0, 1, 1, 0, 1 }, { 0, 0, 1, 1, 0, 0, 1 }, { 0, 0, 1, 0, 0, 1, 1 }, { 0, 1, 1, 1, 1, 0, 1 }, + { 0, 1, 0, 0, 0, 1, 1 }, { 0, 1, 1, 0, 0, 0, 1 }, { 0, 1, 0, 1, 1, 1, 1 }, { 0, 1, 1, 1, 0, 1, 1 }, + { 0, 1, 1, 0, 1, 1, 1 }, { 0, 0, 0, 1, 0, 1, 1 } + }; + int data_left_even[10][7] = { + { 0, 1, 0, 0, 1, 1, 1 }, { 0, 1, 1, 0, 0, 1, 1 }, { 0, 0, 1, 1, 0, 1, 1 }, { 0, 1, 0, 0, 0, 0, 1 }, + { 0, 0, 1, 1, 1, 0, 1 }, { 0, 1, 1, 1, 0, 0, 1 }, { 0, 0, 0, 0, 1, 0, 1 }, { 0, 0, 1, 0, 0, 0, 1 }, + { 0, 0, 0, 1, 0, 0, 1 }, { 0, 0, 1, 0, 1, 1, 1 } + }; + int data_right[10][7] = { + { 1, 1, 1, 0, 0, 1, 0 }, { 1, 1, 0, 0, 1, 1, 0 }, { 1, 1, 0, 1, 1, 0, 0 }, { 1, 0, 0, 0, 0, 1, 0 }, + { 1, 0, 1, 1, 1, 0, 0 }, { 1, 0, 0, 1, 1, 1, 0 }, { 1, 0, 1, 0, 0, 0, 0 }, { 1, 0, 0, 0, 1, 0, 0 }, + { 1, 0, 0, 1, 0, 0, 0 }, { 1, 1, 1, 0, 1, 0, 0 } + }; + uint8 code[13 + 1]; + uint32 tmp_p = 0; + int i, j; + int len; + + for (i = len = 0; i < 13; i++) { + if (!rcode[i]) break; + if ((code[i] = rcode[i] - '0') > 9) + return(0); + len++; + } + if (len != 13 && len != 12 && len != 8 && len != 7) return(0); + + #define BS(x) BarcodeData[tmp_p] = x; tmp_p++ + + for (j = 0; j < 32; j++) { + BS(0x00); + } + + /* Left guard bars */ + BS(1); BS(0); BS(1); + + if (len == 13 || len == 12) { + uint32 csum; + + for (i = 0; i < 6; i++) + if (prefix_parity_type[code[0]][i]) { + for (j = 0; j < 7; j++) { + BS(data_left_even[code[i + 1]][j]); + } + } else + for (j = 0; j < 7; j++) { + BS(data_left_odd[code[i + 1]][j]); + } + + /* Center guard bars */ + BS(0); BS(1); BS(0); BS(1); BS(0); + + for (i = 7; i < 12; i++) + for (j = 0; j < 7; j++) { + BS(data_right[code[i]][j]); + } + csum = 0; + for (i = 0; i < 12; i++) csum += code[i] * ((i & 1) ? 3 : 1); + csum = (10 - (csum % 10)) % 10; + for (j = 0; j < 7; j++) { + BS(data_right[csum][j]); + } + } else if (len == 8 || len == 7) { + uint32 csum = 0; + + for (i = 0; i < 7; i++) csum += (i & 1) ? code[i] : (code[i] * 3); + + csum = (10 - (csum % 10)) % 10; + + for (i = 0; i < 4; i++) + for (j = 0; j < 7; j++) { + BS(data_left_odd[code[i]][j]); + } + + + /* Center guard bars */ + BS(0); BS(1); BS(0); BS(1); BS(0); + + for (i = 4; i < 7; i++) + for (j = 0; j < 7; j++) { + BS(data_right[code[i]][j]); + } + + for (j = 0; j < 7; j++) { + BS(data_right[csum][j]); + } + } + + /* Right guard bars */ + BS(1); BS(0); BS(1); + + for (j = 0; j < 32; j++) { + BS(0x00); + } + + BS(0xFF); + + #undef BS + + BarcodeReadPos = 0; + BarcodeOut = 0x8; + BarcodeCycleCount = 0; + return(1); +} + +static void BarcodeIRQHook(int a) { + BandaiIRQHook(a); + + BarcodeCycleCount += a; + + if (BarcodeCycleCount >= 1000) { + BarcodeCycleCount -= 1000; + if (BarcodeData[BarcodeReadPos] == 0xFF) { + BarcodeOut = 0; + } else { + BarcodeOut = (BarcodeData[BarcodeReadPos] ^ 1) << 3; + BarcodeReadPos++; + } + } +} + +static DECLFR(BarcodeRead) { + return BarcodeOut; +} + +static void M157Power(void) { + IRQa = 0; + BarcodeData[0] = 0xFF; + BarcodeReadPos = 0; + BarcodeOut = 0; + BarcodeCycleCount = 0; + + Sync(); + + SetWriteHandler(0x6000, 0xFFFF, BandaiWrite); + SetReadHandler(0x6000, 0x7FFF, BarcodeRead); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +void Mapper157_Init(CartInfo *info) { + is153 = 1; + info->Power = M157Power; + MapIRQHook = BarcodeIRQHook; + + GameInfo->cspecial = SIS_DATACH; + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/bb.cpp b/apps/fceux/src/boards/bb.cpp new file mode 100644 index 00000000..cdea2c20 --- /dev/null +++ b/apps/fceux/src/boards/bb.cpp @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg, chr; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { &chr, 1, "CHR" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, reg & 3); + setprg32(0x8000, ~0); + setchr8(chr & 3); +} + +static DECLFW(UNLBBWrite) { + if ((A & 0x9000) == 0x8000) + reg = chr = V; + else + chr = V & 1; // hacky hacky, ProWres simplified FDS conversion 2-in-1 mapper + Sync(); +} + +static void UNLBBPower(void) { + chr = 0; + reg = ~0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLBBWrite); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLBB_Init(CartInfo *info) { + info->Power = UNLBBPower; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/bmc13in1jy110.cpp b/apps/fceux/src/boards/bmc13in1jy110.cpp new file mode 100644 index 00000000..f4190451 --- /dev/null +++ b/apps/fceux/src/boards/bmc13in1jy110.cpp @@ -0,0 +1,97 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * BMC 42-in-1 "reset switch" type + */ + +#include "mapinc.h" + +static uint8 bank_mode; +static uint8 bank_value; +static uint8 prgb[4]; +static SFORMAT StateRegs[] = +{ + { &bank_mode, 1, "BNM" }, + { &bank_value, 1, "BMV" }, + { prgb, 4, "PRGB" }, + { 0 } +}; + +static void Sync(void) { +// FCEU_printf("%02x: %02x %02x\n", bank_mode, bank_value, prgb[0]); + switch (bank_mode & 7) { + case 0: + setprg32(0x8000, bank_value & 7); break; + case 1: + setprg16(0x8000, ((8 + (bank_value & 7)) >> 1) + prgb[1]); + setprg16(0xC000, (bank_value & 7) >> 1); + case 4: + setprg32(0x8000, 8 + (bank_value & 7)); break; + case 5: + setprg16(0x8000, ((8 + (bank_value & 7)) >> 1) + prgb[1]); + setprg16(0xC000, ((8 + (bank_value & 7)) >> 1) + prgb[3]); + case 2: + setprg8(0x8000, prgb[0] >> 2); + setprg8(0xa000, prgb[1]); + setprg8(0xc000, prgb[2]); + setprg8(0xe000, ~0); + break; + case 3: + setprg8(0x8000, prgb[0]); + setprg8(0xa000, prgb[1]); + setprg8(0xc000, prgb[2]); + setprg8(0xe000, prgb[3]); + break; + } +} + +static DECLFW(BMC13in1JY110Write) { +// FCEU_printf("%04x:%04x\n",A,V); + switch (A) { + case 0x8000: + case 0x8001: + case 0x8002: + case 0x8003: prgb[A & 3] = V; break; + case 0xD000: bank_mode = V; break; + case 0xD001: setmirror(V & 3); + case 0xD002: break; + case 0xD003: bank_value = V; break; + } + Sync(); +} + +static void BMC13in1JY110Power(void) { + prgb[0] = prgb[1] = prgb[2] = prgb[3] = 0; + bank_mode = 0; + bank_value = 0; + setprg32(0x8000, 0); + setchr8(0); + SetWriteHandler(0x8000, 0xFFFF, BMC13in1JY110Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMC13in1JY110_Init(CartInfo *info) { + info->Power = BMC13in1JY110Power; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/bmc42in1r.cpp b/apps/fceux/src/boards/bmc42in1r.cpp new file mode 100644 index 00000000..06d851ae --- /dev/null +++ b/apps/fceux/src/boards/bmc42in1r.cpp @@ -0,0 +1,86 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * Copyright (C) 2009 qeed + * + * 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 + * + * BMC 42-in-1 "reset switch" + "select switch" + * + */ + +#include "mapinc.h" + +static uint8 isresetbased = 0; +static uint8 latche[2], reset; +static SFORMAT StateRegs[] = +{ + { &reset, 1, "RST" }, + { latche, 2, "LATC" }, + { 0 } +}; + +static void Sync(void) { + uint8 bank; + if (isresetbased) + bank = (latche[0] & 0x1f) | (reset << 5) | ((latche[1] & 1) << 6); + else + bank = (latche[0] & 0x1f) | ((latche[0] & 0x80) >> 2) | ((latche[1] & 1) << 6); + if (!(latche[0] & 0x20)) + setprg32(0x8000, bank >> 1); + else{ + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + setmirror((latche[0] >> 6) & 1); + setchr8(0); +} + +static DECLFW(M226Write) { + latche[A & 1] = V; + Sync(); +} + +static void M226Power(void) { + latche[0] = latche[1] = reset = 0; + Sync(); + SetWriteHandler(0x8000, 0xFFFF, M226Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper226_Init(CartInfo *info) { + isresetbased = 0; + info->Power = M226Power; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} + +static void M233Reset(void) { + reset ^= 1; + Sync(); +} + +void Mapper233_Init(CartInfo *info) { + isresetbased = 1; + info->Power = M226Power; + info->Reset = M233Reset; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/bmc64in1nr.cpp b/apps/fceux/src/boards/bmc64in1nr.cpp new file mode 100644 index 00000000..11102a32 --- /dev/null +++ b/apps/fceux/src/boards/bmc64in1nr.cpp @@ -0,0 +1,83 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * BMC 42-in-1 "reset switch" type + */ + +#include "mapinc.h" + +static uint8 regs[4]; + +static SFORMAT StateRegs[] = +{ + { regs, 4, "REGS" }, + { 0 } +}; + +static void Sync(void) { + if (regs[0] & 0x80) { + if (regs[1] & 0x80) + setprg32(0x8000, regs[1] & 0x1F); + else{ + int bank = ((regs[1] & 0x1f) << 1) | ((regs[1] >> 6) & 1); + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else { + int bank = ((regs[1] & 0x1f) << 1) | ((regs[1] >> 6) & 1); + setprg16(0xC000, bank); + } + if (regs[0] & 0x20) + setmirror(MI_H); + else + setmirror(MI_V); + setchr8((regs[2] << 2) | ((regs[0] >> 1) & 3)); +} + +static DECLFW(BMC64in1nrWriteLo) { + regs[A & 3] = V; + Sync(); +} + +static DECLFW(BMC64in1nrWriteHi) { + regs[3] = V; + Sync(); +} + +static void BMC64in1nrPower(void) { + regs[0] = 0x80; + regs[1] = 0x43; + regs[2] = regs[3] = 0; + Sync(); + SetWriteHandler(0x5000, 0x5003, BMC64in1nrWriteLo); + SetWriteHandler(0x8000, 0xFFFF, BMC64in1nrWriteHi); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMC64in1nr_Init(CartInfo *info) { + info->Power = BMC64in1nrPower; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} + + diff --git a/apps/fceux/src/boards/bmc70in1.cpp b/apps/fceux/src/boards/bmc70in1.cpp new file mode 100644 index 00000000..95c34370 --- /dev/null +++ b/apps/fceux/src/boards/bmc70in1.cpp @@ -0,0 +1,121 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 is_large_banks, hw_switch; +static uint8 large_bank; +static uint8 prg_bank; +static uint8 chr_bank; +static uint8 bank_mode; +static uint8 mirroring; +static SFORMAT StateRegs[] = +{ + { &large_bank, 1, "LB" }, + { &hw_switch, 1, "DPSW" }, + { &prg_bank, 1, "PRG" }, + { &chr_bank, 1, "CHR" }, + { &bank_mode, 1, "BM" }, + { &mirroring, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + switch (bank_mode) { + case 0x00: + case 0x10: + setprg16(0x8000, large_bank | prg_bank); + setprg16(0xC000, large_bank | 7); + break; + case 0x20: + setprg32(0x8000, (large_bank | prg_bank) >> 1); + break; + case 0x30: + setprg16(0x8000, large_bank | prg_bank); + setprg16(0xC000, large_bank | prg_bank); + break; + } + setmirror(mirroring); + if (!is_large_banks) + setchr8(chr_bank); +} + +static DECLFR(BMC70in1Read) { + if (bank_mode == 0x10) +// if(is_large_banks) + return CartBR((A & 0xFFF0) | hw_switch); +// else +// return CartBR((A&0xFFF0)|hw_switch); + else + return CartBR(A); +} + +static DECLFW(BMC70in1Write) { + if (A & 0x4000) { + bank_mode = A & 0x30; + prg_bank = A & 7; + } else { + mirroring = ((A & 0x20) >> 5) ^ 1; + if (is_large_banks) + large_bank = (A & 3) << 3; + else + chr_bank = A & 7; + } + Sync(); +} + +static void BMC70in1Reset(void) { + bank_mode = 0; + large_bank = 0; + Sync(); + hw_switch++; + hw_switch &= 0xf; +} + +static void BMC70in1Power(void) { + setchr8(0); + bank_mode = 0; + large_bank = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, BMC70in1Read); + SetWriteHandler(0x8000, 0xffff, BMC70in1Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMC70in1_Init(CartInfo *info) { + is_large_banks = 0; + hw_switch = 0xd; + info->Power = BMC70in1Power; + info->Reset = BMC70in1Reset; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void BMC70in1B_Init(CartInfo *info) { + is_large_banks = 1; + hw_switch = 0x6; + info->Power = BMC70in1Power; + info->Reset = BMC70in1Reset; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/bonza.cpp b/apps/fceux/src/boards/bonza.cpp new file mode 100644 index 00000000..93b5e045 --- /dev/null +++ b/apps/fceux/src/boards/bonza.cpp @@ -0,0 +1,126 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +#define CARD_EXTERNAL_INSERED 0x80 + +static uint8 prg_reg; +static uint8 chr_reg; +static SFORMAT StateRegs[] = +{ + { &prg_reg, 1, "PREG" }, + { &chr_reg, 1, "CREG" }, + { 0 } +}; + +/* + +cmd[0] = response on/off + 0x00 - on + 0x80 - off +cmd[1] = cmd + + +_GET_CHALLENGE: .BYTE 0,$B4, 0, 0,$62 + +_SELECT_FILE_1_0200: .BYTE 0,$A4, 1, 0, 2, 2, 0 +_SELECT_FILE_2_0201: .BYTE 0,$A4, 2, 0, 2, 2, 1 +_SELECT_FILE_2_0203: .BYTE 0,$A4, 2, 0, 2, 2, 3 +_SELECT_FILE_2_0204: .BYTE 0,$A4, 2, 0, 2, 2, 4 +_SELECT_FILE_2_0205: .BYTE 0,$A4, 2, 0, 2, 2, 5 +_SELECT_FILE_2_3F04: .BYTE 0,$A4, 2, 0, 2,$3F, 4 +_SELECT_FILE_2_4F00: .BYTE 0,$A4, 2, 0, 2,$4F, 0 + +_READ_BINARY_5: .BYTE 0,$B0,$85, 0, 2 +_READ_BINARY_6: .BYTE 0,$B0,$86, 0, 4 +_READ_BINARY_6_0: .BYTE 0,$B0,$86, 0,$18 +_READ_BINARY_0: .BYTE 0,$B0, 0, 2, 3 +_READ_BINARY_0_0: .BYTE 0,$B0, 0, 0, 4 +_READ_BINARY_0_1: .BYTE 0,$B0, 0, 0, $C +_READ_BINARY_0_2: .BYTE 0,$B0, 0, 0,$10 + +_UPDATE_BINARY: .BYTE 0,$D6, 0, 0, 4 +_UPDATE_BINARY_0: .BYTE 0,$D6, 0, 0,$10 + +_GET_RESPONSE: .BYTE $80,$C0, 2,$A1, 8 +_GET_RESPONSE_0: .BYTE 0,$C0, 0, 0, 2 +_GET_RESPONSE_1: .BYTE 0,$C0, 0, 0, 6 +_GET_RESPONSE_2: .BYTE 0,$C0, 0, 0, 8 +_GET_RESPONSE_3: .BYTE 0,$C0, 0, 0, $C +_GET_RESPONSE_4: .BYTE 0,$C0, 0, 0,$10 + +byte_8C0B: .BYTE $80,$30, 0, 2, $A, 0, 1 +byte_8C48: .BYTE $80,$32, 0, 1, 4 +byte_8C89: .BYTE $80,$34, 0, 0, 8, 0, 0 +byte_8D01: .BYTE $80,$36, 0, 0, $C +byte_8CA7: .BYTE $80,$38, 0, 2, 4 +byte_8BEC: .BYTE $80,$3A, 0, 3, 0 + +byte_89A0: .BYTE 0,$48, 0, 0, 6 +byte_8808: .BYTE 0,$54, 0, 0,$1C +byte_89BF: .BYTE 0,$58, 0, 0,$1C + +_MANAGE_CHANNEL: .BYTE 0,$70, 0, 0, 8 +byte_8CE5: .BYTE 0,$74, 0, 0,$12 +byte_8C29: .BYTE 0,$76, 0, 0, 8 +byte_8CC6: .BYTE 0,$78, 0, 0,$12 +*/ + +static void Sync(void) { + setprg32(0x8000, prg_reg); + setchr8(chr_reg); +} + +static void StateRestore(int version) { + Sync(); +} + +static DECLFW(M216WriteHi) { + prg_reg = A & 1; + chr_reg = (A & 0x0E) >> 1; + Sync(); +} + +static DECLFW(M216Write5000) { +// FCEU_printf("WRITE: %04x:%04x (PC=%02x cnt=%02x)\n",A,V,X.PC,sim0bcnt); +} + +static DECLFR(M216Read5000) { +// FCEU_printf("READ: %04x PC=%04x out=%02x byte=%02x cnt=%02x bit=%02x\n",A,X.PC,sim0out,sim0byte,sim0bcnt,sim0bit); + return 0; +} + +static void Power(void) { + prg_reg = 0; + chr_reg = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M216WriteHi); + SetWriteHandler(0x5000, 0x5000, M216Write5000); + SetReadHandler(0x5000, 0x5000, M216Read5000); +} + + +void Mapper216_Init(CartInfo *info) { + info->Power = Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/bs-5.cpp b/apps/fceux/src/boards/bs-5.cpp new file mode 100644 index 00000000..5e2ff1fe --- /dev/null +++ b/apps/fceux/src/boards/bs-5.cpp @@ -0,0 +1,84 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg_prg[4]; +static uint8 reg_chr[4]; +static uint8 dip_switch; + +static SFORMAT StateRegs[] = +{ + { reg_prg, 4, "PREG" }, + { reg_chr, 4, "CREG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x8000, reg_prg[0]); + setprg8(0xa000, reg_prg[1]); + setprg8(0xc000, reg_prg[2]); + setprg8(0xe000, reg_prg[3]); + setchr2(0x0000, reg_chr[0]); + setchr2(0x0800, reg_chr[1]); + setchr2(0x1000, reg_chr[2]); + setchr2(0x1800, reg_chr[3]); + setmirror(MI_V); +} + +static DECLFW(MBS5Write) { + int bank_sel = (A & 0xC00) >> 10; + switch (A & 0xF000) { + case 0x8000: + reg_chr[bank_sel] = A & 0x1F; + break; + case 0xA000: + if (A & (1 << (dip_switch + 4))) + reg_prg[bank_sel] = A & 0x0F; + break; + } + Sync(); +} + +static void MBS5Reset(void) { + dip_switch++; + dip_switch &= 3; + reg_prg[0] = reg_prg[1] = reg_prg[2] = reg_prg[3] = ~0; + Sync(); +} + +static void MBS5Power(void) { + dip_switch = 0; + reg_prg[0] = reg_prg[1] = reg_prg[2] = reg_prg[3] = ~0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, MBS5Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMCBS5_Init(CartInfo *info) { + info->Power = MBS5Power; + info->Reset = MBS5Reset; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/cheapocabra.cpp b/apps/fceux/src/boards/cheapocabra.cpp new file mode 100644 index 00000000..31837815 --- /dev/null +++ b/apps/fceux/src/boards/cheapocabra.cpp @@ -0,0 +1,270 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +// mapper 111 - Cheapocabra board by Memblers +// http://forums.nesdev.com/viewtopic.php?p=146039 +// +// 512k PRG-ROM in 32k pages (flashable if battery backed is specified) +// 32k CHR-RAM used as: +// 2 x 8k pattern pages +// 2 x 8k nametable pages +// +// Notes: +// - CHR-RAM for nametables maps to $3000-3FFF as well, but FCEUX internally mirrors to 4k? + +#include "mapinc.h" +#include "../ines.h" + +static uint8 reg; +static uint8 *CHRRAM = NULL; +const uint32 CHRRAMSIZE = 1024 * 32; + +static bool flash = false; +static uint8 flash_mode; +static uint8 flash_sequence; +static uint8 flash_id; +static uint8 *FLASHROM = NULL; +const uint32 FLASHROMSIZE = 1024 * 512; + + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { 0 } +}; + +static SFORMAT FlashRegs[] = +{ + { &flash_mode, 1, "FMOD" }, + { &flash_sequence, 1, "FSEQ" }, + { &flash_id, 1, "FMID" }, + { 0 } +}; + +static void Sync(void) { + // bit 7 controls green LED + // bit 6 controls red LED + int nt = (reg & 0x20) ? 8192 : 0; // bit 5 controls 8k nametable page + int chr = (reg & 0x10) ? 1 : 0; // bit 4 selects 8k CHR page + int prg = (reg & 0x0F); // bits 0-3 select 32k PRG page + + nt += (16 * 1024); + for (int n=0; n<4; ++n) + { + setntamem(CHRRAM + nt + (1024 * n),1,n); + } + setchr8r(0x10, chr); + + uint32 prg_chip = flash ? 0x10 : 0; + setprg32r(prg_chip,0x8000,prg); +} + +static DECLFW(M111Write) { + if ((A >= 0x5000 && A <= 0x5FFF) || (A >= 0x7000 && A <= 0x7FFF)) + { + reg = V; + Sync(); + } +} + +static DECLFR(M111FlashID) +{ + // Software ID mode is undefined by the datasheet for all but the lowest 2 addressable bytes, + // but some tests of the chip currently being used found it repeats in 512-byte patterns. + // http://forums.nesdev.com/viewtopic.php?p=178728#p178728 + + uint32 aid = A & 0x1FF; + switch (aid) + { + case 0: return 0xBF; + case 1: return 0xB7; + default: return 0xFF; + } +} + +void M111FlashIDEnter() +{ + if (flash_id) return; + flash_id = 1; + SetReadHandler(0x8000,0xFFFF,M111FlashID); +} + +void M111FlashIDExit() +{ + if (!flash_id) return; + flash_id = 0; + SetReadHandler(0x8000,0xFFFF,CartBR); +} + +static DECLFW(M111Flash) { + if (A < 0x8000 || A > 0xFFFF) return; + + uint32 flash_addr = ((reg & 0x0F) << 15) | (A & 0x7FFF); + uint32 command_addr = flash_addr & 0x7FFF; + + enum + { + FLASH_MODE_READY = 0, + FLASH_MODE_COMMAND, + FLASH_MODE_BYTE_WRITE, + FLASH_MODE_ERASE, + }; + + switch (flash_mode) + { + default: + case FLASH_MODE_READY: + if (command_addr == 0x5555 && V == 0xAA) + { + flash_mode = FLASH_MODE_COMMAND; + flash_sequence = 0; + } + else if (V == 0xF0) + { + M111FlashIDExit(); + } + break; + case FLASH_MODE_COMMAND: + if (flash_sequence == 0) + { + if (command_addr == 0x2AAA && V == 0x55) + flash_sequence = 1; + else + flash_mode = FLASH_MODE_READY; + } + else if (flash_sequence == 1) + { + if (command_addr == 0x5555) + { + flash_sequence = 0; + switch (V) + { + default: flash_mode = FLASH_MODE_READY; break; + case 0xA0: flash_mode = FLASH_MODE_BYTE_WRITE; break; + case 0x80: flash_mode = FLASH_MODE_ERASE; break; + case 0x90: M111FlashIDEnter(); flash_mode = FLASH_MODE_READY; break; + case 0xF0: M111FlashIDExit(); flash_mode = FLASH_MODE_READY; break; + } + } + else + flash_mode = FLASH_MODE_READY; + } + else + flash_mode = FLASH_MODE_READY; // should be unreachable + break; + case FLASH_MODE_BYTE_WRITE: + FLASHROM[flash_addr] &= V; + flash_mode = FLASH_MODE_READY; + break; + case FLASH_MODE_ERASE: + if (flash_sequence == 0) + { + if (command_addr == 0x5555 && V == 0xAA) + flash_sequence = 1; + else + flash_mode = FLASH_MODE_READY; + } + else if (flash_sequence == 1) + { + if (command_addr == 0x2AAA && V == 0x55) + flash_sequence = 2; + else + flash_mode = FLASH_MODE_READY; + } + else if (flash_sequence == 2) + { + if (command_addr == 0x5555 && V == 0x10) // erase chip + { + memset(FLASHROM, 0xFF, FLASHROMSIZE); + } + else if (V == 0x30) // erase 4k sector + { + uint32 sector = flash_addr & 0x7F000; + memset(FLASHROM + sector, 0xFF, 1024 * 4); + } + flash_mode = FLASH_MODE_READY; + } + else + flash_mode = FLASH_MODE_READY; // should be unreachable + break; + } +} + +static void M111Power(void) { + reg = 0xFF; + Sync(); + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x5000, 0x5fff, M111Write); + SetWriteHandler(0x7000, 0x7fff, M111Write); + + if (flash) + { + flash_mode = 0; + flash_sequence = 0; + flash_id = false; + SetWriteHandler(0x8000, 0xFFFF, M111Flash); + } +} + +static void M111Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; + + if (FLASHROM) + FCEU_gfree(FLASHROM); + FLASHROM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper111_Init(CartInfo *info) { + info->Power = M111Power; + info->Close = M111Close; + + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); + + flash = (info->battery != 0); + if (flash) + { + FLASHROM = (uint8*)FCEU_gmalloc(FLASHROMSIZE); + info->SaveGame[0] = FLASHROM; + info->SaveGameLen[0] = FLASHROMSIZE; + AddExState(FLASHROM, FLASHROMSIZE, 0, "FROM"); + AddExState(&FlashRegs, ~0, 0, 0); + + // copy PRG ROM into FLASHROM, use it instead of PRG ROM + const uint32 PRGSIZE = ROM_size * 16 * 1024; + for (uint32 w=0, r=0; w= PRGSIZE) r = 0; + } + SetupCartPRGMapping(0x10, FLASHROM, FLASHROMSIZE, 0); + } +} diff --git a/apps/fceux/src/boards/cityfighter.cpp b/apps/fceux/src/boards/cityfighter.cpp new file mode 100644 index 00000000..32149596 --- /dev/null +++ b/apps/fceux/src/boards/cityfighter.cpp @@ -0,0 +1,124 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * City Fighter IV sith Sound VRC4 hacked + */ + +#include "mapinc.h" + +static int32 IRQCount; +static uint8 IRQa; +static uint8 prg_reg, prg_mode, mirr; +static uint8 chr_reg[8]; +static writefunc pcmwrite; + +static SFORMAT StateRegs[] = +{ + { &IRQCount, 4, "IRQC" }, + { &IRQa, 1, "IRQA" }, + { &prg_reg, 1, "PREG" }, + { &prg_mode, 1, "PMOD" }, + { &mirr, 1, "MIRR" }, + { chr_reg, 8, "CREG" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, prg_reg >> 2); + if (!prg_mode) + setprg8(0xC000, prg_reg); + int i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chr_reg[i]); + switch (mirr) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(UNLCITYFIGHTWrite) { + //FCEU_printf("%04x %02x",A,V); + switch (A & 0xF00C) { + case 0x9000: prg_reg = V & 0xC; mirr = V & 3; break; + case 0x9004: + case 0x9008: + case 0x900C: + if (A & 0x800) + pcmwrite(0x4011, (V & 0xf) << 3); + else + prg_reg = V & 0xC; + break; + case 0xC000: + case 0xC004: + case 0xC008: + case 0xC00C: prg_mode = V & 1; break; + case 0xD000: chr_reg[0] = (chr_reg[0] & 0xF0) | (V & 0x0F); break; + case 0xD004: chr_reg[0] = (chr_reg[0] & 0x0F) | (V << 4); break; + case 0xD008: chr_reg[1] = (chr_reg[1] & 0xF0) | (V & 0x0F); break; + case 0xD00C: chr_reg[1] = (chr_reg[1] & 0x0F) | (V << 4); break; + case 0xA000: chr_reg[2] = (chr_reg[2] & 0xF0) | (V & 0x0F); break; + case 0xA004: chr_reg[2] = (chr_reg[2] & 0x0F) | (V << 4); break; + case 0xA008: chr_reg[3] = (chr_reg[3] & 0xF0) | (V & 0x0F); break; + case 0xA00C: chr_reg[3] = (chr_reg[3] & 0x0F) | (V << 4); break; + case 0xB000: chr_reg[4] = (chr_reg[4] & 0xF0) | (V & 0x0F); break; + case 0xB004: chr_reg[4] = (chr_reg[4] & 0x0F) | (V << 4); break; + case 0xB008: chr_reg[5] = (chr_reg[5] & 0xF0) | (V & 0x0F); break; + case 0xB00C: chr_reg[5] = (chr_reg[5] & 0x0F) | (V << 4); break; + case 0xE000: chr_reg[6] = (chr_reg[6] & 0xF0) | (V & 0x0F); break; + case 0xE004: chr_reg[6] = (chr_reg[6] & 0x0F) | (V << 4); break; + case 0xE008: chr_reg[7] = (chr_reg[7] & 0xF0) | (V & 0x0F); break; + case 0xE00C: chr_reg[7] = (chr_reg[7] & 0x0F) | (V << 4); break; + case 0xF000: IRQCount = ((IRQCount & 0x1E0) | ((V & 0xF) << 1)); break; + case 0xF004: IRQCount = ((IRQCount & 0x1E) | ((V & 0xF) << 5)); break; + case 0xF008: IRQa = V & 2; X6502_IRQEnd(FCEU_IQEXT); break; + default: + break; + } + Sync(); +} + +static void UNLCITYFIGHTIRQ(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount <= 0) { + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void UNLCITYFIGHTPower(void) { + prg_reg = 0; + Sync(); + pcmwrite = GetWriteHandler(0x4011); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLCITYFIGHTWrite); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLCITYFIGHT_Init(CartInfo *info) { + info->Power = UNLCITYFIGHTPower; + MapIRQHook = UNLCITYFIGHTIRQ; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/coolboy.cpp b/apps/fceux/src/boards/coolboy.cpp new file mode 100644 index 00000000..7b050fe2 --- /dev/null +++ b/apps/fceux/src/boards/coolboy.cpp @@ -0,0 +1,196 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2018 CaH4e3, Cluster + * + * 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 + * + * CoolBoy 400-in-1 FK23C-mimic mapper 16Mb/32Mb PROM + 128K/256K CHR RAM, optional SRAM, optional battery + * only MMC3 mode + * + * $6000 + * 7 bit 0 + * ---- ---- + * ABCC DEEE + * |||| |||| + * |||| |+++-- PRG offset (PRG A19, A18, A17) + * |||| +----- Alternate CHR A17 + * ||++------- PRG offset (PRG A24, A23) + * |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) + * +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) + * + * $6001 + * + * 7 bit 0 + * ---- ---- + * GHIJ KKLx + * |||| ||| + * |||| ||+--- GNROM mode bank PRG size (1: 32 KiB bank, PRG A14=CPU A14; 0: 16 KiB bank, PRG A14=offset A14) + * |||+-++---- PRG offset (in order: PRG A20, A22, A21) + * ||+-------- PRG mask (PRG A20 from 0: offset; 1: MMC3) + * |+--------- PRG mask (PRG A19 from 0: offset; 1: MMC3) + * +---------- PRG mask (PRG A18 from 0: MMC3; 1: offset) + * + * $6002 + * 7 bit 0 + * ---- ---- + * xxxx MMMM + * |||| + * ++++-- CHR offset for GNROM mode (CHR A16, A15, A14, A13) + * + * $6003 + * 7 bit 0 + * ---- ---- + * NPxP QQRx + * || | ||| + * || | +++--- PRG offset for GNROM mode (PRG A16, A15, A14) + * || +------- 1: GNROM mode; 0: MMC3 mode + * || | (1: PRG A16...13 from QQ, L, R, CPU A14, A13 + CHR A16...10 from MMMM, PPU A12...10; + * || | 0: PRG A16...13 from MMC3 + CHR A16...A10 from MMC3 ) + * |+-+------- Banking mode + * |+--------- "Weird MMC3 mode" + * +---------- Lockout (prevent further writes to these four registers, only works in MMC3 mode) + * + * There is also alternative version from MINDKIDS, + * the only difference is register addresses - 500x instead of 600x + * + * Also some new cartridges from MINDKIDS have /WE and /OE pins connected to mapper, + * which allows you to rewrite flash memory without soldering. + * This also allows console to write data to the cartridge. + * This behavior is not emulated. + * No cart has been discovered so far that makes use of this feature, but this can be used for homebrew. + * + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void COOLBOYCW(uint32 A, uint8 V) { + uint32 mask = 0xFF ^ (EXPREGS[0] & 0x80); + if (EXPREGS[3] & 0x10) { + if (EXPREGS[3] & 0x40) { // Weird mode + int cbase = (MMC3_cmd & 0x80) << 5; + switch (cbase ^ A) { // Don't even try do understand + case 0x0400: + case 0x0C00: V &= 0x7F; break; + } + } + // Highest bit goes from MMC3 registers when EXPREGS[3]&0x80==0 or from EXPREGS[0]&0x08 otherwise + setchr1(A, + (V & 0x80 & mask) | ((((EXPREGS[0] & 0x08) << 4) & ~mask)) // 7th bit + | ((EXPREGS[2] & 0x0F) << 3) // 6-3 bits + | ((A >> 10) & 7) // 2-0 bits + ); + } else { + if (EXPREGS[3] & 0x40) { // Weird mode, again + int cbase = (MMC3_cmd & 0x80) << 5; + switch (cbase ^ A) { // Don't even try do understand + case 0x0000: V = DRegBuf[0]; break; + case 0x0800: V = DRegBuf[1]; break; + case 0x0400: + case 0x0C00: V = 0; break; + } + } + // Simple MMC3 mode + // Highest bit goes from MMC3 registers when EXPREGS[3]&0x80==0 or from EXPREGS[0]&0x08 otherwise + setchr1(A, (V & mask) | (((EXPREGS[0] & 0x08) << 4) & ~mask)); + } +} + +static void COOLBOYPW(uint32 A, uint8 V) { + uint32 mask = ((0x3F | (EXPREGS[1] & 0x40) | ((EXPREGS[1] & 0x20) << 2)) ^ ((EXPREGS[0] & 0x40) >> 2)) ^ ((EXPREGS[1] & 0x80) >> 2); + uint32 base = ((EXPREGS[0] & 0x07) >> 0) | ((EXPREGS[1] & 0x10) >> 1) | ((EXPREGS[1] & 0x0C) << 2) | ((EXPREGS[0] & 0x30) << 2); + + // Very weird mode + // Last banks are first in this mode, ignored when MMC3_cmd&0x40 + if ((EXPREGS[3] & 0x40) && (V >= 0xFE) && !((MMC3_cmd & 0x40) != 0)) { + switch (A & 0xE000) { + case 0xC000: + case 0xE000: + V = 0; + break; + } + } + + // Regular MMC3 mode, internal ROM size can be up to 2048kb! + if (!(EXPREGS[3] & 0x10)) + setprg8(A, (((base << 4) & ~mask)) | (V & mask)); + else { // NROM mode + mask &= 0xF0; + uint8 emask; + if ((((EXPREGS[1] & 2) != 0))) // 32kb mode + emask = (EXPREGS[3] & 0x0C) | ((A & 0x4000) >> 13); + else // 16kb mode + emask = EXPREGS[3] & 0x0E; + setprg8(A, ((base << 4) & ~mask) // 7-4 bits are from base (see below) + | (V & mask) // ... or from MM3 internal regs, depends on mask + | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] + | ((A & 0x2000) >> 13)); // 0th just as is + } +} + +static DECLFW(COOLBOYWrite) { + if(A001B & 0x80) + CartBW(A,V); + + // Deny any further writes when 7th bit is 1 AND 4th is 0 + if ((EXPREGS[3] & 0x90) != 0x80) { + EXPREGS[A & 3] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +} + +static void COOLBOYReset(void) { + MMC3RegReset(); + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void COOLBOYPower(void) { + GenMMC3Power(); + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + SetWriteHandler(0x5000, 0x5fff, CartBW); // some games access random unmapped areas and crashes because of KT-008 PCB hack in MMC3 source lol + SetWriteHandler(0x6000, 0x6fff, COOLBOYWrite); +} + +static void MINDKIDSPower(void) { + GenMMC3Power(); + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + SetWriteHandler(0x5000, 0x5fff, COOLBOYWrite); +} + +void COOLBOY_Init(CartInfo *info) { + GenMMC3_Init(info, 2048, 256, 8, 1); + pwrap = COOLBOYPW; + cwrap = COOLBOYCW; + info->Power = COOLBOYPower; + info->Reset = COOLBOYReset; + AddExState(EXPREGS, 4, 0, "EXPR"); +} + +void MINDKIDS_Init(CartInfo *info) { + GenMMC3_Init(info, 2048, 256, 8, 1); + pwrap = COOLBOYPW; + cwrap = COOLBOYCW; + info->Power = MINDKIDSPower; + info->Reset = COOLBOYReset; + AddExState(EXPREGS, 4, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/dance2000.cpp b/apps/fceux/src/boards/dance2000.cpp new file mode 100644 index 00000000..37a6e1ea --- /dev/null +++ b/apps/fceux/src/boards/dance2000.cpp @@ -0,0 +1,110 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 prg, mode; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static uint32 lastnt = 0; + +static SFORMAT StateRegs[] = +{ + { &prg, 1, "REGS" }, + { &mode, 1, "MODE" }, + { &lastnt, 4, "LSNT" }, + { 0 } +}; + +static void Sync(void) { + setmirror((mode ^ 1) & 1); + setprg8r(0x10, 0x6000, 0); + setchr4(0x0000, lastnt); + setchr4(0x1000, 1); + if (mode & 4) + setprg32(0x8000, prg & 7); + else { + setprg16(0x8000, prg & 0x0f); + setprg16(0xC000, 0); + } +} + +static DECLFW(UNLD2000Write) { + switch (A) { + case 0x5000: prg = V; Sync(); break; + case 0x5200: mode = V; if (mode & 4) Sync(); break; + } +} + +static DECLFR(UNLD2000Read) { + if (prg & 0x40) + return X.DB; + else + return CartBR(A); +} + +static void UNLD2000Power(void) { + prg = mode = 0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, UNLD2000Read); + SetWriteHandler(0x5000, 0x5FFF, UNLD2000Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void UNL2000Hook(uint32 A) { + if (mode & 2) { + if ((A & 0x3000) == 0x2000) { + uint32 curnt = A & 0x800; + if (curnt != lastnt) { + setchr4(0x0000, curnt >> 11); + lastnt = curnt; + } + } + } else { + lastnt = 0; + setchr4(0x0000, 0); + } +} + +static void UNLD2000Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLD2000_Init(CartInfo *info) { + info->Power = UNLD2000Power; + info->Close = UNLD2000Close; + PPU_hook = UNL2000Hook; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/datalatch.cpp b/apps/fceux/src/boards/datalatch.cpp new file mode 100644 index 00000000..c9e9c985 --- /dev/null +++ b/apps/fceux/src/boards/datalatch.cpp @@ -0,0 +1,543 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" +#include "../ines.h" + +static uint8 latche, latcheinit, bus_conflict; +static uint16 addrreg0, addrreg1; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static void (*WSync)(void); + +static DECLFW(LatchWrite) { +// FCEU_printf("bs %04x %02x\n",A,V); + if (bus_conflict) + latche = V & CartBR(A); + else + latche = V; + WSync(); +} + +static void LatchPower(void) { + latche = latcheinit; + WSync(); + if (WRAM) { + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + } else { + SetReadHandler(0x8000, 0xFFFF, CartBR); + } + SetWriteHandler(addrreg0, addrreg1, LatchWrite); +} + +static void LatchClose(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + WSync(); +} + +static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 adr0, uint16 adr1, uint8 wram, uint8 busc) { + bus_conflict = busc; + latcheinit = init; + addrreg0 = adr0; + addrreg1 = adr1; + WSync = proc; + info->Power = LatchPower; + info->Close = LatchClose; + GameStateRestore = StateRestore; + if(info->ines2) + if(info->battery_wram_size + info->wram_size > 0) + wram = 1; + if (wram) + { + if(info->ines2) + { + //I would like to do it in this way, but FCEUX is woefully inadequate + //for instance if WRAMSIZE is large, the cheat pointers may get overwritten. and it's just a giant mess. + //WRAMSIZE = info->battery_wram_size + info->wram_size; + //WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + //if(!info->wram_size && !info->battery_wram_size) {} + //else if(info->wram_size && !info->battery_wram_size) + // SetupCartPRGMapping(0x10, WRAM, info->wram_size, 1); + //else if(!info->wram_size && info->battery_wram_size) + //{ + // SetupCartPRGMapping(0x10, WRAM, info->battery_wram_size, 1); + // info->SaveGame[0] = WRAM; + // info->SaveGameLen[0] = info->battery_wram_size; + //} else { + // //well, this is annoying + // SetupCartPRGMapping(0x10, WRAM, info->wram_size, 1); + // SetupCartPRGMapping(0x11, WRAM, info->battery_wram_size, 1); //? ? ? there probably isnt even a way to select this + // info->SaveGame[0] = WRAM + info->wram_size; + // info->SaveGameLen[0] = info->battery_wram_size; + //} + + //this is more likely the only practical scenario + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + setprg8r(0x10, 0x6000, 0); + if(info->battery_wram_size) + { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = 8192; + } + } + else + { + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + } + + } + AddExState(&latche, 1, 0, "LATC"); +} + +//------------------ Map 0 --------------------------- + +#ifdef DEBUG_MAPPER +static DECLFW(NROMWrite) { + FCEU_printf("bs %04x %02x\n", A, V); + CartBW(A, V); +} +#endif + +static void NROMPower(void) { + setprg8r(0x10, 0x6000, 0); // Famili BASIC (v3.0) need it (uses only 4KB), FP-BASIC uses 8KB + setprg16(0x8000, ~1); + setprg16(0xC000, ~0); + setchr8(0); + + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + +#ifdef DEBUG_MAPPER + SetWriteHandler(0x4020, 0xFFFF, NROMWrite); + #endif +} + +void NROM_Init(CartInfo *info) { + info->Power = NROMPower; + info->Close = LatchClose; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); +} + +//------------------ Map 2 --------------------------- + +static void UNROMSync(void) { +// static uint32 mirror_in_use = 0; +// if (PRGsize[0] <= 128 * 1024) { +// setprg16(0x8000, latche & 0x7); +// if (latche & 8) mirror_in_use = 1; +// if (mirror_in_use) +// setmirror(((latche >> 3) & 1) ^ 1); // Higway Star Hacked mapper, disabled till new mapper defined +// } else + setprg16(0x8000, latche); + setprg16(0xc000, ~0); + setchr8(0); +} + +void UNROM_Init(CartInfo *info) { + Latch_Init(info, UNROMSync, 0, 0x8000, 0xFFFF, 0, 1); +} + +//------------------ Map 3 --------------------------- + +static void CNROMSync(void) { + setchr8(latche); + setprg32(0x8000, 0); + setprg8r(0x10, 0x6000, 0); // Hayauchy IGO uses 2Kb or RAM +} + +void CNROM_Init(CartInfo *info) { + Latch_Init(info, CNROMSync, 0, 0x8000, 0xFFFF, 1, 1); +} + +//------------------ Map 7 --------------------------- + +static void ANROMSync() { + setprg32(0x8000, latche & 0xF); + setmirror(MI_0 + ((latche >> 4) & 1)); + setchr8(0); +} + +void ANROM_Init(CartInfo *info) { + Latch_Init(info, ANROMSync, 0, 0x4020, 0xFFFF, 0, 0); +} + +//------------------ Map 8 --------------------------- + +static void M8Sync() { + setprg16(0x8000, latche >> 3); + setprg16(0xc000, 1); + setchr8(latche & 3); +} + +void Mapper8_Init(CartInfo *info) { + Latch_Init(info, M8Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 11 --------------------------- + +static void M11Sync(void) { + setprg32(0x8000, latche & 0xF); + setchr8(latche >> 4); +} + +void Mapper11_Init(CartInfo *info) { + Latch_Init(info, M11Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +void Mapper144_Init(CartInfo *info) { + Latch_Init(info, M11Sync, 0, 0x8001, 0xFFFF, 0, 0); +} + +//------------------ Map 13 --------------------------- + +static void CPROMSync(void) { + setchr4(0x0000, 0); + setchr4(0x1000, latche & 3); + setprg32(0x8000, 0); +} + +void CPROM_Init(CartInfo *info) { + Latch_Init(info, CPROMSync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 29 --------------------------- //Used by Glider, http://www.retrousb.com/product_info.php?cPath=30&products_id=58 + +static void M29Sync() { + setprg16(0x8000, (latche & 0x1C) >> 2); + setprg16(0xc000, ~0); + setchr8r(0, latche & 3); + setprg8r(0x10, 0x6000, 0); +} + +void Mapper29_Init(CartInfo *info) { + Latch_Init(info, M29Sync, 0, 0x8000, 0xFFFF, 1, 0); +} + + +//------------------ Map 38 --------------------------- + +static void M38Sync(void) { + setprg32(0x8000, latche & 3); + setchr8(latche >> 2); +} + +void Mapper38_Init(CartInfo *info) { + Latch_Init(info, M38Sync, 0, 0x7000, 0x7FFF, 0, 0); +} + +//------------------ Map 66 --------------------------- + +static void MHROMSync(void) { + setprg32(0x8000, latche >> 4); + setchr8(latche & 0xF); +} + +void MHROM_Init(CartInfo *info) { + Latch_Init(info, MHROMSync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 70 --------------------------- + +static void M70Sync() { + setprg16(0x8000, latche >> 4); + setprg16(0xc000, ~0); + setchr8(latche & 0xf); +} + +void Mapper70_Init(CartInfo *info) { + Latch_Init(info, M70Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 78 --------------------------- +/* Should be two separate emulation functions for this "mapper". Sigh. URGE TO KILL RISING. */ +static void M78Sync() { + setprg16(0x8000, (latche & 7)); + setprg16(0xc000, ~0); + setchr8(latche >> 4); + setmirror(MI_0 + ((latche >> 3) & 1)); +} + +void Mapper78_Init(CartInfo *info) { + Latch_Init(info, M78Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 86 --------------------------- + +static void M86Sync(void) { + setprg32(0x8000, (latche >> 4) & 3); + setchr8((latche & 3) | ((latche >> 4) & 4)); +} + +void Mapper86_Init(CartInfo *info) { + Latch_Init(info, M86Sync, ~0, 0x6000, 0x6FFF, 0, 0); +} + +//------------------ Map 87 --------------------------- + +static void M87Sync(void) { + setprg32(0x8000, 0); + setchr8(((latche >> 1) & 1) | ((latche << 1) & 2)); +} + +void Mapper87_Init(CartInfo *info) { + Latch_Init(info, M87Sync, ~0, 0x6000, 0xFFFF, 0, 0); +} + +//------------------ Map 89 --------------------------- + +static void M89Sync(void) { + setprg16(0x8000, (latche >> 4) & 7); + setprg16(0xc000, ~0); + setchr8((latche & 7) | ((latche >> 4) & 8)); + setmirror(MI_0 + ((latche >> 3) & 1)); +} + +void Mapper89_Init(CartInfo *info) { + Latch_Init(info, M89Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 93 --------------------------- + +static void SSUNROMSync(void) { + setprg16(0x8000, latche >> 4); + setprg16(0xc000, ~0); + setchr8(0); +} + +void SUNSOFT_UNROM_Init(CartInfo *info) { + Latch_Init(info, SSUNROMSync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 94 --------------------------- + +static void M94Sync(void) { + setprg16(0x8000, latche >> 2); + setprg16(0xc000, ~0); + setchr8(0); +} + +void Mapper94_Init(CartInfo *info) { + Latch_Init(info, M94Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 97 --------------------------- + +static void M97Sync(void) { + setchr8(0); + setprg16(0x8000, ~0); + setprg16(0xc000, latche & 15); + switch (latche >> 6) { + case 0: break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_V); break; + case 3: break; + } + setchr8(((latche >> 1) & 1) | ((latche << 1) & 2)); +} + +void Mapper97_Init(CartInfo *info) { + Latch_Init(info, M97Sync, ~0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 101 --------------------------- + +static void M101Sync(void) { + setprg32(0x8000, 0); + setchr8(latche); +} + +void Mapper101_Init(CartInfo *info) { + Latch_Init(info, M101Sync, ~0, 0x6000, 0x7FFF, 0, 0); +} + +//------------------ Map 107 --------------------------- + +static void M107Sync(void) { + setprg32(0x8000, (latche >> 1) & 3); + setchr8(latche & 7); +} + +void Mapper107_Init(CartInfo *info) { + Latch_Init(info, M107Sync, ~0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 113 --------------------------- + +static void M113Sync(void) { + setprg32(0x8000, (latche >> 3) & 7); + setchr8(((latche >> 3) & 8) | (latche & 7)); +// setmirror(latche>>7); // only for HES 6in1 +} + +void Mapper113_Init(CartInfo *info) { + Latch_Init(info, M113Sync, 0, 0x4100, 0x7FFF, 0, 0); +} + +//------------------ Map 140 --------------------------- + +void Mapper140_Init(CartInfo *info) { + Latch_Init(info, MHROMSync, 0, 0x6000, 0x7FFF, 0, 0); +} + +//------------------ Map 152 --------------------------- + +static void M152Sync() { + setprg16(0x8000, (latche >> 4) & 7); + setprg16(0xc000, ~0); + setchr8(latche & 0xf); + setmirror(MI_0 + ((latche >> 7) & 1)); /* Saint Seiya...hmm. */ +} + +void Mapper152_Init(CartInfo *info) { + Latch_Init(info, M152Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 180 --------------------------- + +static void M180Sync(void) { + setprg16(0x8000, 0); + setprg16(0xc000, latche); + setchr8(0); +} + +void Mapper180_Init(CartInfo *info) { + Latch_Init(info, M180Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 184 --------------------------- + +static void M184Sync(void) { + setchr4(0x0000, latche); + setchr4(0x1000, latche >> 4); + setprg32(0x8000, 0); +} + +void Mapper184_Init(CartInfo *info) { + Latch_Init(info, M184Sync, 0, 0x6000, 0x7FFF, 0, 0); +} + +//------------------ Map 203 --------------------------- + +static void M203Sync(void) { + setprg16(0x8000, (latche >> 2) & 3); + setprg16(0xC000, (latche >> 2) & 3); + setchr8(latche & 3); +} + +void Mapper203_Init(CartInfo *info) { + Latch_Init(info, M203Sync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ Map 240 --------------------------- + +static void M240Sync(void) { + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, latche >> 4); + setchr8(latche & 0xF); +} + +void Mapper240_Init(CartInfo *info) { + Latch_Init(info, M240Sync, 0, 0x4020, 0x5FFF, 1, 0); +} + +//------------------ Map 241 --------------------------- +// Mapper 7 mostly, but with SRAM or maybe prot circuit +// figure out, which games do need 5xxx area reading + +static void M241Sync(void) { + setchr8(0); + setprg8r(0x10, 0x6000, 0); + if (latche & 0x80) + setprg32(0x8000, latche | 8); // no 241 actually, but why not afterall? + else + setprg32(0x8000, latche); +} + +void Mapper241_Init(CartInfo *info) { + Latch_Init(info, M241Sync, 0, 0x8000, 0xFFFF, 1, 0); +} + +//------------------ A65AS --------------------------- + +// actually, there is two cart in one... First have extra mirroring +// mode (one screen) and 32K bankswitching, second one have only +// 16 bankswitching mode and normal mirroring... But there is no any +// correlations between modes and they can be used in one mapper code. + +static void BMCA65ASSync(void) { + if (latche & 0x40) + setprg32(0x8000, (latche >> 1) & 0x0F); + else { + setprg16(0x8000, ((latche & 0x30) >> 1) | (latche & 7)); + setprg16(0xC000, ((latche & 0x30) >> 1) | 7); + } + setchr8(0); + if (latche & 0x80) + setmirror(MI_0 + (((latche >> 5) & 1))); + else + setmirror(((latche >> 3) & 1) ^ 1); +} + +void BMCA65AS_Init(CartInfo *info) { + Latch_Init(info, BMCA65ASSync, 0, 0x8000, 0xFFFF, 0, 0); +} + +//------------------ BMC-11160 --------------------------- +// Simple BMC discrete mapper by TXC + +static void BMC11160Sync(void) { + uint32 bank = (latche >> 4) & 7; + setprg32(0x8000, bank); + setchr8((bank << 2) | (latche & 3)); + setmirror((latche >> 7) & 1); +} + +void BMC11160_Init(CartInfo *info) { + Latch_Init(info, BMC11160Sync, 0, 0x8000, 0xFFFF, 0, 0); +} diff --git a/apps/fceux/src/boards/dream.cpp b/apps/fceux/src/boards/dream.cpp new file mode 100644 index 00000000..c42fa0f9 --- /dev/null +++ b/apps/fceux/src/boards/dream.cpp @@ -0,0 +1,51 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 latche; + +static void Sync(void) { + setprg16(0x8000, latche); + setprg16(0xC000, 8); +} + +static DECLFW(DREAMWrite) { + latche = V & 7; + Sync(); +} + +static void DREAMPower(void) { + latche = 0; + Sync(); + setchr8(0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x5020, 0x5020, DREAMWrite); +} + +static void Restore(int version) { + Sync(); +} + +void DreamTech01_Init(CartInfo *info) { + GameStateRestore = Restore; + info->Power = DREAMPower; + AddExState(&latche, 1, 0, "LATC"); +} diff --git a/apps/fceux/src/boards/edu2000.cpp b/apps/fceux/src/boards/edu2000.cpp new file mode 100644 index 00000000..36f1ad1b --- /dev/null +++ b/apps/fceux/src/boards/edu2000.cpp @@ -0,0 +1,78 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + */ + +#include "mapinc.h" + +static uint8 *WRAM = NULL; +static uint8 reg; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg8r(0x10, 0x6000, (reg & 0xC0) >> 6); + setprg32(0x8000, reg & 0x1F); +// setmirror(((reg&0x20)>>5)); +} + +static DECLFW(UNLEDU2000HiWrite) { +// FCEU_printf("%04x:%02x\n",A,V); + reg = V; + Sync(); +} + +static void UNLEDU2000Power(void) { + setmirror(MI_0); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0xFFFF, CartBW); + SetWriteHandler(0x8000, 0xFFFF, UNLEDU2000HiWrite); + FCEU_CheatAddRAM(32, 0x6000, WRAM); + reg = 0; + Sync(); +} + +static void UNLEDU2000Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void UNLEDU2000Restore(int version) { + Sync(); +} + +void UNLEDU2000_Init(CartInfo *info) { + info->Power = UNLEDU2000Power; + info->Close = UNLEDU2000Close; + GameStateRestore = UNLEDU2000Restore; + WRAM = (uint8*)FCEU_gmalloc(32768); + SetupCartPRGMapping(0x10, WRAM, 32768, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = 32768; + } + AddExState(WRAM, 32768, 0, "WRAM"); + AddExState(StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/eh8813a.cpp b/apps/fceux/src/boards/eh8813a.cpp new file mode 100644 index 00000000..34f7ce66 --- /dev/null +++ b/apps/fceux/src/boards/eh8813a.cpp @@ -0,0 +1,83 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2015 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint16 addrlatch; +static uint8 datalatch, hw_mode; + +static SFORMAT StateRegs[] = +{ + { &addrlatch, 2, "ADRL" }, + { &datalatch, 1, "DATL" }, + { &hw_mode, 1, "HWMO" }, + { 0 } +}; + +static void Sync(void) { + uint8 prg = (addrlatch & 7); + setchr8(datalatch); + if(addrlatch & 0x80) { + setprg16(0x8000,prg); + setprg16(0xC000,prg); + } else { + setprg32(0x8000,prg >> 1); + } + setmirror(MI_V); +} + +static DECLFW(EH8813AWrite) { + if((addrlatch & 0x100) == 0) { + addrlatch = A & 0x1FF; + datalatch = V & 0xF; + } + Sync(); +} + +static DECLFR(EH8813ARead) { + if (addrlatch & 0x40) + A= (A & 0xFFF0) + hw_mode; + return CartBR(A); +} + +static void EH8813APower(void) { + addrlatch = datalatch = hw_mode = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, EH8813ARead); + SetWriteHandler(0x8000, 0xFFFF, EH8813AWrite); +} + +static void EH8813AReset(void) { + addrlatch = datalatch = 0; + hw_mode = (hw_mode + 1) & 0xF; + FCEU_printf("Hardware Switch is %01X\n", hw_mode); + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLEH8813A_Init(CartInfo *info) { + info->Reset = EH8813AReset; + info->Power = EH8813APower; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/emu2413.c b/apps/fceux/src/boards/emu2413.c new file mode 100644 index 00000000..5c8e7042 --- /dev/null +++ b/apps/fceux/src/boards/emu2413.c @@ -0,0 +1,1078 @@ +/*********************************************************************************** + + emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001 + + 2001 01-08 : Version 0.10 -- 1st version. + 2001 01-15 : Version 0.20 -- semi-public version. + 2001 01-16 : Version 0.30 -- 1st public version. + 2001 01-17 : Version 0.31 -- Fixed bassdrum problem. + : Version 0.32 -- LPF implemented. + 2001 01-18 : Version 0.33 -- Fixed the drum problem, refine the mix-down method. + -- Fixed the LFO bug. + 2001 01-24 : Version 0.35 -- Fixed the drum problem, + support undocumented EG behavior. + 2001 02-02 : Version 0.38 -- Improved the performance. + Fixed the hi-hat and cymbal model. + Fixed the default percussive datas. + Noise reduction. + Fixed the feedback problem. + 2001 03-03 : Version 0.39 -- Fixed some drum bugs. + Improved the performance. + 2001 03-04 : Version 0.40 -- Improved the feedback. + Change the default table size. + Clock and Rate can be changed during play. + 2001 06-24 : Version 0.50 -- Improved the hi-hat and the cymbal tone. + Added VRC7 patch (OPLL_reset_patch is changed). + Fixed OPLL_reset() bug. + Added OPLL_setMask, OPLL_getMask and OPLL_toggleMask. + Added OPLL_writeIO. + 2001 09-28 : Version 0.51 -- Removed the noise table. + 2002 01-28 : Version 0.52 -- Added Stereo mode. + 2002 02-07 : Version 0.53 -- Fixed some drum bugs. + 2002 02-20 : Version 0.54 -- Added the best quality mode. + 2002 03-02 : Version 0.55 -- Removed OPLL_init & OPLL_close. + 2002 05-30 : Version 0.60 -- Fixed HH&CYM generator and all voice datas. + + 2004 01-24 : Modified by xodnizel to remove code not needed for the VRC7, among other things. + + References: + fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). + fmopl.c(fixed) -- (C) 2002 Jarek Burczynski. + s_opl.c -- 2001 written by Mamiya (NEZplug development). + fmgen.cpp -- 1999,2000 written by cisc. + fmpac.ill -- 2000 created by NARUTO. + MSX-Datapack + YMU757 data sheet + YM2143 data sheet + +**************************************************************************************/ +#include +#include +#include +#include +#include "emu2413.h" + +static const unsigned char default_inst[15][8] = { + /* VRC7 instruments, March 15, 2019 dumped by Nuke.YKT */ + { 0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27 }, + { 0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12 }, + { 0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12 }, + { 0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27 }, + { 0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28 }, + { 0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4 }, + { 0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07 }, + { 0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17 }, + { 0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01 }, + { 0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02 }, + { 0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12 }, + { 0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16 }, + { 0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02 }, + { 0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6 }, + { 0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06 }, +}; + +/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.)*/ +#define PG_BITS 9 +#define PG_WIDTH (1 << PG_BITS) + +/* Phase increment counter */ +#define DP_BITS 18 +#define DP_WIDTH (1 << DP_BITS) +#define DP_BASE_BITS (DP_BITS - PG_BITS) + +/* Dynamic range (Accuracy of sin table) */ +#define DB_BITS 8 +#define DB_STEP (48.0 / (1 << DB_BITS)) +#define DB_MUTE (1 << DB_BITS) + +/* Dynamic range of envelope */ +#define EG_STEP 0.375 +#define EG_BITS 7 +#define EG_MUTE (1 << EG_BITS) + +/* Dynamic range of total level */ +#define TL_STEP 0.75 +#define TL_BITS 6 +#define TL_MUTE (1 << TL_BITS) + +/* Dynamic range of sustine level */ +#define SL_STEP 3.0 +#define SL_BITS 4 +#define SL_MUTE (1 << SL_BITS) + +#define EG2DB(d) ((d) * (int32)(EG_STEP / DB_STEP)) +#define TL2EG(d) ((d) * (int32)(TL_STEP / EG_STEP)) +#define SL2EG(d) ((d) * (int32)(SL_STEP / EG_STEP)) + +#define DB_POS(x) (uint32)((x) / DB_STEP) +#define DB_NEG(x) (uint32)(DB_MUTE + DB_MUTE + (x) / DB_STEP) + +/* Bits for liner value */ +#define DB2LIN_AMP_BITS 11 +#define SLOT_AMP_BITS (DB2LIN_AMP_BITS) + +/* Bits for envelope phase incremental counter */ +#define EG_DP_BITS 22 +#define EG_DP_WIDTH (1 << EG_DP_BITS) + +/* Bits for Pitch and Amp modulator */ +#define PM_PG_BITS 8 +#define PM_PG_WIDTH (1 << PM_PG_BITS) +#define PM_DP_BITS 16 +#define PM_DP_WIDTH (1 << PM_DP_BITS) +#define AM_PG_BITS 8 +#define AM_PG_WIDTH (1 << AM_PG_BITS) +#define AM_DP_BITS 16 +#define AM_DP_WIDTH (1 << AM_DP_BITS) + +/* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */ +#define PM_AMP_BITS 8 +#define PM_AMP (1 << PM_AMP_BITS) + +/* PM speed(Hz) and depth(cent) */ +#define PM_SPEED 6.4 +#define PM_DEPTH 13.75 + +/* AM speed(Hz) and depth(dB) */ +#define AM_SPEED 3.7 +//#define AM_DEPTH 4.8 +#define AM_DEPTH 2.4 + +/* Cut the lower b bit(s) off. */ +#define HIGHBITS(c, b) ((c) >> (b)) + +/* Leave the lower b bit(s). */ +#define LOWBITS(c, b) ((c) & ((1 << (b)) - 1)) + +/* Expand x which is s bits to d bits. */ +#define EXPAND_BITS(x, s, d) ((x) << ((d) - (s))) + +/* Expand x which is s bits to d bits and fill expanded bits '1' */ +#define EXPAND_BITS_X(x, s, d) (((x) << ((d) - (s))) | ((1 << ((d) - (s))) - 1)) + +/* Adjust envelope speed which depends on sampling rate. */ +#define rate_adjust(x) (rate == 49716 ? x : (uint32)((double)(x) * clk / 72 / rate + 0.5)) /* added 0.5 to round the value*/ + +#define MOD(o, x) (&(o)->slot[(x) << 1]) +#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) + +#define BIT(s, b) (((s) >> (b)) & 1) + +/* Input clock */ +static uint32 clk = 844451141; +/* Sampling rate */ +static uint32 rate = 3354932; + +/* WaveTable for each envelope amp */ +static uint16 fullsintable[PG_WIDTH]; +static uint16 halfsintable[PG_WIDTH]; + +static uint16 *waveform[2] = { fullsintable, halfsintable }; + +/* LFO Table */ +static int32 pmtable[PM_PG_WIDTH]; +static int32 amtable[AM_PG_WIDTH]; + +/* Phase delta for LFO */ +static uint32 pm_dphase; +static uint32 am_dphase; + +/* dB to Liner table */ +static int16 DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2]; + +/* Liner to Log curve conversion table (for Attack rate). */ +static uint16 AR_ADJUST_TABLE[1 << EG_BITS]; + +/* Definition of envelope mode */ +enum +{ SETTLE, ATTACK, DECAY, SUSHOLD, SUSTINE, RELEASE, FINISH }; + +/* Phase incr table for Attack */ +static uint32 dphaseARTable[16][16]; +/* Phase incr table for Decay and Release */ +static uint32 dphaseDRTable[16][16]; + +/* KSL + TL Table */ +static uint32 tllTable[16][8][1 << TL_BITS][4]; +static int32 rksTable[2][8][2]; + +/* Phase incr table for PG */ +static uint32 dphaseTable[512][8][16]; + +/*************************************************** + + Create tables + +****************************************************/ +INLINE static int32 Min(int32 i, int32 j) { + if (i < j) + return i; + else + return j; +} + +/* Table for AR to LogCurve. */ +static void makeAdjustTable(void) { + int32 i; + + AR_ADJUST_TABLE[0] = (1 << EG_BITS); + for (i = 1; i < 128; i++) + AR_ADJUST_TABLE[i] = (uint16)((double)(1 << EG_BITS) - 1 - (1 << EG_BITS) * log(i) / log(128)); +} + + +/* Table for dB(0 -- (1<= DB_MUTE) DB2LIN_TABLE[i] = 0; + DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (int16)(-DB2LIN_TABLE[i]); + } +} + +/* Liner(+0.0 - +1.0) to dB((1<> (20 - DP_BITS)); +} + +static void makeTllTable(void) { +#define dB2(x) ((x) * 2) + + static double kltable[16] = { + dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), dB2(16.875), dB2(17.625), + dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000) + }; + + int32 tmp; + int32 fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) + for (block = 0; block < 8; block++) + for (TL = 0; TL < 64; TL++) + for (KL = 0; KL < 4; KL++) { + if (KL == 0) { + tllTable[fnum][block][TL][KL] = TL2EG(TL); + } else { + tmp = (int32)(kltable[fnum] - dB2(3.000) * (7 - block)); + if (tmp <= 0) + tllTable[fnum][block][TL][KL] = TL2EG(TL); + else + tllTable[fnum][block][TL][KL] = (uint32)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); + } + } +} + +/* Rate Table for Attack */ +static void makeDphaseARTable(void) { + int32 AR, Rks, RM, RL; + for (AR = 0; AR < 16; AR++) + for (Rks = 0; Rks < 16; Rks++) { + RM = AR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (AR) { + case 0: + dphaseARTable[AR][Rks] = 0; + break; + case 15: + dphaseARTable[AR][Rks] = 0; /*EG_DP_WIDTH;*/ + break; + default: + dphaseARTable[AR][Rks] = rate_adjust(3 * (RL + 4) << (RM + 1)); + break; + } + } +} + +/* Rate Table for Decay and Release */ +static void makeDphaseDRTable(void) { + int32 DR, Rks, RM, RL; + + for (DR = 0; DR < 16; DR++) + for (Rks = 0; Rks < 16; Rks++) { + RM = DR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) + RM = 15; + switch (DR) { + case 0: + dphaseDRTable[DR][Rks] = 0; + break; + default: + dphaseDRTable[DR][Rks] = rate_adjust((RL + 4) << (RM - 1)); + break; + } + } +} + +static void makeRksTable(void) { + int32 fnum8, block, KR; + + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) + for (KR = 0; KR < 2; KR++) { + if (KR != 0) + rksTable[fnum8][block][KR] = (block << 1) + fnum8; + else + rksTable[fnum8][block][KR] = block >> 1; + } +} + +/************************************************************ + + Calc Parameters + +************************************************************/ + +INLINE static uint32 calc_eg_dphase(OPLL_SLOT * slot) { + switch (slot->eg_mode) { + case ATTACK: + return dphaseARTable[slot->patch.AR][slot->rks]; + + case DECAY: + return dphaseDRTable[slot->patch.DR][slot->rks]; + + case SUSHOLD: + return 0; + + case SUSTINE: + return dphaseDRTable[slot->patch.RR][slot->rks]; + + case RELEASE: + if (slot->sustine) + return dphaseDRTable[5][slot->rks]; + else if (slot->patch.EG) + return dphaseDRTable[slot->patch.RR][slot->rks]; + else + return dphaseDRTable[7][slot->rks]; + + case FINISH: + return 0; + + default: + return 0; + } +} + +/************************************************************* + + OPLL internal interfaces + +*************************************************************/ + +#define UPDATE_PG(S) (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch.ML] +#define UPDATE_TLL(S) \ + (((S)->type == 0) ? \ + ((S)->tll = tllTable[((S)->fnum) >> 5][(S)->block][(S)->patch.TL][(S)->patch.KL]) : \ + ((S)->tll = tllTable[((S)->fnum) >> 5][(S)->block][(S)->volume][(S)->patch.KL])) +#define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum) >> 8][(S)->block][(S)->patch.KR] +#define UPDATE_WF(S) (S)->sintbl = waveform[(S)->patch.WF] +#define UPDATE_EG(S) (S)->eg_dphase = calc_eg_dphase(S) +#define UPDATE_ALL(S) \ + UPDATE_PG(S); \ + UPDATE_TLL(S); \ + UPDATE_RKS(S); \ + UPDATE_WF(S); \ + UPDATE_EG(S) /* EG should be updated last. */ + + +/* Slot key on */ +INLINE static void slotOn(OPLL_SLOT * slot) { + slot->eg_mode = ATTACK; + slot->eg_phase = 0; + slot->phase = 0; +} + +/* Slot key on without reseting the phase */ +INLINE static void slotOn2(OPLL_SLOT * slot) { + slot->eg_mode = ATTACK; + slot->eg_phase = 0; +} + +/* Slot key off */ +INLINE static void slotOff(OPLL_SLOT * slot) { + if (slot->eg_mode == ATTACK) + slot->eg_phase = EXPAND_BITS(AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS); + slot->eg_mode = RELEASE; +} + +/* Channel key on */ +INLINE static void keyOn(OPLL * opll, int32 i) { + if (!opll->slot_on_flag[i * 2]) + slotOn(MOD(opll, i)); + if (!opll->slot_on_flag[i * 2 + 1]) + slotOn(CAR(opll, i)); + opll->key_status[i] = 1; +} + +/* Channel key off */ +INLINE static void keyOff(OPLL * opll, int32 i) { + if (opll->slot_on_flag[i * 2 + 1]) + slotOff(CAR(opll, i)); + opll->key_status[i] = 0; +} + +/* Set sustine parameter */ +INLINE static void setSustine(OPLL * opll, int32 c, int32 sustine) { + CAR(opll, c)->sustine = sustine; + if (MOD(opll, c)->type) + MOD(opll, c)->sustine = sustine; +} + +/* Volume : 6bit ( Volume register << 2 ) */ +INLINE static void setVolume(OPLL * opll, int32 c, int32 volume) { + CAR(opll, c)->volume = volume; +} + +INLINE static void setSlotVolume(OPLL_SLOT * slot, int32 volume) { + slot->volume = volume; +} + +/* Set F-Number ( fnum : 9bit ) */ +INLINE static void setFnumber(OPLL * opll, int32 c, int32 fnum) { + CAR(opll, c)->fnum = fnum; + MOD(opll, c)->fnum = fnum; +} + +/* Set Block data (block : 3bit ) */ +INLINE static void setBlock(OPLL * opll, int32 c, int32 block) { + CAR(opll, c)->block = block; + MOD(opll, c)->block = block; +} + +INLINE static void update_key_status(OPLL * opll) { int ch; + + for (ch = 0; ch < 6; ch++) + opll->slot_on_flag[ch * 2] = opll->slot_on_flag[ch * 2 + 1] = (opll->HiFreq[ch]) & 0x10; +} + +/*********************************************************** + + Initializing + +***********************************************************/ + +static void OPLL_SLOT_reset(OPLL_SLOT * slot, int type) { + slot->type = type; + slot->sintbl = waveform[0]; + slot->phase = 0; + slot->dphase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->feedback = 0; + slot->eg_mode = SETTLE; + slot->eg_phase = EG_DP_WIDTH; + slot->eg_dphase = 0; + slot->rks = 0; + slot->tll = 0; + slot->sustine = 0; + slot->fnum = 0; + slot->block = 0; + slot->volume = 0; + slot->pgout = 0; + slot->egout = 0; +} + +static void internal_refresh(void) { + makeDphaseTable(); + makeDphaseARTable(); + makeDphaseDRTable(); + pm_dphase = (uint32)rate_adjust(PM_SPEED * PM_DP_WIDTH / (clk / 72)); + am_dphase = (uint32)rate_adjust(AM_SPEED * AM_DP_WIDTH / (clk / 72)); +} + +static void maketables(uint32 c, uint32 r) { + if (c != clk) { + clk = c; + makePmTable(); + makeAmTable(); + makeDB2LinTable(); + makeAdjustTable(); + makeTllTable(); + makeRksTable(); + makeSinTable(); + //makeDefaultPatch (); + } + + if (r != rate) { + rate = r; + internal_refresh(); + } +} + +OPLL *OPLL_new(uint32 clk, uint32 rate) { + OPLL *opll; + + maketables(clk, rate); + + opll = (OPLL*)calloc(sizeof(OPLL), 1); + if (opll == NULL) + return NULL; + + opll->mask = 0; + + OPLL_reset(opll); + + return opll; +} + + +void OPLL_delete(OPLL * opll) { + free(opll); +} + +/* Reset whole of OPLL except patch datas. */ +void OPLL_reset(OPLL * opll) { + int32 i; + + if (!opll) + return; + + opll->adr = 0; + opll->out = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->mask = 0; + + for (i = 0; i < 12; i++) + OPLL_SLOT_reset(&opll->slot[i], i % 2); + + for (i = 0; i < 6; i++) { + opll->key_status[i] = 0; + //setPatch (opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg(opll, i, 0); + + opll->realstep = (uint32)((1 << 31) / rate); + opll->opllstep = (uint32)((1 << 31) / (clk / 72)); + opll->oplltime = 0; +} + +/* Force Refresh (When external program changes some parameters). */ +void OPLL_forceRefresh(OPLL * opll) { + int32 i; + + if (opll == NULL) + return; + + for (i = 0; i < 12; i++) { + UPDATE_PG(&opll->slot[i]); + UPDATE_RKS(&opll->slot[i]); + UPDATE_TLL(&opll->slot[i]); + UPDATE_WF(&opll->slot[i]); + UPDATE_EG(&opll->slot[i]); + } +} + +void OPLL_set_rate(OPLL * opll, uint32 r) { + if (opll->quality) + rate = 49716; + else + rate = r; + internal_refresh(); + rate = r; +} + +void OPLL_set_quality(OPLL * opll, uint32 q) { + opll->quality = q; + OPLL_set_rate(opll, rate); +} + +/********************************************************* + + Generate wave data + +*********************************************************/ +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */ +#if (SLOT_AMP_BITS - PG_BITS) > 0 +#define wave2_2pi(e) ((e) >> (SLOT_AMP_BITS - PG_BITS)) +#else +#define wave2_2pi(e) ((e) << (PG_BITS - SLOT_AMP_BITS)) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */ +#if (SLOT_AMP_BITS - PG_BITS - 1) == 0 +#define wave2_4pi(e) (e) +#elif (SLOT_AMP_BITS - PG_BITS - 1) > 0 +#define wave2_4pi(e) ((e) >> (SLOT_AMP_BITS - PG_BITS - 1)) +#else +#define wave2_4pi(e) ((e) << (1 + PG_BITS - SLOT_AMP_BITS)) +#endif + +/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */ +#if (SLOT_AMP_BITS - PG_BITS - 2) == 0 +#define wave2_8pi(e) (e) +#elif (SLOT_AMP_BITS - PG_BITS - 2) > 0 +#define wave2_8pi(e) ((e) >> (SLOT_AMP_BITS - PG_BITS - 2)) +#else +#define wave2_8pi(e) ((e) << (2 + PG_BITS - SLOT_AMP_BITS)) +#endif + + + +/* Update AM, PM unit */ +static void update_ampm(OPLL * opll) { + opll->pm_phase = (opll->pm_phase + pm_dphase) & (PM_DP_WIDTH - 1); + opll->am_phase = (opll->am_phase + am_dphase) & (AM_DP_WIDTH - 1); + opll->lfo_am = amtable[HIGHBITS(opll->am_phase, AM_DP_BITS - AM_PG_BITS)]; + opll->lfo_pm = pmtable[HIGHBITS(opll->pm_phase, PM_DP_BITS - PM_PG_BITS)]; +} + +/* PG */ +INLINE static void calc_phase(OPLL_SLOT * slot, int32 lfo) { + if (slot->patch.PM) + slot->phase += (slot->dphase * lfo) >> PM_AMP_BITS; + else + slot->phase += slot->dphase; + + slot->phase &= (DP_WIDTH - 1); + + slot->pgout = HIGHBITS(slot->phase, DP_BASE_BITS); +} + +/* EG */ +static void calc_envelope(OPLL_SLOT * slot, int32 lfo) { +#define S2E(x) (SL2EG((int32)(x / SL_STEP)) << (EG_DP_BITS - EG_BITS)) + + static uint32 SL[16] = { + S2E(0.0), S2E(3.0), S2E(6.0), S2E(9.0), S2E(12.0), S2E(15.0), S2E(18.0), S2E(21.0), + S2E(24.0), S2E(27.0), S2E(30.0), S2E(33.0), S2E(36.0), S2E(39.0), S2E(42.0), S2E(48.0) + }; + + uint32 egout; + + switch (slot->eg_mode) { + case ATTACK: + egout = AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)]; + slot->eg_phase += slot->eg_dphase; + if ((EG_DP_WIDTH & slot->eg_phase) || (slot->patch.AR == 15)) { + egout = 0; + slot->eg_phase = 0; + slot->eg_mode = DECAY; + UPDATE_EG(slot); + } + break; + + case DECAY: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (slot->eg_phase >= SL[slot->patch.SL]) { + if (slot->patch.EG) { + slot->eg_phase = SL[slot->patch.SL]; + slot->eg_mode = SUSHOLD; + UPDATE_EG(slot); + } else { + slot->eg_phase = SL[slot->patch.SL]; + slot->eg_mode = SUSTINE; + UPDATE_EG(slot); + } + } + break; + + case SUSHOLD: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + if (slot->patch.EG == 0) { + slot->eg_mode = SUSTINE; + UPDATE_EG(slot); + } + break; + + case SUSTINE: + case RELEASE: + egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); + slot->eg_phase += slot->eg_dphase; + if (egout >= (1 << EG_BITS)) { + slot->eg_mode = FINISH; + egout = (1 << EG_BITS) - 1; + } + break; + + case FINISH: + egout = (1 << EG_BITS) - 1; + break; + + default: + egout = (1 << EG_BITS) - 1; + break; + } + + if (slot->patch.AM) + egout = EG2DB(egout + slot->tll) + lfo; + else + egout = EG2DB(egout + slot->tll); + + if (egout >= DB_MUTE) + egout = DB_MUTE - 1; + + slot->egout = egout; +} + +/* CARRIOR */ +INLINE static int32 calc_slot_car(OPLL_SLOT * slot, int32 fm) { + slot->output[1] = slot->output[0]; + + if (slot->egout >= (DB_MUTE - 1)) { + slot->output[0] = 0; + } else { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout + wave2_8pi(fm)) & (PG_WIDTH - 1)] + slot->egout]; + } + + return (slot->output[1] + slot->output[0]) >> 1; +} + +/* MODULATOR */ +INLINE static int32 calc_slot_mod(OPLL_SLOT * slot) { + int32 fm; + + slot->output[1] = slot->output[0]; + + if (slot->egout >= (DB_MUTE - 1)) { + slot->output[0] = 0; + } else if (slot->patch.FB != 0) { + fm = wave2_4pi(slot->feedback) >> (7 - slot->patch.FB); + slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout + fm) & (PG_WIDTH - 1)] + slot->egout]; + } else { + slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout]; + } + + slot->feedback = (slot->output[1] + slot->output[0]) >> 1; + + return slot->feedback; +} + +static INLINE int16 calc(OPLL * opll) { + int32 inst = 0, out = 0; + int32 i; + + update_ampm(opll); + + for (i = 0; i < 12; i++) { + calc_phase(&opll->slot[i], opll->lfo_pm); + calc_envelope(&opll->slot[i], opll->lfo_am); + } + + for (i = 0; i < 6; i++) + if (!(opll->mask & OPLL_MASK_CH(i)) && (CAR(opll, i)->eg_mode != FINISH)) + inst += calc_slot_car(CAR(opll, i), calc_slot_mod(MOD(opll, i))); + + out = inst; + return (int16)out; +} + +void OPLL_fillbuf(OPLL* opll, int32 *buf, int32 len, int shift) { + while (len > 0) { + *buf += (calc(opll) + 32768) << shift; + buf++; + len--; + } +} + +int16 OPLL_calc(OPLL * opll) { + if (!opll->quality) + return calc(opll); + + while (opll->realstep > opll->oplltime) { + opll->oplltime += opll->opllstep; + opll->prev = opll->next; + opll->next = calc(opll); + } + + opll->oplltime -= opll->realstep; + opll->out = (int16)(((double)opll->next * (opll->opllstep - opll->oplltime) + + (double)opll->prev * opll->oplltime) / opll->opllstep); + + return (int16)opll->out; +} + +uint32 OPLL_setMask(OPLL * opll, uint32 mask) { + uint32 ret; + + if (opll) { + ret = opll->mask; + opll->mask = mask; + return ret; + } else + return 0; +} + +uint32 OPLL_toggleMask(OPLL * opll, uint32 mask) { + uint32 ret; + + if (opll) { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } else + return 0; +} + +/**************************************************** + + I/O Ctrl + +*****************************************************/ + +static void setInstrument(OPLL * opll, uint32 i, uint32 inst) { + const uint8 *src; + OPLL_PATCH *modp, *carp; + + opll->patch_number[i] = inst; + + if (inst) + src = default_inst[inst - 1]; + else + src = opll->CustInst; + + modp = &MOD(opll, i)->patch; + carp = &CAR(opll, i)->patch; + + modp->AM = (src[0] >> 7) & 1; + modp->PM = (src[0] >> 6) & 1; + modp->EG = (src[0] >> 5) & 1; + modp->KR = (src[0] >> 4) & 1; + modp->ML = (src[0] & 0xF); + + carp->AM = (src[1] >> 7) & 1; + carp->PM = (src[1] >> 6) & 1; + carp->EG = (src[1] >> 5) & 1; + carp->KR = (src[1] >> 4) & 1; + carp->ML = (src[1] & 0xF); + + modp->KL = (src[2] >> 6) & 3; + modp->TL = (src[2] & 0x3F); + + carp->KL = (src[3] >> 6) & 3; + carp->WF = (src[3] >> 4) & 1; + + modp->WF = (src[3] >> 3) & 1; + + modp->FB = (src[3]) & 7; + + modp->AR = (src[4] >> 4) & 0xF; + modp->DR = (src[4] & 0xF); + + carp->AR = (src[5] >> 4) & 0xF; + carp->DR = (src[5] & 0xF); + + modp->SL = (src[6] >> 4) & 0xF; + modp->RR = (src[6] & 0xF); + + carp->SL = (src[7] >> 4) & 0xF; + carp->RR = (src[7] & 0xF); +} + + +void OPLL_writeReg(OPLL * opll, uint32 reg, uint32 data) { + int32 i, v, ch; + + data = data & 0xff; + reg = reg & 0x3f; + + switch (reg) { + case 0x00: + opll->CustInst[0] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_PG(MOD(opll, i)); + UPDATE_RKS(MOD(opll, i)); + UPDATE_EG(MOD(opll, i)); + } + } + break; + + case 0x01: + opll->CustInst[1] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_PG(CAR(opll, i)); + UPDATE_RKS(CAR(opll, i)); + UPDATE_EG(CAR(opll, i)); + } + } + break; + + case 0x02: + opll->CustInst[2] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_TLL(MOD(opll, i)); + } + } + break; + + case 0x03: + opll->CustInst[3] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_WF(MOD(opll, i)); + UPDATE_WF(CAR(opll, i)); + } + } + break; + + case 0x04: + opll->CustInst[4] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_EG(MOD(opll, i)); + } + } + break; + + case 0x05: + opll->CustInst[5] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_EG(CAR(opll, i)); + } + } + break; + + case 0x06: + opll->CustInst[6] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_EG(MOD(opll, i)); + } + } + break; + + case 0x07: + opll->CustInst[7] = data; + for (i = 0; i < 6; i++) { + if (opll->patch_number[i] == 0) { + setInstrument(opll, i, 0); + UPDATE_EG(CAR(opll, i)); + } + } + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + ch = reg - 0x10; + opll->LowFreq[ch] = data; + setFnumber(opll, ch, data + ((opll->HiFreq[ch] & 1) << 8)); + UPDATE_ALL(MOD(opll, ch)); + UPDATE_ALL(CAR(opll, ch)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + ch = reg - 0x20; + opll->HiFreq[ch] = data; + + setFnumber(opll, ch, ((data & 1) << 8) + opll->LowFreq[ch]); + setBlock(opll, ch, (data >> 1) & 7); + setSustine(opll, ch, (data >> 5) & 1); + if (data & 0x10) + keyOn(opll, ch); + else + keyOff(opll, ch); + UPDATE_ALL(MOD(opll, ch)); + UPDATE_ALL(CAR(opll, ch)); + update_key_status(opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + opll->InstVol[reg - 0x30] = data; + i = (data >> 4) & 15; + v = data & 15; + setInstrument(opll, reg - 0x30, i); + setVolume(opll, reg - 0x30, v << 2); + UPDATE_ALL(MOD(opll, reg - 0x30)); + UPDATE_ALL(CAR(opll, reg - 0x30)); + break; + + default: + break; + } +} + +void OPLL_writeIO(OPLL * opll, uint32 adr, uint32 val) { + if (adr & 1) + OPLL_writeReg(opll, opll->adr, val); + else + opll->adr = val; +} + diff --git a/apps/fceux/src/boards/emu2413.h b/apps/fceux/src/boards/emu2413.h new file mode 100644 index 00000000..c895f30f --- /dev/null +++ b/apps/fceux/src/boards/emu2413.h @@ -0,0 +1,139 @@ +#ifndef _EMU2413_H_ +#define _EMU2413_H_ + +#ifndef INLINE +#if defined(_MSC_VER) +#define INLINE __forceinline +#elif defined(__GNUC__) +#define INLINE __inline__ +#elif defined(_MWERKS_) +#define INLINE inline +#else +#define INLINE +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char uint8 ; +typedef signed char int8 ; + +typedef unsigned short uint16 ; +typedef signed short int16 ; + +typedef unsigned int uint32 ; +typedef signed int int32 ; + +#define PI 3.14159265358979323846 + +enum { OPLL_VRC7_TONE=0 }; + +/* voice data */ +typedef struct { + uint32 TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WF; +} OPLL_PATCH; + +/* slot */ +typedef struct { + OPLL_PATCH patch; + + int32 type; /* 0 : modulator 1 : carrier */ + + /* OUTPUT */ + int32 feedback; + int32 output[2]; /* Output value of slot */ + + /* for Phase Generator (PG) */ + uint16 *sintbl; /* Wavetable */ + uint32 phase; /* Phase */ + uint32 dphase; /* Phase increment amount */ + uint32 pgout; /* output */ + + /* for Envelope Generator (EG) */ + int32 fnum; /* F-Number */ + int32 block; /* Block */ + int32 volume; /* Current volume */ + int32 sustine; /* Sustine 1 = ON, 0 = OFF */ + uint32 tll; /* Total Level + Key scale level*/ + uint32 rks; /* Key scale offset (Rks) */ + int32 eg_mode; /* Current state */ + uint32 eg_phase; /* Phase */ + uint32 eg_dphase; /* Phase increment amount */ + uint32 egout; /* output */ +} OPLL_SLOT; + +/* Mask */ +#define OPLL_MASK_CH(x) (1 << (x)) + +/* opll */ +typedef struct { + uint32 adr; + int32 out; + + uint32 realstep; + uint32 oplltime; + uint32 opllstep; + int32 prev, next; + + /* Register */ + uint8 LowFreq[6]; + uint8 HiFreq[6]; + uint8 InstVol[6]; + + uint8 CustInst[8]; + + int32 slot_on_flag[6 * 2]; + + /* Pitch Modulator */ + uint32 pm_phase; + int32 lfo_pm; + + /* Amp Modulator */ + int32 am_phase; + int32 lfo_am; + + uint32 quality; + + /* Channel Data */ + int32 patch_number[6]; + int32 key_status[6]; + + /* Slot */ + OPLL_SLOT slot[6 * 2]; + + uint32 mask; +} OPLL; + +/* Create Object */ +OPLL *OPLL_new(uint32 clk, uint32 rate); +void OPLL_delete(OPLL *); + +/* Setup */ +void OPLL_reset(OPLL *); +void OPLL_set_rate(OPLL *opll, uint32 r); +void OPLL_set_quality(OPLL *opll, uint32 q); + +/* Port/Register access */ +void OPLL_writeIO(OPLL *, uint32 reg, uint32 val); +void OPLL_writeReg(OPLL *, uint32 reg, uint32 val); + +/* Synthsize */ +int16 OPLL_calc(OPLL *); + +/* Misc */ +void OPLL_forceRefresh(OPLL *); + +/* Channel Mask */ +uint32 OPLL_setMask(OPLL *, uint32 mask); +uint32 OPLL_toggleMask(OPLL *, uint32 mask); + + +void OPLL_fillbuf(OPLL* opll, int32 *buf, int32 len, int shift); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/apps/fceux/src/boards/et-100.cpp b/apps/fceux/src/boards/et-100.cpp new file mode 100644 index 00000000..b3539ce1 --- /dev/null +++ b/apps/fceux/src/boards/et-100.cpp @@ -0,0 +1,103 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2015 Cluster + * http://clusterrr.com + * clusterrr@clusterrr.com + * + * 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 + */ + +/* + MMC3-based multicart mapper with CHR RAM, CHR ROM and PRG RAM + + $6000-7FFF: A~[011xxxxx xxMRSBBB] Multicart reg + This register can only be written to if PRG-RAM is enabled and writable (see $A001) + and BBB = 000 (power on state) + + BBB = CHR+PRG block select bits (A19, A18, A17 for both PRG and CHR) + S = PRG block size (0=256k 1=128k) + R = CHR mode (0=CHR ROM 1=CHR RAM) + M = CHR block size (0=256k 1=128k) + ignored when S is 0 for some reason + + Example Game: + -------------------------- + 6 in 1 multicart (SMB3, TMNT2, Contra, Ninja Cat, Ninja Crusaders, Rainbow Islands 2) +*/ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 *CHRRAM; +static uint32 CHRRAMSize; + +static void BMC1024CA1PW(uint32 A, uint8 V) { + if ((EXPREGS[0]>>3)&1) + setprg8(A, (V&0x1F) | ((EXPREGS[0] & 7) << 4)); + else + setprg8(A, (V&0x0F) | ((EXPREGS[0] & 7) << 4)); +} + +static void BMC1024CA1CW(uint32 A, uint8 V) { + if ((EXPREGS[0]>>4)&1) + setchr1r(0x10, A, V); + else if (((EXPREGS[0]>>5)&1) && ((EXPREGS[0]>>3)&1)) + setchr1(A, V | ((EXPREGS[0] & 7) << 7)); + else + setchr1(A, (V&0x7F) | ((EXPREGS[0] & 7) << 7)); +} + +static DECLFW(BMC1024CA1Write) { + if (((A001B & 0xC0) == 0x80) && !(EXPREGS[0] & 7)) + { + EXPREGS[0] = A & 0x3F; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else { + CartBW(A, V); + } +} + +static void BMC1024CA1Reset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +static void BMC1024CA1Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, BMC1024CA1Write); +} + +static void BMC1024CA1Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +void BMC1024CA1_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 8, 0); + CHRRAMSize = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSize); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSize, 1); + AddExState(CHRRAM, CHRRAMSize, 0, "CHRR"); + pwrap = BMC1024CA1PW; + cwrap = BMC1024CA1CW; + info->Power = BMC1024CA1Power; + info->Reset = BMC1024CA1Reset; + info->Close = BMC1024CA1Close; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/et-4320.cpp b/apps/fceux/src/boards/et-4320.cpp new file mode 100644 index 00000000..19042260 --- /dev/null +++ b/apps/fceux/src/boards/et-4320.cpp @@ -0,0 +1,119 @@ +/* FCE Ultra - NES/Famicom Emulator +* +* Copyright notice for this file: +* Copyright (C) 2016 Cluster +* http://clusterrr.com +* clusterrr@clusterrr.com +* +* 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 +*/ + +/* +MMC3-based multicart mapper with CHR RAM, CHR ROM and PRG RAM + +$6000-7FFF: A~[011xxxxx xxMRSBBB] Multicart reg +This register can only be written to if PRG-RAM is enabled and writable (see $A001) +and BBB = 000 (power on state) + +BBB = CHR+PRG block select bits (A19, A18, A17 for both PRG and CHR) +S = PRG block size & mirroring mode (0=128k with normal MMC3, 1=256k with TxSROM-like single-screen mirroring) +R = CHR mode (0=CHR ROM 1=CHR RAM) +M = CHR block size (0=256k 1=128k) +ignored when S is 0 for some reason + +Example Game: +-------------------------- +7 in 1 multicart (Amarello, TMNT2, Contra, Ninja Cat, Ninja Crusaders, Rainbow Islands 2) +*/ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 *CHRRAM; +static uint32 CHRRAMSize; +static uint8 PPUCHRBus; +static uint8 TKSMIR[8]; + +static void BMC810131C_PW(uint32 A, uint8 V) { + if ((EXPREGS[0] >> 3) & 1) + setprg8(A, (V & 0x1F) | ((EXPREGS[0] & 7) << 4)); + else + setprg8(A, (V & 0x0F) | ((EXPREGS[0] & 7) << 4)); +} + +static void BMC810131C_CW(uint32 A, uint8 V) { + if ((EXPREGS[0] >> 4) & 1) + setchr1r(0x10, A, V); + else if (((EXPREGS[0] >> 5) & 1) && ((EXPREGS[0] >> 3) & 1)) + setchr1(A, V | ((EXPREGS[0] & 7) << 7)); + else + setchr1(A, (V & 0x7F) | ((EXPREGS[0] & 7) << 7)); + + TKSMIR[A >> 10] = V >> 7; + if (((EXPREGS[0] >> 3) & 1) && (PPUCHRBus == (A >> 10))) + setmirror(MI_0 + (V >> 7)); +} + +static DECLFW(BMC810131C_Write) { + if (((A001B & 0xC0) == 0x80) && !(EXPREGS[0] & 7)) + { + EXPREGS[0] = A & 0x3F; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } + else { + CartBW(A, V); + } +} + +static void BMC810131C_Reset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +static void BMC810131C_Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, BMC810131C_Write); +} + +static void BMC810131C_Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +static void TKSPPU(uint32 A) { + A &= 0x1FFF; + A >>= 10; + PPUCHRBus = A; + if ((EXPREGS[0] >> 3) & 1) + setmirror(MI_0 + TKSMIR[A]); +} + +void BMC810131C_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 8, 0); + CHRRAMSize = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSize); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSize, 1); + AddExState(CHRRAM, CHRRAMSize, 0, "CHRR"); + pwrap = BMC810131C_PW; + cwrap = BMC810131C_CW; + PPU_hook = TKSPPU; + info->Power = BMC810131C_Power; + info->Reset = BMC810131C_Reset; + info->Close = BMC810131C_Close; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/famicombox.cpp b/apps/fceux/src/boards/famicombox.cpp new file mode 100644 index 00000000..077931f9 --- /dev/null +++ b/apps/fceux/src/boards/famicombox.cpp @@ -0,0 +1,115 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 regs[8]; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { regs, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg2r(0x10, 0x0800, 0); + setprg2r(0x10, 0x1000, 1); + setprg2r(0x10, 0x1800, 2); + setprg8r(0x10, 0x6000, 1); + setprg16(0x8000, 0); + setprg16(0xC000, ~0); + setchr8(0); +} + +//static DECLFW(SSSNROMWrite) +//{ +// CartBW(A,V); +//} + +static DECLFW(SSSNROMWrite) { +// FCEU_printf("write %04x %02x\n",A,V); +// regs[A&7] = V; +} + +static DECLFR(SSSNROMRead) { +// FCEU_printf("read %04x\n",A); + switch (A & 7) { + case 0: return regs[0] = 0xff; // clear all exceptions + case 2: return 0xc0; // DIP selftest + freeplay + case 3: return 0x00; // 0, 1 - attract + // 2 + // 4 - menu + // 8 - self check and game casette check + // 10 - lock? + // 20 - game title & count display + case 7: return 0x22; // TV type, key not turned, relay B + default: return 0; + } +} + +static void SSSNROMPower(void) { + regs[0] = regs[1] = regs[2] = regs[3] = regs[4] = regs[5] = regs[6] = 0; + regs[7] = 0xff; + Sync(); + FCEU_MemoryRand(WRAM, WRAMSIZE, true); +// SetWriteHandler(0x0000,0x1FFF,SSSNROMRamWrite); + SetReadHandler(0x0800, 0x1FFF, CartBR); + SetWriteHandler(0x0800, 0x1FFF, CartBW); + SetReadHandler(0x5000, 0x5FFF, SSSNROMRead); + SetWriteHandler(0x5000, 0x5FFF, SSSNROMWrite); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void SSSNROMReset(void) { + regs[1] = regs[2] = regs[3] = regs[4] = regs[5] = regs[6] = 0; +} + +static void SSSNROMClose(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void SSSNROMIRQHook(void) { +// X6502_IRQBegin(FCEU_IQEXT); +} + +static void StateRestore(int version) { + Sync(); +} + +void SSSNROM_Init(CartInfo *info) { + info->Reset = SSSNROMReset; + info->Power = SSSNROMPower; + info->Close = SSSNROMClose; + GameHBIRQHook = SSSNROMIRQHook; + GameStateRestore = StateRestore; + + WRAMSIZE = 16384; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ffe.cpp b/apps/fceux/src/boards/ffe.cpp new file mode 100644 index 00000000..51e591ee --- /dev/null +++ b/apps/fceux/src/boards/ffe.cpp @@ -0,0 +1,154 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * FFE Copier Mappers + * + */ + +#include "mapinc.h" + +static uint8 preg[4], creg[8], latch, ffemode; +static uint8 IRQa, mirr; +static int32 IRQCount, IRQLatch; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { preg, 4, "PREG" }, + { creg, 8, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { &IRQLatch, 4, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0x10, 0x6000, 0); + if (ffemode) { + int i; + for (i = 0; i < 8; i++) setchr1(i << 10, creg[i]); + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, preg[3]); + } else { + setchr8(latch & 3); + setprg16(0x8000, (latch >> 2) & 0x3F); + setprg16(0xc000, 0x7); + } + switch (mirr) { + case 0: setmirror(MI_0); break; + case 1: setmirror(MI_1); break; + case 2: setmirror(MI_V); break; + case 3: setmirror(MI_H); break; + } +} + +static DECLFW(FFEWriteMirr) { + mirr = ((A << 1) & 2) | ((V >> 4) & 1); + Sync(); +} + +static DECLFW(FFEWriteIRQ) { + switch (A) { + case 0x4501: IRQa = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x4502: IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x4503: IRQCount &= 0x00FF; IRQCount |= V << 8; IRQa = 1; X6502_IRQEnd(FCEU_IQEXT); break; + } +} + +static DECLFW(FFEWritePrg) { + preg[A & 3] = V; + Sync(); +} + +static DECLFW(FFEWriteChr) { + creg[A & 7] = V; + Sync(); +} + +static DECLFW(FFEWriteLatch) { + latch = V; + Sync(); +} + +static void FFEPower(void) { + preg[3] = ~0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x42FE, 0x42FF, FFEWriteMirr); + SetWriteHandler(0x4500, 0x4503, FFEWriteIRQ); + SetWriteHandler(0x4504, 0x4507, FFEWritePrg); + SetWriteHandler(0x4510, 0x4517, FFEWriteChr); + SetWriteHandler(0x4510, 0x4517, FFEWriteChr); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, FFEWriteLatch); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void FFEIRQHook(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount >= 0x10000) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + IRQCount = 0; + } + } +} + +static void FFEClose(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper6_Init(CartInfo *info) { + ffemode = 0; + mirr = ((info->mirror & 1) ^ 1) | 2; + + info->Power = FFEPower; + info->Close = FFEClose; + MapIRQHook = FFEIRQHook; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper17_Init(CartInfo *info) { + ffemode = 1; + Mapper6_Init(info); +} diff --git a/apps/fceux/src/boards/fk23c.cpp b/apps/fceux/src/boards/fk23c.cpp new file mode 100644 index 00000000..f8e6856a --- /dev/null +++ b/apps/fceux/src/boards/fk23c.cpp @@ -0,0 +1,284 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" +#include "../ines.h" + +static bool is_BMCFK23CA; +static uint8 unromchr; +static uint32 dipswitch; +static uint8 *CHRRAM=NULL; +static uint32 CHRRAMSize; + +static void BMCFK23CCW(uint32 A, uint8 V) +{ + if(EXPREGS[0]&0x40) + setchr8(EXPREGS[2]|unromchr); + else if(EXPREGS[0]&0x20) { + setchr1r(0x10, A, V); + } else { + uint16 base=(EXPREGS[2]&0x7F)<<3; + if(EXPREGS[3]&2) { + int cbase=(MMC3_cmd&0x80)<<5; + setchr1(A,V|base); + setchr1(0x0000^cbase,DRegBuf[0]|base); + setchr1(0x0400^cbase,EXPREGS[6]|base); + setchr1(0x0800^cbase,DRegBuf[1]|base); + setchr1(0x0c00^cbase,EXPREGS[7]|base); + } else + setchr1(A,V|base); + } +} + +//some games are wired differently, and this will need to be changed. +//all the WXN games require prg_bonus = 1, and cah4e3's multicarts require prg_bonus = 0 +//we'll populate this from a game database +static int prg_bonus; +static int prg_mask; + +//prg_bonus = 0 +//4-in-1 (FK23C8021)[p1][!].nes +//4-in-1 (FK23C8033)[p1][!].nes +//4-in-1 (FK23C8043)[p1][!].nes +//4-in-1 (FK23Cxxxx, S-0210A PCB)[p1][!].nes + +//prg_bonus = 1 +//[m176]大富ç¿2-上海大亨.wxn.nes +//[m176]宠物翡翠.fix.nes +//[m176]格兰å¸äºš.wxn.nes +//[m176]梦幻之星.wxn.nes +//[m176]水浒神兽.fix.nes +//[m176]西楚霸王.fix.nes +//[m176]超级大富ç¿.wxn.nes +//[m176]雄霸天下.wxn.nes + +//works as-is under virtuanes m176 +//[m176]三侠五义.wxn.nes +//[m176]å£è¢‹é‡‘.fix.nes +//[m176]爆笑三国.fix.nes + +//needs other tweaks +//[m176]三国忠烈传.wxn.nes +//[m176]破釜沉舟.fix.nes + +//PRG wrapper +static void BMCFK23CPW(uint32 A, uint8 V) +{ +// FCEU_printf("0:%04X:%02X\n",A,V); + + if((EXPREGS[0]&7)==4) + setprg32(0x8000,EXPREGS[1]>>1); + else if ((EXPREGS[0]&7)==3) { + setprg16(0x8000,EXPREGS[1]); + setprg16(0xC000,EXPREGS[1]); + } else { + if(EXPREGS[0]&3) { + uint32 blocksize = (6)-(EXPREGS[0]&3); + uint32 mask = (1<Power=BMCFK23CPower; + info->Reset=BMCFK23CReset; + AddExState(EXPREGS, 8, 0, "EXPR"); + AddExState(&unromchr, 1, 0, "UCHR"); + AddExState(&dipswitch, 1, 0, "DPSW"); + + prg_bonus = 1; + if(MasterRomInfoParams.find("bonus") != MasterRomInfoParams.end()) + prg_bonus = atoi(MasterRomInfoParams["bonus"].c_str()); + + prg_mask = 0x7F>>(prg_bonus); +} + +void BMCFK23CA_Init(CartInfo *info) +{ + is_BMCFK23CA = true; + + GenMMC3_Init(info, 512, 256, 8, 0); + cwrap=BMCFK23CCW; + pwrap=BMCFK23CPW; + info->Power=BMCFK23CAPower; + info->Reset=BMCFK23CReset; + info->Close=BMCFK23CAClose; + + CHRRAMSize=8192; + CHRRAM=(uint8*)FCEU_gmalloc(CHRRAMSize); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSize, 1); + AddExState(CHRRAM, CHRRAMSize, 0, "CRAM"); + + AddExState(EXPREGS, 8, 0, "EXPR"); + AddExState(&unromchr, 1, 0, "UCHR"); + AddExState(&dipswitch, 1, 0, "DPSW"); + + prg_bonus = 1; + if(MasterRomInfoParams.find("bonus") != MasterRomInfoParams.end()) + prg_bonus = atoi(MasterRomInfoParams["bonus"].c_str()); + prg_mask = 0x7F>>(prg_bonus); +} diff --git a/apps/fceux/src/boards/ghostbusters63in1.cpp b/apps/fceux/src/boards/ghostbusters63in1.cpp new file mode 100644 index 00000000..5e09d9be --- /dev/null +++ b/apps/fceux/src/boards/ghostbusters63in1.cpp @@ -0,0 +1,96 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * 63in1 ghostbusters + */ + +#include "mapinc.h" + +static uint8 reg[2], bank; +static uint8 banks[4] = { 0, 0, 1, 2 }; +static uint8 *CHRROM = NULL; +static uint32 CHRROMSIZE; + +static SFORMAT StateRegs[] = +{ + { reg, 2, "REGS" }, + { &bank, 1, "BANK" }, + { 0 } +}; + +static void Sync(void) { + if (reg[0] & 0x20) { + setprg16r(banks[bank], 0x8000, reg[0] & 0x1F); + setprg16r(banks[bank], 0xC000, reg[0] & 0x1F); + } else + setprg32r(banks[bank], 0x8000, (reg[0] >> 1) & 0x0F); + if (reg[1] & 2) + setchr8r(0x10, 0); + else + setchr8(0); + setmirror((reg[0] & 0x40) >> 6); +} + +static DECLFW(BMCGhostbusters63in1Write) { + reg[A & 1] = V; + bank = ((reg[0] & 0x80) >> 7) | ((reg[1] & 1) << 1); +// FCEU_printf("reg[0]=%02x, reg[1]=%02x, bank=%02x\n",reg[0],reg[1],bank); + Sync(); +} + +static DECLFR(BMCGhostbusters63in1Read) { + if (bank == 1) + return X.DB; + else + return CartBR(A); +} + +static void BMCGhostbusters63in1Power(void) { + reg[0] = reg[1] = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, BMCGhostbusters63in1Read); + SetWriteHandler(0x8000, 0xFFFF, BMCGhostbusters63in1Write); +} + +static void BMCGhostbusters63in1Reset(void) { + reg[0] = reg[1] = 0; +} + +static void StateRestore(int version) { + Sync(); +} + +static void BMCGhostbusters63in1Close(void) { + if (CHRROM) + FCEU_gfree(CHRROM); + CHRROM = NULL; +} + +void BMCGhostbusters63in1_Init(CartInfo *info) { + info->Reset = BMCGhostbusters63in1Reset; + info->Power = BMCGhostbusters63in1Power; + info->Close = BMCGhostbusters63in1Close; + + CHRROMSIZE = 8192; // dummy CHRROM, VRAM disable + CHRROM = (uint8*)FCEU_gmalloc(CHRROMSIZE); + SetupCartPRGMapping(0x10, CHRROM, CHRROMSIZE, 0); + AddExState(CHRROM, CHRROMSIZE, 0, "CROM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/gs-2004.cpp b/apps/fceux/src/boards/gs-2004.cpp new file mode 100644 index 00000000..43f70684 --- /dev/null +++ b/apps/fceux/src/boards/gs-2004.cpp @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg, mirr; +static SFORMAT StateRegs[] = +{ + { ®, 1, "REGS" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(1, 0x6000, 0); + setprg32(0x8000, reg); + setchr8(0); +} + +static DECLFW(BMCGS2004Write) { + reg = V; + Sync(); +} + +static void BMCGS2004Power(void) { + reg = ~0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, BMCGS2004Write); +} + +static void BMCGS2004Reset(void) { + reg = ~0; +} + +static void StateRestore(int version) { + Sync(); +} + +void BMCGS2004_Init(CartInfo *info) { + info->Reset = BMCGS2004Reset; + info->Power = BMCGS2004Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/gs-2013.cpp b/apps/fceux/src/boards/gs-2013.cpp new file mode 100644 index 00000000..704a8cf1 --- /dev/null +++ b/apps/fceux/src/boards/gs-2013.cpp @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg, mirr; +static SFORMAT StateRegs[] = +{ + { ®, 1, "REGS" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0, 0x6000, ~0); + setprg32r((reg & 8) >> 3, 0x8000, reg); + setchr8(0); +} + +static DECLFW(BMCGS2013Write) { + reg = V; + Sync(); +} + +static void BMCGS2013Power(void) { + reg = ~0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, BMCGS2013Write); +} + +static void BMCGS2013Reset(void) { + reg = ~0; +} + +static void StateRestore(int version) { + Sync(); +} + +void BMCGS2013_Init(CartInfo *info) { + info->Reset = BMCGS2013Reset; + info->Power = BMCGS2013Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/h2288.cpp b/apps/fceux/src/boards/h2288.cpp new file mode 100644 index 00000000..07ff76c1 --- /dev/null +++ b/apps/fceux/src/boards/h2288.cpp @@ -0,0 +1,70 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +extern uint8 m114_perm[8]; + +static void H2288PW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x40) { + uint8 bank = (EXPREGS[0] & 5) | ((EXPREGS[0] & 8) >> 2) | ((EXPREGS[0] & 0x20) >> 2); + if (EXPREGS[0] & 2) + setprg32(0x8000, bank >> 1); + else{ + setprg16(0x8000, bank); + setprg16(0xC000, bank); + } + } else + setprg8(A, V & 0x3F); +} + +static DECLFW(H2288WriteHi) { + switch (A & 0x8001) { + case 0x8000: MMC3_CMDWrite(0x8000, (V & 0xC0) | (m114_perm[V & 7])); break; + case 0x8001: MMC3_CMDWrite(0x8001, V); break; + } +} + +static DECLFW(H2288WriteLo) { + if (A & 0x800) { + if (A & 1) + EXPREGS[1] = V; + else + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + } +} + +static void H2288Power(void) { + EXPREGS[0] = EXPREGS[1] = 0; + GenMMC3Power(); +// SetReadHandler(0x5000,0x5FFF,H2288Read); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x5000, 0x5FFF, H2288WriteLo); + SetWriteHandler(0x8000, 0x9FFF, H2288WriteHi); +} + +void UNLH2288_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + pwrap = H2288PW; + info->Power = H2288Power; + AddExState(EXPREGS, 2, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/hp10xx_hp20xx.cpp b/apps/fceux/src/boards/hp10xx_hp20xx.cpp new file mode 100644 index 00000000..76276e96 --- /dev/null +++ b/apps/fceux/src/boards/hp10xx_hp20xx.cpp @@ -0,0 +1,177 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2017 CaH4e3 + * + * 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 + * + * HP10xx/HP20xx - a simplified version of FK23C mapper with pretty strict and better + * organized banking behaviour. It seems that some 176 mapper assigned game may be + * actually this kind of board instead but in common they aren't compatible at all, + * the games on the regular FK23C boards couldn't run on this mapper and vice versa... + * + */ + +#include "mapinc.h" +#include "mmc3.h" +#include "../ines.h" + +static uint8 unromchr, lock; +static uint32 dipswitch; + +static void BMCHPxxCW(uint32 A, uint8 V) +{ + if(EXPREGS[0]&4) { // custom banking + switch(EXPREGS[0]&3) { + case 0: + case 1: + setchr8(EXPREGS[2]&0x3F); +// FCEU_printf("\tCHR8 %02X\n",EXPREGS[2]&0x3F); + break; + case 2: + setchr8((EXPREGS[2]&0x3E)|(unromchr&1)); +// FCEU_printf("\tCHR8 %02X\n",(EXPREGS[2]&0x3E)|(unromchr&1)); + break; + case 3: + setchr8((EXPREGS[2]&0x3C)|(unromchr&3)); +// FCEU_printf("\tCHR8 %02X\n",(EXPREGS[2]&0x3C)|(unromchr&3)); + break; + } + } else { // mmc3 banking + int base, mask; + if(EXPREGS[0]&1) { // 128K mode + base=EXPREGS[2]&0x30; + mask=0x7F; + } else { // 256K mode + base=EXPREGS[2]&0x20; + mask=0xFF; + } +// FCEU_printf("\tCHR1 %04x:%02X\n",A,(V&mask)|(base<<3)); + setchr1(A,(V&mask)|(base<<3)); + } +} + +//PRG wrapper +static void BMCHPxxPW(uint32 A, uint8 V) +{ + if(EXPREGS[0]&4) { // custom banking + if((EXPREGS[0]&0xF)==4) { // 16K mode +// FCEU_printf("\tPRG16 %02X\n",EXPREGS[1]&0x1F); + setprg16(0x8000,EXPREGS[1]&0x1F); + setprg16(0xC000,EXPREGS[1]&0x1F); + } else { // 32K modes +// FCEU_printf("\tPRG32 %02X\n",(EXPREGS[1]&0x1F)>>1); + setprg32(0x8000,(EXPREGS[1]&0x1F)>>1); + } + } else { // mmc3 banking + int base, mask; + if(EXPREGS[0]&2) { // 128K mode + base=EXPREGS[1]&0x18; + mask=0x0F; + } else { // 256K mode + base=EXPREGS[1]&0x10; + mask=0x1F; + } +// FCEU_printf("\tPRG8 %02X\n",(V&mask)|(base<<1)); + setprg8(A,(V&mask)|(base<<1)); + setprg8r(0x10,0x6000,A001B&3); + } +} + +//MIRROR wrapper +static void BMCHPxxMW(uint8 V) { + if(EXPREGS[0]&4) { // custom banking +// FCEU_printf("CUSTOM MIRR: %d\n",(unromchr>>2)&1); + setmirror(((unromchr>>2)&1)^1); + } else { // mmc3 banking +// FCEU_printf("MMC3 MIRR: %d\n",(V&1)^1); + A000B = V; + setmirror((A000B & 1) ^ 1); + } +} + +//PRG handler ($8000-$FFFF) +static DECLFW(BMCHPxxHiWrite) +{ +// FCEU_printf("HI WRITE %04X:%02X\n",A,V); + if(EXPREGS[0]&4) { // custom banking +// FCEU_printf("CUSTOM\n"); + unromchr=V; + FixMMC3CHR(MMC3_cmd); + } else { // mmc3 banking +// FCEU_printf("MMC3\n"); + if(A<0xC000) { + MMC3_CMDWrite(A,V); + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else { + MMC3_IRQWrite(A,V); + } + } +} + +//EXP handler ($5000-$5FFF) +static DECLFW(BMCHPxxWrite) +{ + if(!lock) { +// FCEU_printf("LO WRITE %04X:%02X\n",A,V); + EXPREGS[A&3]=V; + lock=V&0x80; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +} + +static DECLFR(BMCHPxxRead) { + return dipswitch; +} + +static void BMCHPxxReset(void) +{ + dipswitch++; + dipswitch&=0xF; + lock=0; +// FCEU_printf("BMCHPxx dipswitch set to %d\n",dipswitch); + EXPREGS[0]=EXPREGS[1]=EXPREGS[2]=EXPREGS[3]=0; + MMC3RegReset(); + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void BMCHPxxPower(void) +{ + GenMMC3Power(); + dipswitch=lock=0; + EXPREGS[0]=EXPREGS[1]=EXPREGS[2]=EXPREGS[3]=0; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + SetReadHandler(0x5000,0x5fff,BMCHPxxRead); + SetWriteHandler(0x5000,0x5fff,BMCHPxxWrite); + SetWriteHandler(0x8000,0xffff,BMCHPxxHiWrite); +} + +void BMCHPxx_Init(CartInfo *info) +{ + GenMMC3_Init(info, 256, 256, 8, 0); + cwrap=BMCHPxxCW; + pwrap=BMCHPxxPW; + mwrap=BMCHPxxMW; + info->Power=BMCHPxxPower; + info->Reset=BMCHPxxReset; + AddExState(EXPREGS, 8, 0, "EXPR"); + AddExState(&unromchr, 1, 0, "UCHR"); + AddExState(&dipswitch, 1, 0, "DPSW"); + AddExState(&lock, 1, 0, "LOCK"); +} diff --git a/apps/fceux/src/boards/hp898f.cpp b/apps/fceux/src/boards/hp898f.cpp new file mode 100644 index 00000000..b0471bb5 --- /dev/null +++ b/apps/fceux/src/boards/hp898f.cpp @@ -0,0 +1,69 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2015 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 regs[2]; + +static SFORMAT StateRegs[] = +{ + { regs, 2, "REGS" }, + { 0 } +}; + +static void Sync(void) { + uint8 chr = (regs[0] >> 4) & 7; + uint8 prg = (regs[1] >> 3) & 7; + uint8 dec = (regs[1] >> 4) & 4; + setchr8(chr & (~(((regs[0] & 1) << 2) | (regs[0] & 2)))); + setprg16(0x8000,prg & (~dec)); + setprg16(0xC000,prg | dec); + setmirror(regs[1] >> 7); +} + +static DECLFW(HP898FWrite) { + if((A & 0x6000) == 0x6000) { + regs[(A & 4) >> 2] = V; + Sync(); + } +} + +static void HP898FPower(void) { + regs[0] = regs[1] = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0xFFFF, HP898FWrite); +} + +static void HP898FReset(void) { + regs[0] = regs[1] = 0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void BMCHP898F_Init(CartInfo *info) { + info->Reset = HP898FReset; + info->Power = HP898FPower; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/inlnsf.cpp b/apps/fceux/src/boards/inlnsf.cpp new file mode 100644 index 00000000..c9b9fd6a --- /dev/null +++ b/apps/fceux/src/boards/inlnsf.cpp @@ -0,0 +1,62 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 regs[8]; + +static SFORMAT StateRegs[] = +{ + { regs, 8, "REGS" }, + { 0 } +}; + +static void Sync(void) { + for (int i=0; i < 8; ++i) + { + setprg4(0x8000 + (0x1000 * i), regs[i]); + } +} + +static DECLFW(M31Write) { + if (A >= 0x5000 && A <= 0x5FFF) + { + regs[A&7] = V; + Sync(); + } +} + +static void M31Power(void) { + setchr8(0); + regs[7] = 0xFF; + Sync(); + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x5000, 0x5fff, M31Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper31_Init(CartInfo *info) { + info->Power = M31Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/karaoke.cpp b/apps/fceux/src/boards/karaoke.cpp new file mode 100644 index 00000000..81d9dbc8 --- /dev/null +++ b/apps/fceux/src/boards/karaoke.cpp @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +extern uint32 ROM_size; +static uint8 latche; + +static void Sync(void) { + if (latche) { + if (latche & 0x10) + setprg16(0x8000, (latche & 7)); + else + setprg16(0x8000, (latche & 7) | 8); + } else + setprg16(0x8000, 7 + (ROM_size >> 4)); +} + +static DECLFW(M188Write) { + latche = V; + Sync(); +} + +static DECLFR(ExtDev) { + return(3); +} + +static void Power(void) { + latche = 0; + Sync(); + setchr8(0); + setprg16(0xc000, 0x7); + SetReadHandler(0x6000, 0x7FFF, ExtDev); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M188Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper188_Init(CartInfo *info) { + info->Power = Power; + GameStateRestore = StateRestore; + AddExState(&latche, 1, 0, "LATC"); +} diff --git a/apps/fceux/src/boards/kof97.cpp b/apps/fceux/src/boards/kof97.cpp new file mode 100644 index 00000000..4a7925ac --- /dev/null +++ b/apps/fceux/src/boards/kof97.cpp @@ -0,0 +1,46 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static DECLFW(UNLKOF97CMDWrite) { + V = (V & 0xD8) | ((V & 0x20) >> 4) | ((V & 4) << 3) | ((V & 2) >> 1) | ((V & 1) << 2); //76143502 + if (A == 0x9000) A = 0x8001; + MMC3_CMDWrite(A, V); +} + +static DECLFW(UNLKOF97IRQWrite) { + V = (V & 0xD8) | ((V & 0x20) >> 4) | ((V & 4) << 3) | ((V & 2) >> 1) | ((V & 1) << 2); + if (A == 0xD000) A = 0xC001; + else if (A == 0xF000) A = 0xE001; + MMC3_IRQWrite(A, V); +} + +static void UNLKOF97Power(void) { + GenMMC3Power(); + SetWriteHandler(0x8000, 0xA000, UNLKOF97CMDWrite); + SetWriteHandler(0xC000, 0xF000, UNLKOF97IRQWrite); +} + +void UNLKOF97_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 0, 0); + info->Power = UNLKOF97Power; +} diff --git a/apps/fceux/src/boards/ks7010.cpp b/apps/fceux/src/boards/ks7010.cpp new file mode 100644 index 00000000..7adbe89c --- /dev/null +++ b/apps/fceux/src/boards/ks7010.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg[4], creg, mirr; + +static SFORMAT StateRegs[] = +{ + { preg, 4, "PREG" }, + { &creg, 1, "CREG" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, preg[0]); + setprg8(0x8000, 0xa); + setprg8(0xa000, 0xb); + setprg8(0xc000, 0x6); + setprg8(0xe000, 0x7); + setchr8(0x0c); + setmirror(mirr); +} + +static DECLFW(UNLKS7010Write) { + switch (A) { + case 0x4025: mirr = (((V >> 3) & 1) ^ 1); Sync(); break; + default: + FCEU_printf("bs %04x %02x\n",A,V); + break; + } +} + +static void UNLKS7010Reset(void) { + preg[0]++; + if(preg[0] == 0x10) { + preg[0] = 0; + preg[1]++; + if(preg[1] == 0x10) { + preg[1] = 0; + preg[2]++; + } + } + FCEU_printf("preg %02x %02x %02x\n",preg[0], preg[1], preg[2]); + Sync(); +} + +static void UNLKS7010Power(void) { + preg[0] = preg[1] = preg[2] = 0; + Sync(); + SetReadHandler(0x6000, 0x7fff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x4020, 0xffff, UNLKS7010Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7010_Init(CartInfo *info) { + info->Power = UNLKS7010Power; + info->Reset = UNLKS7010Reset; + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7012.cpp b/apps/fceux/src/boards/ks7012.cpp new file mode 100644 index 00000000..49def232 --- /dev/null +++ b/apps/fceux/src/boards/ks7012.cpp @@ -0,0 +1,84 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0x10, 0x6000, 0); + setprg32(0x8000, reg & 1); + setchr8(0); +} + +static DECLFW(UNLKS7012Write) { +// FCEU_printf("bs %04x %02x\n",A,V); + switch (A) { + case 0xE0A0: reg = 0; Sync(); break; + case 0xEE36: reg = 1; Sync(); break; + } +} + +static void UNLKS7012Power(void) { + reg = ~0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLKS7012Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void UNLKS7012Reset(void) { + reg = ~0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +static void UNLKS7012Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void UNLKS7012_Init(CartInfo *info) { + info->Power = UNLKS7012Power; + info->Reset = UNLKS7012Reset; + info->Close = UNLKS7012Close; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7013.cpp b/apps/fceux/src/boards/ks7013.cpp new file mode 100644 index 00000000..fe8349e9 --- /dev/null +++ b/apps/fceux/src/boards/ks7013.cpp @@ -0,0 +1,77 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + * + * Just another pirate cart with pirate mapper, instead of original MMC1 + * Kaiser Highway Star + * + */ + +#include "mapinc.h" + +static uint8 reg, mirr; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REGS" }, + { &mirr, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg16(0x8000, reg); + setprg16(0xc000, ~0); + setmirror(mirr); + setchr8(0); +} + +static DECLFW(UNLKS7013BLoWrite) { + reg = V; + Sync(); +} + +static DECLFW(UNLKS7013BHiWrite) { + mirr = (V & 1) ^ 1; + Sync(); +} + +static void UNLKS7013BPower(void) { + reg = 0; + mirr = 0; + Sync(); + SetWriteHandler(0x6000, 0x7FFF, UNLKS7013BLoWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLKS7013BHiWrite); +} + +static void UNLKS7013BReset(void) { + reg = 0; + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7013B_Init(CartInfo *info) { + info->Power = UNLKS7013BPower; + info->Reset = UNLKS7013BReset; + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7016.cpp b/apps/fceux/src/boards/ks7016.cpp new file mode 100644 index 00000000..3e856202 --- /dev/null +++ b/apps/fceux/src/boards/ks7016.cpp @@ -0,0 +1,85 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2016 CaH4e3 + * + * 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 + * + * FDS Conversion (Exciting Basket), weird banking addressing, seems because + * of used addressing scheme, made to disable the lower system banks from 6000 + * but the kaiser mapper chip and PCB are the same as usual + * probably need a hard eprom dump to verify actual banks layout + * + */ + +#include "mapinc.h" + +static uint8 preg; + +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, preg); + setprg8(0x8000, 0xC); + setprg8(0xA000, 0xD); + setprg8(0xC000, 0xE); + setprg8(0xE000, 0xF); + setchr8(0); +} + +static DECLFW(UNLKS7016Write) { + u16 mask = (A & 0x30); + switch(A & 0xD943) { + case 0xD943: { + if(mask == 0x30) { + preg = 8 | 3; // or A, or no bus (all FF) + } else { + preg = (A >> 2) & 0xF; // can be anything but C-F + } + Sync(); + break; + } + case 0xD903: { // this case isn't usedby the game, but addressing does this as a side effect + if(mask == 0x30) { + preg = 8 | ((A >> 2) & 3); // also masked C-F from output + } else { + preg = 8 | 3; + } + Sync(); + break; + } + } +} + +static void UNLKS7016Power(void) { + preg = 8; + Sync(); + SetReadHandler(0x6000, 0xffff, CartBR); + SetWriteHandler(0x8000, 0xffff, UNLKS7016Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7016_Init(CartInfo *info) { + info->Power = UNLKS7016Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7017.cpp b/apps/fceux/src/boards/ks7017.cpp new file mode 100644 index 00000000..3f119cf6 --- /dev/null +++ b/apps/fceux/src/boards/ks7017.cpp @@ -0,0 +1,115 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg, mirr; +static int32 IRQa, IRQCount, IRQLatch; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { &mirr, 1, "MIRR" }, + { ®, 1, "REGS" }, + { &IRQa, 4, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { &IRQLatch, 4, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + setprg16(0x8000, reg); + setprg16(0xC000, 2); + setmirror(mirr); +} + +static DECLFW(UNLKS7017Write) { +// FCEU_printf("bs %04x %02x\n",A,V); + if ((A & 0xFF00) == 0x4A00) { + reg = ((A >> 2) & 3) | ((A >> 4) & 4); + } else if ((A & 0xFF00) == 0x5100) { + Sync(); + } else if (A == 0x4020) { + X6502_IRQEnd(FCEU_IQEXT); + IRQCount &= 0xFF00; + IRQCount |= V; + } else if (A == 0x4021) { + X6502_IRQEnd(FCEU_IQEXT); + IRQCount &= 0xFF; + IRQCount |= V << 8; + IRQa = 1; + } else if (A == 0x4025) { + mirr = ((V & 8) >> 3) ^ 1; + } +} +static DECLFR(FDSRead4030) { + X6502_IRQEnd(FCEU_IQEXT); + return X.IRQlow & FCEU_IQEXT ? 1 : 0; +} + +static void UNL7017IRQ(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount <= 0) { + IRQa = 0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void UNLKS7017Power(void) { + Sync(); + setchr8(0); + setprg8r(0x10, 0x6000, 0); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0x4030, 0x4030, FDSRead4030); + SetWriteHandler(0x4020, 0x5FFF, UNLKS7017Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void UNLKS7017Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7017_Init(CartInfo *info) { + info->Power = UNLKS7017Power; + info->Close = UNLKS7017Close; + MapIRQHook = UNL7017IRQ; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7030.cpp b/apps/fceux/src/boards/ks7030.cpp new file mode 100644 index 00000000..8e6a592e --- /dev/null +++ b/apps/fceux/src/boards/ks7030.cpp @@ -0,0 +1,140 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * FDS Conversion + * + * Logical bank layot 32 K BANK 0, 64K BANK 1, 32K ~0 hardwired, 8K is missing + * need redump from MASKROM! + * probably need refix mapper after hard dump + * + */ + +#include "mapinc.h" + +static uint8 reg0, reg1; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®0, 1, "REG0" }, + { ®1, 1, "REG1" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg32(0x8000, ~0); + setprg4(0xb800, reg0); + setprg4(0xc800, 8 + reg1); +} + +// 6000 - 6BFF - RAM +// 6C00 - 6FFF - BANK 1K REG1 +// 7000 - 7FFF - BANK 4K REG0 + +static DECLFW(UNLKS7030RamWrite0) { + if ((A >= 0x6000) && A <= 0x6BFF) { + WRAM[A - 0x6000] = V; + } else if ((A >= 0x6C00) && A <= 0x6FFF) { + CartBW(0xC800 + (A - 0x6C00), V); + } else if ((A >= 0x7000) && A <= 0x7FFF) { + CartBW(0xB800 + (A - 0x7000), V); + } +} + +static DECLFR(UNLKS7030RamRead0) { + if ((A >= 0x6000) && A <= 0x6BFF) { + return WRAM[A - 0x6000]; + } else if ((A >= 0x6C00) && A <= 0x6FFF) { + return CartBR(0xC800 + (A - 0x6C00)); + } else if ((A >= 0x7000) && A <= 0x7FFF) { + return CartBR(0xB800 + (A - 0x7000)); + } + return 0; +} + +// B800 - BFFF - RAM +// C000 - CBFF - BANK 3K +// CC00 - D7FF - RAM + +static DECLFW(UNLKS7030RamWrite1) { + if ((A >= 0xB800) && A <= 0xBFFF) { + WRAM[0x0C00 + (A - 0xB800)] = V; + } else if ((A >= 0xC000) && A <= 0xCBFF) { + CartBW(0xCC00 + (A - 0xC000), V); + } else if ((A >= 0xCC00) && A <= 0xD7FF) { + WRAM[0x1400 + (A - 0xCC00)] = V; + } +} + +static DECLFR(UNLKS7030RamRead1) { + if ((A >= 0xB800) && A <= 0xBFFF) { + return WRAM[0x0C00 + (A - 0xB800)]; + } else if ((A >= 0xC000) && A <= 0xCBFF) { + return CartBR(0xCC00 + (A - 0xC000)); + } else if ((A >= 0xCC00) && A <= 0xD7FF) { + return WRAM[0x1400 + (A - 0xCC00)]; + } + return 0; +} + +static DECLFW(UNLKS7030Write0) { + reg0 = A & 7; + Sync(); +} + +static DECLFW(UNLKS7030Write1) { + reg1 = A & 15; + Sync(); +} + +static void UNLKS7030Power(void) { + reg0 = reg1 = ~0; + Sync(); + SetReadHandler(0x6000, 0x7FFF, UNLKS7030RamRead0); + SetWriteHandler(0x6000, 0x7FFF, UNLKS7030RamWrite0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0x8FFF, UNLKS7030Write0); + SetWriteHandler(0x9000, 0x9FFF, UNLKS7030Write1); + SetReadHandler(0xB800, 0xD7FF, UNLKS7030RamRead1); + SetWriteHandler(0xB800, 0xD7FF, UNLKS7030RamWrite1); +} + +static void UNLKS7030Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7030_Init(CartInfo *info) { + info->Power = UNLKS7030Power; + info->Close = UNLKS7030Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7031.cpp b/apps/fceux/src/boards/ks7031.cpp new file mode 100644 index 00000000..f9944340 --- /dev/null +++ b/apps/fceux/src/boards/ks7031.cpp @@ -0,0 +1,80 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg[4]; + +static SFORMAT StateRegs[] = +{ + { reg, 4, "REGS" }, + { 0 } +}; + +static void Sync(void) { + setprg2(0x6000, reg[0]); + setprg2(0x6800, reg[1]); + setprg2(0x7000, reg[2]); + setprg2(0x7800, reg[3]); + + setprg2(0x8000, 15); + setprg2(0x8800, 14); + setprg2(0x9000, 13); + setprg2(0x9800, 12); + setprg2(0xa000, 11); + setprg2(0xa800, 10); + setprg2(0xb000, 9); + setprg2(0xb800, 8); + + setprg2(0xc000, 7); + setprg2(0xc800, 6); + setprg2(0xd000, 5); + setprg2(0xd800, 4); + setprg2(0xe000, 3); + setprg2(0xe800, 2); + setprg2(0xf000, 1); + setprg2(0xf800, 0); + + setchr8(0); +} + +static DECLFW(UNLKS7031Write) { + reg[(A >> 11) & 3] = V; + Sync(); +} + +static void UNLKS7031Power(void) { + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xffff, UNLKS7031Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7031_Init(CartInfo *info) { + info->Power = UNLKS7031Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7032.cpp b/apps/fceux/src/boards/ks7032.cpp new file mode 100644 index 00000000..71d4c5f8 --- /dev/null +++ b/apps/fceux/src/boards/ks7032.cpp @@ -0,0 +1,88 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 reg[8], cmd, IRQa = 0, isirqused = 0; +static int32 IRQCount; + +static SFORMAT StateRegs[] = +{ + { &cmd, 1, "CMD" }, + { reg, 8, "REGS" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, reg[4]); + setprg8(0x8000, reg[1]); + setprg8(0xA000, reg[2]); + setprg8(0xC000, reg[3]); + setprg8(0xE000, ~0); + setchr8(0); +} + +static DECLFW(UNLKS7032Write) { +// FCEU_printf("bs %04x %02x\n",A,V); + switch (A & 0xF000) { +// case 0x8FFF: reg[4]=V; Sync(); break; + case 0x8000: X6502_IRQEnd(FCEU_IQEXT); IRQCount = (IRQCount & 0x000F) | (V & 0x0F); isirqused = 1; break; + case 0x9000: X6502_IRQEnd(FCEU_IQEXT); IRQCount = (IRQCount & 0x00F0) | ((V & 0x0F) << 4); isirqused = 1; break; + case 0xA000: X6502_IRQEnd(FCEU_IQEXT); IRQCount = (IRQCount & 0x0F00) | ((V & 0x0F) << 8); isirqused = 1; break; + case 0xB000: X6502_IRQEnd(FCEU_IQEXT); IRQCount = (IRQCount & 0xF000) | (V << 12); isirqused = 1; break; + case 0xC000: if (isirqused) { + X6502_IRQEnd(FCEU_IQEXT); IRQa = 1; + } + break; + case 0xE000: cmd = V & 7; break; + case 0xF000: reg[cmd] = V; Sync(); break; + } +} + +static void UNLSMB2JIRQHook(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount >= 0xFFFF) { + IRQa = 0; + IRQCount = 0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void UNLKS7032Power(void) { + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4020, 0xFFFF, UNLKS7032Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLKS7032_Init(CartInfo *info) { + info->Power = UNLKS7032Power; + MapIRQHook = UNLSMB2JIRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7037.cpp b/apps/fceux/src/boards/ks7037.cpp new file mode 100644 index 00000000..232e7583 --- /dev/null +++ b/apps/fceux/src/boards/ks7037.cpp @@ -0,0 +1,126 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg[8], cmd; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static void (*WSync)(void); + +static SFORMAT StateRegs[] = +{ + { &cmd, 1, "CMD" }, + { reg, 8, "REGS" }, + { 0 } +}; + +static void SyncKS7037(void) { + setprg4r(0x10, 0x6000, 0); + setprg4(0x7000, 15); + setprg8(0x8000, reg[6]); + setprg4(0xA000, ~3); + setprg4r(0x10, 0xB000, 1); + setprg8(0xC000, reg[7]); + setprg8(0xE000, ~0); + setchr8(0); + setmirrorw(reg[2] & 1, reg[4] & 1, reg[3] & 1, reg[5] & 1); +} + +static void SyncLH10(void) { + setprg8(0x6000, ~1); + setprg8(0x8000, reg[6]); + setprg8(0xA000, reg[7]); + setprg8r(0x10, 0xC000, 0); + setprg8(0xE000, ~0); + setchr8(0); + setmirror(0); +} + +static DECLFW(UNLKS7037Write) { + switch (A & 0xE001) { + case 0x8000: cmd = V & 7; break; + case 0x8001: reg[cmd] = V; WSync(); break; + } +} + +static void UNLKS7037Power(void) { + reg[0] = reg[1] = reg[2] = reg[3] = reg[4] = reg[5] = reg[6] = reg[7] = 0; + WSync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0x9FFF, UNLKS7037Write); + SetWriteHandler(0xA000, 0xBFFF, CartBW); + SetWriteHandler(0xC000, 0xFFFF, UNLKS7037Write); +} + +static void LH10Power(void) { + reg[0] = reg[1] = reg[2] = reg[3] = reg[4] = reg[5] = reg[6] = reg[7] = 0; + WSync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xBFFF, UNLKS7037Write); + SetWriteHandler(0xC000, 0xDFFF, CartBW); + SetWriteHandler(0xE000, 0xFFFF, UNLKS7037Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + WSync(); +} + +void UNLKS7037_Init(CartInfo *info) { + info->Power = UNLKS7037Power; + info->Close = Close; + + WSync = SyncKS7037; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void LH10_Init(CartInfo *info) { + info->Power = LH10Power; + info->Close = Close; + + WSync = SyncLH10; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/ks7057.cpp b/apps/fceux/src/boards/ks7057.cpp new file mode 100644 index 00000000..a9c47968 --- /dev/null +++ b/apps/fceux/src/boards/ks7057.cpp @@ -0,0 +1,92 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg[8], mirror; +static SFORMAT StateRegs[] = +{ + { reg, 8, "PRG" }, + { &mirror, 1, "MIRR" }, + { 0 } +}; + +static void Sync(void) { + setprg2(0x6000, reg[4]); + setprg2(0x6800, reg[5]); + setprg2(0x7000, reg[6]); + setprg2(0x7800, reg[7]); + setprg2(0x8000, reg[0]); + setprg2(0x8800, reg[1]); + setprg2(0x9000, reg[2]); + setprg2(0x9800, reg[3]); + setprg8(0xA000, 0xd); + setprg16(0xC000, 7); + setchr8(0); + setmirror(mirror); +} + +static DECLFW(UNLKS7057Write) { + switch (A & 0xF003) { + case 0x8000: + case 0x8001: + case 0x8002: + case 0x8003: + case 0x9000: + case 0x9001: + case 0x9002: + case 0x9003: mirror = V & 1; Sync(); break; + case 0xB000: reg[0] = (reg[0] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xB001: reg[0] = (reg[0] & 0x0F) | (V << 4); Sync(); break; + case 0xB002: reg[1] = (reg[1] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xB003: reg[1] = (reg[1] & 0x0F) | (V << 4); Sync(); break; + case 0xC000: reg[2] = (reg[2] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xC001: reg[2] = (reg[2] & 0x0F) | (V << 4); Sync(); break; + case 0xC002: reg[3] = (reg[3] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xC003: reg[3] = (reg[3] & 0x0F) | (V << 4); Sync(); break; + case 0xD000: reg[4] = (reg[4] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xD001: reg[4] = (reg[4] & 0x0F) | (V << 4); Sync(); break; + case 0xD002: reg[5] = (reg[5] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xD003: reg[5] = (reg[5] & 0x0F) | (V << 4); Sync(); break; + case 0xE000: reg[6] = (reg[6] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xE001: reg[6] = (reg[6] & 0x0F) | (V << 4); Sync(); break; + case 0xE002: reg[7] = (reg[7] & 0xF0) | (V & 0x0F); Sync(); break; + case 0xE003: reg[7] = (reg[7] & 0x0F) | (V << 4); Sync(); break; + } +} + +static void UNLKS7057Power(void) { + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLKS7057Write); +} + +static void UNLKS7057Reset(void) { + Sync(); +} + +void UNLKS7057_Init(CartInfo *info) { + info->Power = UNLKS7057Power; + info->Reset = UNLKS7057Reset; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/le05.cpp b/apps/fceux/src/boards/le05.cpp new file mode 100644 index 00000000..efbe13f6 --- /dev/null +++ b/apps/fceux/src/boards/le05.cpp @@ -0,0 +1,62 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2011 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 chr; +static SFORMAT StateRegs[] = +{ + { &chr, 1, "CHR" }, + { 0 } +}; + +static void Sync(void) { + setprg2r(0, 0xE000, 0); + setprg2r(0, 0xE800, 0); + setprg2r(0, 0xF000, 0); + setprg2r(0, 0xF800, 0); + + setprg8r(1, 0x6000, 3); + setprg8r(1, 0x8000, 0); + setprg8r(1, 0xA000, 1); + setprg8r(1, 0xC000, 2); + + setchr8(chr & 1); + setmirror(MI_V); +} + +static DECLFW(LE05Write) { + chr = V; + Sync(); +} + +static void LE05Power(void) { + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, LE05Write); +} + +void LE05_Init(CartInfo *info) { + info->Power = LE05Power; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/lh32.cpp b/apps/fceux/src/boards/lh32.cpp new file mode 100644 index 00000000..39833275 --- /dev/null +++ b/apps/fceux/src/boards/lh32.cpp @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x6000, reg); + setprg8(0x8000, ~3); + setprg8(0xa000, ~2); + setprg8r(0x10, 0xc000, 0); + setprg8(0xe000, ~0); + setchr8(0); +} + +static DECLFW(LH32Write) { + reg = V; + Sync(); +} + +static void LH32Power(void) { + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0xC000, 0xDFFF, CartBW); + SetWriteHandler(0x6000, 0x6000, LH32Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void LH32Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void LH32_Init(CartInfo *info) { + info->Power = LH32Power; + info->Close = LH32Close; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/lh53.cpp b/apps/fceux/src/boards/lh53.cpp new file mode 100644 index 00000000..7065b0ca --- /dev/null +++ b/apps/fceux/src/boards/lh53.cpp @@ -0,0 +1,108 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + * + * FDS Conversion + * + */ + +#include "mapinc.h" + +static uint8 reg, IRQa; +static int32 IRQCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { ®, 1, "REG" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 4, "IRQC" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg8(0x6000, reg); + setprg8(0x8000, 0xc); + setprg4(0xa000, (0xd << 1)); + setprg2(0xb000, (0xd << 2) + 2); + setprg2r(0x10, 0xb800, 4); + setprg2r(0x10, 0xc000, 5); + setprg2r(0x10, 0xc800, 6); + setprg2r(0x10, 0xd000, 7); + setprg2(0xd800, (0xe << 2) + 3); + setprg8(0xe000, 0xf); +} + +static DECLFW(LH53RamWrite) { + WRAM[(A - 0xB800) & 0x1FFF] = V; +} + +static DECLFW(LH53Write) { + reg = V; + Sync(); +} + +static DECLFW(LH53IRQaWrite) { + IRQa = V & 2; + IRQCount = 0; + if (!IRQa) + X6502_IRQEnd(FCEU_IQEXT); +} + +static void LH53IRQ(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount > 7560) + X6502_IRQBegin(FCEU_IQEXT); + } +} + +static void LH53Power(void) { + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0xB800, 0xD7FF, LH53RamWrite); + SetWriteHandler(0xE000, 0xEFFF, LH53IRQaWrite); + SetWriteHandler(0xF000, 0xFFFF, LH53Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void LH53Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void LH53_Init(CartInfo *info) { + info->Power = LH53Power; + info->Close = LH53Close; + MapIRQHook = LH53IRQ; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/malee.cpp b/apps/fceux/src/boards/malee.cpp new file mode 100644 index 00000000..9bf4fa63 --- /dev/null +++ b/apps/fceux/src/boards/malee.cpp @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 WRAM[2048]; + +static void MALEEPower(void) { + setprg2r(0x10, 0x7000, 0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0x6000, 0x67FF, CartBR); + SetReadHandler(0x7000, 0x77FF, CartBR); + SetWriteHandler(0x7000, 0x77FF, CartBW); + setprg2r(1, 0x6000, 0); + setprg32(0x8000, 0); + setchr8(0); +} + +void MALEE_Init(CartInfo *info) { + info->Power = MALEEPower; + FCEU_MemoryRand(WRAM,sizeof(WRAM),true); + SetupCartPRGMapping(0x10, WRAM, 2048, 1); + AddExState(WRAM, 2048, 0, "WRAM"); +} diff --git a/apps/fceux/src/boards/mapinc.h b/apps/fceux/src/boards/mapinc.h new file mode 100644 index 00000000..8fa46f42 --- /dev/null +++ b/apps/fceux/src/boards/mapinc.h @@ -0,0 +1,12 @@ +#include "../types.h" +#include "../utils/memory.h" +#include "../x6502.h" +#include "../fceu.h" +#include "../ppu.h" +#include "../sound.h" +#include "../state.h" +#include "../cart.h" +#include "../cheat.h" +#include "../unif.h" +#include +#include diff --git a/apps/fceux/src/boards/mihunche.cpp b/apps/fceux/src/boards/mihunche.cpp new file mode 100644 index 00000000..512aa0f2 --- /dev/null +++ b/apps/fceux/src/boards/mihunche.cpp @@ -0,0 +1,68 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2013 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint16 latche; + +static SFORMAT StateRegs[] = +{ + { &latche, 2, "LATC" }, + { 0 } +}; + +static void Sync(void) { + setprg32(0x8000, 0); + if(CHRsize[0] == 8192) { + setchr4(0x0000, latche & 1); + setchr4(0x1000, latche & 1); + } else { + setchr8(latche & 1); // actually, my bad, overdumped roms, the real CHR size if 8K + } + setmirror(MI_0 + (latche & 1)); +} + +static DECLFW(UNLCC21Write1) { + latche = A; + Sync(); +} + +static DECLFW(UNLCC21Write2) { + latche = V; + Sync(); +} + +static void UNLCC21Power(void) { + latche = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8001, 0xFFFF, UNLCC21Write1); + SetWriteHandler(0x8000, 0x8000, UNLCC21Write2); // another one many-in-1 mapper, there is a lot of similar carts with little different wirings +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLCC21_Init(CartInfo *info) { + info->Power = UNLCC21Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/mmc1.cpp b/apps/fceux/src/boards/mmc1.cpp new file mode 100644 index 00000000..bfe07a78 --- /dev/null +++ b/apps/fceux/src/boards/mmc1.cpp @@ -0,0 +1,404 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static void GenMMC1Power(void); +static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int bram); + +static uint8 DRegs[4]; +static uint8 Buffer, BufferShift; + +static uint32 WRAMSIZE; +static uint32 NONBRAMSIZE; // size of non-battery-backed portion of WRAM + +static void (*MMC1CHRHook4)(uint32 A, uint8 V); +static void (*MMC1PRGHook16)(uint32 A, uint8 V); + +static uint8 *WRAM = NULL; +static uint8 *CHRRAM = NULL; +static int is155, is171; + +static DECLFW(MBWRAM) { + if (!(DRegs[3] & 0x10) || is155) + Page[A >> 11][A] = V; // WRAM is enabled. +} + +static DECLFR(MAWRAM) { + if ((DRegs[3] & 0x10) && !is155) + return X.DB; // WRAM is disabled + return(Page[A >> 11][A]); +} + +static void MMC1CHR(void) { + if (WRAMSIZE > 0x2000) { + if (WRAMSIZE > 0x4000) + setprg8r(0x10, 0x6000, (DRegs[1] >> 2) & 3); + else + setprg8r(0x10, 0x6000, (DRegs[1] >> 3) & 1); + } + + if (MMC1CHRHook4) { + if (DRegs[0] & 0x10) { + MMC1CHRHook4(0x0000, DRegs[1]); + MMC1CHRHook4(0x1000, DRegs[2]); + } else { + MMC1CHRHook4(0x0000, (DRegs[1] & 0xFE)); + MMC1CHRHook4(0x1000, DRegs[1] | 1); + } + } else { + if (DRegs[0] & 0x10) { + setchr4(0x0000, DRegs[1]); + setchr4(0x1000, DRegs[2]); + } else + setchr8(DRegs[1] >> 1); + } +} + +static void MMC1PRG(void) { + uint8 offs_16banks = DRegs[1] & 0x10; + uint8 prg_reg = DRegs[3] & 0xF; //homebrewers arent allowed to use more banks on MMC1. use another mapper. + if (MMC1PRGHook16) { + switch (DRegs[0] & 0xC) { + case 0xC: + MMC1PRGHook16(0x8000, (prg_reg + offs_16banks)); + MMC1PRGHook16(0xC000, 0xF + offs_16banks); + break; + case 0x8: + MMC1PRGHook16(0xC000, (prg_reg + offs_16banks)); + MMC1PRGHook16(0x8000, offs_16banks); + break; + case 0x0: + case 0x4: + MMC1PRGHook16(0x8000, ((prg_reg & ~1) + offs_16banks)); + MMC1PRGHook16(0xc000, ((prg_reg & ~1) + offs_16banks + 1)); + break; + } + } else { + switch (DRegs[0] & 0xC) { + case 0xC: + setprg16(0x8000, (prg_reg + offs_16banks)); + setprg16(0xC000, 0xF + offs_16banks); + break; + case 0x8: + setprg16(0xC000, (prg_reg + offs_16banks)); + setprg16(0x8000, offs_16banks); + break; + case 0x0: + case 0x4: + setprg16(0x8000, ((prg_reg & ~1) + offs_16banks)); + setprg16(0xc000, ((prg_reg & ~1) + offs_16banks + 1)); + break; + } + } +} + +static void MMC1MIRROR(void) { + if (!is171) + switch (DRegs[0] & 3) { + case 2: setmirror(MI_V); break; + case 3: setmirror(MI_H); break; + case 0: setmirror(MI_0); break; + case 1: setmirror(MI_1); break; + } +} + +static uint64 lreset; +static DECLFW(MMC1_write) { + int n = (A >> 13) - 4; + + /* The MMC1 is busy so ignore the write. */ + /* As of version FCE Ultra 0.81, the timestamp is only + increased before each instruction is executed(in other words + precision isn't that great), but this should still work to + deal with 2 writes in a row from a single RMW instruction. + */ + if ((timestampbase + timestamp) < (lreset + 2)) + return; +// FCEU_printf("Write %04x:%02x\n",A,V); + if (V & 0x80) { + DRegs[0] |= 0xC; + BufferShift = Buffer = 0; + MMC1PRG(); + lreset = timestampbase + timestamp; + return; + } + + Buffer |= (V & 1) << (BufferShift++); + + if (BufferShift == 5) { + DRegs[n] = Buffer; + BufferShift = Buffer = 0; + switch (n) { + case 0: MMC1MIRROR(); MMC1CHR(); MMC1PRG(); break; + case 1: MMC1CHR(); MMC1PRG(); break; + case 2: MMC1CHR(); break; + case 3: MMC1PRG(); break; + } + } +} + +static void MMC1_Restore(int version) { + MMC1MIRROR(); + MMC1CHR(); + MMC1PRG(); + lreset = 0; // timestamp(base) is not stored in save states. +} + +static void MMC1CMReset(void) { + int i; + + for (i = 0; i < 4; i++) + DRegs[i] = 0; + Buffer = BufferShift = 0; + DRegs[0] = 0x1F; + + DRegs[1] = 0; + DRegs[2] = 0; // Should this be something other than 0? + DRegs[3] = 0; + + MMC1MIRROR(); + MMC1CHR(); + MMC1PRG(); +} + +static int DetectMMC1WRAMSize(CartInfo *info, int *bs) { + int ws = 8; + switch (info->CRC32) { + case 0xc6182024: // Romance of the 3 Kingdoms + case 0xabbf7217: // "" "" (J) (PRG0) + case 0xccf35c02: // "" "" (J) (PRG1) + case 0x2225c20f: // Genghis Khan + case 0xfb69743a: // "" "" (J) + case 0x4642dda6: // Nobunaga's Ambition + case 0x3f7ad415: // "" "" (J) (PRG0) + case 0x2b11e0b0: // "" "" (J) (PRG1) + *bs = 8; + ws = 16; + break; + case 0xb8747abf: // Best Play Pro Yakyuu Special (J) (PRG0) + case 0xc3de7c69: // "" "" (J) (PRG1) + case 0xc9556b36: // Final Fantasy I & II (J) [!] + *bs = 32; + ws = 32; + break; + default: + if(info->ines2) { + ws = (info->wram_size + info->battery_wram_size) / 1024; + *bs = info->battery_wram_size / 1024; + // we only support sizes between 8K and 32K + if (ws > 0 && ws < 8) ws = 8; + if (ws > 32) ws = 32; + if (*bs > ws) *bs = ws; + } + } + if (ws > 8) + FCEU_printf(" >8KB external WRAM present. Use NES 2.0 if you hack the ROM image.\n"); + return ws; +} + +static uint32 NWCIRQCount; +static uint8 NWCRec; +#define NWCDIP 0xE + +static void NWCIRQHook(int a) { + if (!(NWCRec & 0x10)) { + NWCIRQCount += a; + if ((NWCIRQCount | (NWCDIP << 25)) >= 0x3e000000) { + NWCIRQCount = 0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void NWCCHRHook(uint32 A, uint8 V) { + if ((V & 0x10)) { // && !(NWCRec&0x10)) + NWCIRQCount = 0; + X6502_IRQEnd(FCEU_IQEXT); + } + + NWCRec = V; + if (V & 0x08) + MMC1PRG(); + else + setprg32(0x8000, (V >> 1) & 3); +} + +static void NWCPRGHook(uint32 A, uint8 V) { + if (NWCRec & 0x8) + setprg16(A, 8 | (V & 0x7)); + else + setprg32(0x8000, (NWCRec >> 1) & 3); +} + +static void NWCPower(void) { + GenMMC1Power(); + setchr8r(0, 0); +} + +void Mapper105_Init(CartInfo *info) { + GenMMC1Init(info, 256, 256, 8, 0); + MMC1CHRHook4 = NWCCHRHook; + MMC1PRGHook16 = NWCPRGHook; + MapIRQHook = NWCIRQHook; + info->Power = NWCPower; +} + +static void GenMMC1Power(void) { + lreset = 0; + SetWriteHandler(0x8000, 0xFFFF, MMC1_write); + SetReadHandler(0x8000, 0xFFFF, CartBR); + + if (WRAMSIZE) { + FCEU_CheatAddRAM(8, 0x6000, WRAM); + + // clear non-battery-backed portion of WRAM + if (NONBRAMSIZE) + FCEU_MemoryRand(WRAM, NONBRAMSIZE, true); + + SetReadHandler(0x6000, 0x7FFF, MAWRAM); + SetWriteHandler(0x6000, 0x7FFF, MBWRAM); + setprg8r(0x10, 0x6000, 0); + } + + MMC1CMReset(); +} + +static void GenMMC1Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + if (WRAM) + FCEU_gfree(WRAM); + CHRRAM = WRAM = NULL; +} + +static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int bram) { + is155 = 0; + + info->Close = GenMMC1Close; + MMC1PRGHook16 = MMC1CHRHook4 = 0; + WRAMSIZE = wram * 1024; + NONBRAMSIZE = (wram - bram) * 1024; + PRGmask16[0] &= (prg >> 14) - 1; + CHRmask4[0] &= (chr >> 12) - 1; + CHRmask8[0] &= (chr >> 13) - 1; + + if (WRAMSIZE) { + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (bram) { + info->SaveGame[0] = WRAM + NONBRAMSIZE; + info->SaveGameLen[0] = bram * 1024; + } + } + if (!chr) { + CHRRAM = (uint8*)FCEU_gmalloc(8192); + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(CHRRAM, 8192, 0, "CHRR"); + } + AddExState(DRegs, 4, 0, "DREG"); + + info->Power = GenMMC1Power; + GameStateRestore = MMC1_Restore; + AddExState(&lreset, 8, 1, "LRST"); + AddExState(&Buffer, 1, 1, "BFFR"); + AddExState(&BufferShift, 1, 1, "BFRS"); +} + +void Mapper1_Init(CartInfo *info) { + int bs = info->battery ? 8 : 0; + int ws = DetectMMC1WRAMSize(info, &bs); + GenMMC1Init(info, 512, 256, ws, bs); +} + +/* Same as mapper 1, without respect for WRAM enable bit. */ +void Mapper155_Init(CartInfo *info) { + GenMMC1Init(info, 512, 256, 8, info->battery ? 8 : 0); + is155 = 1; +} + +/* Same as mapper 1, with different (or without) mirroring control. */ +/* Kaiser KS7058 board, KS203 custom chip */ +void Mapper171_Init(CartInfo *info) { + GenMMC1Init(info, 32, 32, 0, 0); + is171 = 1; +} + +void SAROM_Init(CartInfo *info) { + GenMMC1Init(info, 128, 64, 8, info->battery ? 8 : 0); +} + +void SBROM_Init(CartInfo *info) { + GenMMC1Init(info, 128, 64, 0, 0); +} + +void SCROM_Init(CartInfo *info) { + GenMMC1Init(info, 128, 128, 0, 0); +} + +void SEROM_Init(CartInfo *info) { + GenMMC1Init(info, 32, 64, 0, 0); +} + +void SGROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 0, 0, 0); +} + +void SKROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 64, 8, info->battery ? 8 : 0); +} + +void SLROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 128, 0, 0); +} + +void SL1ROM_Init(CartInfo *info) { + GenMMC1Init(info, 128, 128, 0, 0); +} + +/* Begin unknown - may be wrong - perhaps they use different MMC1s from the + similarly functioning boards? +*/ + +void SL2ROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 256, 0, 0); +} + +void SFROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 256, 0, 0); +} + +void SHROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 256, 0, 0); +} + +/* End unknown */ +/* */ +/* */ + +void SNROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 0, 8, info->battery ? 8 : 0); +} + +void SOROM_Init(CartInfo *info) { + GenMMC1Init(info, 256, 0, 16, info->battery ? 8 : 0); +} diff --git a/apps/fceux/src/boards/mmc2and4.cpp b/apps/fceux/src/boards/mmc2and4.cpp new file mode 100644 index 00000000..5d9d73d6 --- /dev/null +++ b/apps/fceux/src/boards/mmc2and4.cpp @@ -0,0 +1,138 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2002 Xodnizel + * + * 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 + * + */ + +#include "mapinc.h" + +static uint8 is10; +static uint8 creg[4], latch0, latch1, preg, mirr; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { creg, 4, "CREG" }, + { &preg, 1, "PREG" }, + { &mirr, 1, "MIRR" }, + { &latch0, 1, "LAT0" }, + { &latch1, 1, "LAT1" }, + { 0 } +}; + +static void Sync(void) { + if (is10) { + setprg8r(0x10, 0x6000, 0); + setprg16(0x8000, preg); + setprg16(0xC000, ~0); + } else { + setprg8(0x8000, preg); + setprg8(0xA000, ~2); + setprg8(0xC000, ~1); + setprg8(0xE000, ~0); + } + setchr4(0x0000, creg[latch0]); + setchr4(0x1000, creg[latch1 + 2]); + setmirror(mirr); +} + +DECLFW(MMC2and4Write) { + switch (A & 0xF000) { + case 0xA000: preg = V; Sync(); break; + case 0xB000: creg[0] = V; Sync(); break; + case 0xC000: creg[1] = V; Sync(); break; + case 0xD000: creg[2] = V; Sync(); break; + case 0xE000: creg[3] = V; Sync(); break; + case 0xF000: mirr = (V & 1) ^ 1; Sync(); break; + } +} + +static void MMC2and4PPUHook(uint32 A) { + uint8 l, h = A >> 8; + if (h >= 0x20 || ((h & 0xF) != 0xF)) + return; + l = A & 0xF0; + if (h < 0x10) { + if (l == 0xD0) { + latch0 = 0; + setchr4(0x0000, creg[0]); + } else if (l == 0xE0) { + latch0 = 1; + setchr4(0x0000, creg[1]); + } + } else { + if (l == 0xD0) { + latch1 = 0; + setchr4(0x1000, creg[2]); + } else if (l == 0xE0) { + latch1 = 1; + setchr4(0x1000, creg[3]); + } + } +} + +static void MMC2and4Power(void) { + preg = 0; + latch0 = latch1 = 1; + Sync(); + if (is10) { + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + } + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0xA000, 0xFFFF, MMC2and4Write); +} + +static void StateRestore(int version) { + Sync(); +} + +static void MMC2and4Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void Mapper9_Init(CartInfo *info) { + is10 = 0; + info->Power = MMC2and4Power; + PPU_hook = MMC2and4PPUHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper10_Init(CartInfo *info) { + is10 = 1; + info->Power = MMC2and4Power; + info->Close = MMC2and4Close; + PPU_hook = MMC2and4PPUHook; + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/mmc3.cpp b/apps/fceux/src/boards/mmc3.cpp new file mode 100644 index 00000000..e4f1a9e2 --- /dev/null +++ b/apps/fceux/src/boards/mmc3.cpp @@ -0,0 +1,1365 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2003 Xodnizel + * Mapper 12 code Copyright (C) 2003 CaH4e3 + * + * 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 + */ + +/* Code for emulating iNES mappers 4,12,44,45,47,49,52,74,114,115,116,118, + 119,165,205,245,249,250,254 +*/ + +#include "mapinc.h" +#include "mmc3.h" + +uint8 MMC3_cmd; +uint8 kt_extra; +uint8 *WRAM; +uint32 WRAMSIZE; +uint8 *CHRRAM; +uint32 CHRRAMSIZE; +uint8 DRegBuf[8]; +uint8 EXPREGS[8]; /* For bootleg games, mostly. */ +uint8 A000B, A001B; +uint8 mmc3opts = 0; + +#undef IRQCount +#undef IRQLatch +#undef IRQa +uint8 IRQCount, IRQLatch, IRQa; +uint8 IRQReload; + +static SFORMAT MMC3_StateRegs[] = +{ + { DRegBuf, 8, "REGS" }, + { &MMC3_cmd, 1, "CMD" }, + { &A000B, 1, "A000" }, + { &A001B, 1, "A001" }, + { &IRQReload, 1, "IRQR" }, + { &IRQCount, 1, "IRQC" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQa, 1, "IRQA" }, + { 0 } +}; + +static int isRevB = 1; + +void (*pwrap)(uint32 A, uint8 V); +void (*cwrap)(uint32 A, uint8 V); +void (*mwrap)(uint8 V); + +void GenMMC3Power(void); +void FixMMC3PRG(int V); +void FixMMC3CHR(int V); + +void GenMMC3_Init(CartInfo *info, int prg, int chr, int wram, int battery); + +// ---------------------------------------------------------------------- +// ------------------------- Generic MM3 Code --------------------------- +// ---------------------------------------------------------------------- + +void FixMMC3PRG(int V) { + if (V & 0x40) { + pwrap(0xC000, DRegBuf[6]); + pwrap(0x8000, ~1); + } else { + pwrap(0x8000, DRegBuf[6]); + pwrap(0xC000, ~1); + } + pwrap(0xA000, DRegBuf[7]); + pwrap(0xE000, ~0); +} + +void FixMMC3CHR(int V) { + int cbase = (V & 0x80) << 5; + + cwrap((cbase ^ 0x000), DRegBuf[0] & (~1)); + cwrap((cbase ^ 0x400), DRegBuf[0] | 1); + cwrap((cbase ^ 0x800), DRegBuf[1] & (~1)); + cwrap((cbase ^ 0xC00), DRegBuf[1] | 1); + + cwrap(cbase ^ 0x1000, DRegBuf[2]); + cwrap(cbase ^ 0x1400, DRegBuf[3]); + cwrap(cbase ^ 0x1800, DRegBuf[4]); + cwrap(cbase ^ 0x1c00, DRegBuf[5]); + + if (mwrap) mwrap(A000B); +} + +void MMC3RegReset(void) { + IRQCount = IRQLatch = IRQa = MMC3_cmd = 0; + + DRegBuf[0] = 0; + DRegBuf[1] = 2; + DRegBuf[2] = 4; + DRegBuf[3] = 5; + DRegBuf[4] = 6; + DRegBuf[5] = 7; + DRegBuf[6] = 0; + DRegBuf[7] = 1; + +// KT-008 boards hack 2-in-1, TODO assign to new ines mapper, most dump of KT-boards on the net are mapper 4, so need database or goodnes fix support + kt_extra = 0; + + FixMMC3PRG(0); + FixMMC3CHR(0); +} + +DECLFW(MMC3_CMDWrite) { +// FCEU_printf("bs %04x %02x\n",A,V); + switch (A & 0xE001) { + case 0x8000: + if ((V & 0x40) != (MMC3_cmd & 0x40)) + FixMMC3PRG(V); + if ((V & 0x80) != (MMC3_cmd & 0x80)) + FixMMC3CHR(V); + MMC3_cmd = V; + break; + case 0x8001: + { + int cbase = (MMC3_cmd & 0x80) << 5; + DRegBuf[MMC3_cmd & 0x7] = V; + switch (MMC3_cmd & 0x07) { + case 0: + cwrap((cbase ^ 0x000), V & (~1)); + cwrap((cbase ^ 0x400), V | 1); + break; + case 1: + cwrap((cbase ^ 0x800), V & (~1)); + cwrap((cbase ^ 0xC00), V | 1); + break; + case 2: + cwrap(cbase ^ 0x1000, V); + break; + case 3: + cwrap(cbase ^ 0x1400, V); + break; + case 4: + cwrap(cbase ^ 0x1800, V); + break; + case 5: + cwrap(cbase ^ 0x1C00, V); + break; + case 6: + if (MMC3_cmd & 0x40) + pwrap(0xC000, V); + else + pwrap(0x8000, V); + break; + case 7: + pwrap(0xA000, V); + break; + } + break; + } + case 0xA000: + if (mwrap) mwrap(V); + break; + case 0xA001: + A001B = V; + break; + } +} + +DECLFW(MMC3_IRQWrite) { +// FCEU_printf("%04x:%04x\n",A,V); + switch (A & 0xE001) { + case 0xC000: IRQLatch = V; break; + case 0xC001: IRQReload = 1; break; + case 0xE000: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break; + case 0xE001: IRQa = 1; break; + } +} + +// KT-008 boards hack 2-in-1, TODO assign to new ines mapper, most dump of KT-boards on the net are mapper 4, so need database or goodnes fix support +DECLFW(KT008HackWrite) { +// FCEU_printf("%04x:%04x\n",A,V); + switch (A & 3) { + case 0: { + if (V == 0x27) // FF Xn hack! one more mapper in one + kt_extra = 0; + else + kt_extra = V; + FixMMC3PRG(MMC3_cmd); + break; + } + case 1: break; // unk + case 2: break; // unk + case 3: break; // unk + } +} + +static void ClockMMC3Counter(void) { + int count = IRQCount; + if (!count || IRQReload) { + IRQCount = IRQLatch; + IRQReload = 0; + } else + IRQCount--; + if ((count | isRevB) && !IRQCount) { + if (IRQa) { + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void MMC3_hb(void) { + ClockMMC3Counter(); +} + +static void MMC3_hb_KickMasterHack(void) { + if (scanline == 238) ClockMMC3Counter(); + ClockMMC3Counter(); +} + +static void MMC3_hb_PALStarWarsHack(void) { + if (scanline == 240) ClockMMC3Counter(); + ClockMMC3Counter(); +} + +void GenMMC3Restore(int version) { + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void GENCWRAP(uint32 A, uint8 V) { + setchr1(A, V); // Business Wars NEEDS THIS for 8K CHR-RAM +} + +static void GENPWRAP(uint32 A, uint8 V) { +// [NJ102] Mo Dao Jie (C) has 1024Mb MMC3 BOARD, maybe something other will be broken +// also HengGe BBC-2x boards enables this mode as default board mode at boot up + setprg8(A, (V & 0x7F) | ((kt_extra & 4) << 4)); +// KT-008 boards hack 2-in-1, TODO assign to new ines mapper, most dump of KT-boards on the net are mapper 4, so need database or goodnes fix support +} + +static void GENMWRAP(uint8 V) { + A000B = V; + setmirror((V & 1) ^ 1); +} + +static void GENNOMWRAP(uint8 V) { + A000B = V; +} + +static DECLFW(MBWRAMMMC6) { + WRAM[A & 0x3ff] = V; +} + +static DECLFR(MAWRAMMMC6) { + return(WRAM[A & 0x3ff]); +} + +void GenMMC3Power(void) { + if (UNIFchrrama) setchr8(0); + + SetWriteHandler(0x8000, 0xBFFF, MMC3_CMDWrite); + SetWriteHandler(0xC000, 0xFFFF, MMC3_IRQWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); + +// KT-008 boards hack 2-in-1, TODO assign to new ines mapper, most dump of KT-boards on the net are mapper 4, so need database or goodnes fix support + SetWriteHandler(0x5000,0x5FFF, KT008HackWrite); + + A001B = A000B = 0; + setmirror(1); + if (mmc3opts & 1) { + if (WRAMSIZE == 1024) { + FCEU_CheatAddRAM(1, 0x7000, WRAM); + SetReadHandler(0x7000, 0x7FFF, MAWRAMMMC6); + SetWriteHandler(0x7000, 0x7FFF, MBWRAMMMC6); + } else { + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + SetWriteHandler(0x6000, 0x6000 + ((WRAMSIZE - 1) & 0x1fff), CartBW); + SetReadHandler(0x6000, 0x6000 + ((WRAMSIZE - 1) & 0x1fff), CartBR); + setprg8r(0x10, 0x6000, 0); + } + if (!(mmc3opts & 2)) + FCEU_MemoryRand(WRAM, WRAMSIZE, true); + } + MMC3RegReset(); + if (CHRRAM) + FCEU_MemoryRand(CHRRAM, CHRRAMSIZE, true); +} + +static void GenMMC3Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + if (WRAM) + FCEU_gfree(WRAM); + CHRRAM = WRAM = NULL; +} + +void GenMMC3_Init(CartInfo *info, int prg, int chr, int wram, int battery) { + pwrap = GENPWRAP; + cwrap = GENCWRAP; + mwrap = GENMWRAP; + + WRAMSIZE = wram << 10; + + PRGmask8[0] &= (prg >> 13) - 1; + CHRmask1[0] &= (chr >> 10) - 1; + CHRmask2[0] &= (chr >> 11) - 1; + + if (wram) { + mmc3opts |= 1; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + } + + if (battery) { + mmc3opts |= 2; + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + +// KT-008 boards hack 2-in-1, TODO assign to new ines mapper, most dump of KT-boards on the net are mapper 4, so need database or goodnes fix support + AddExState(&kt_extra, 1, 0, "KTEX"); + AddExState(MMC3_StateRegs, ~0, 0, 0); + + info->Power = GenMMC3Power; + info->Reset = MMC3RegReset; + info->Close = GenMMC3Close; + + if (info->CRC32 == 0x5104833e) // Kick Master + GameHBIRQHook = MMC3_hb_KickMasterHack; + else if (info->CRC32 == 0x5a6860f1 || info->CRC32 == 0xae280e20)// Shougi Meikan '92/'93 + GameHBIRQHook = MMC3_hb_KickMasterHack; + else if (info->CRC32 == 0xfcd772eb) // PAL Star Wars, similar problem as Kick Master. + GameHBIRQHook = MMC3_hb_PALStarWarsHack; + else + GameHBIRQHook = MMC3_hb; + GameStateRestore = GenMMC3Restore; +} + +// ---------------------------------------------------------------------- +// -------------------------- MMC3 Based Code --------------------------- +// ---------------------------------------------------------------------- + +// ---------------------------- Mapper 4 -------------------------------- + +static int hackm4 = 0; /* For Karnov, maybe others. BLAH. Stupid iNES format.*/ + +static void M4Power(void) { + GenMMC3Power(); + A000B = (hackm4 ^ 1) & 1; + setmirror(hackm4); +} + +void Mapper4_Init(CartInfo *info) { + int ws = 8; + + if ((info->CRC32 == 0x93991433 || info->CRC32 == 0xaf65aa84)) { + FCEU_printf("Low-G-Man can not work normally in the iNES format.\nThis game has been recognized by its CRC32 value, and the appropriate changes will be made so it will run.\nIf you wish to hack this game, you should use the UNIF format for your hack.\n\n"); + ws = 0; + } + GenMMC3_Init(info, 512, 256, ws, info->battery); + info->Power = M4Power; + hackm4 = info->mirror; +} + +// ---------------------------- Mapper 12 ------------------------------- + +static void M12CW(uint32 A, uint8 V) { + setchr1(A, (EXPREGS[(A & 0x1000) >> 12] << 8) + V); +} + +static DECLFW(M12Write) { + EXPREGS[0] = V & 0x01; + EXPREGS[1] = (V & 0x10) >> 4; +} + +static DECLFR(M12Read) { + return EXPREGS[2]; +} + +static void M12Power(void) { + EXPREGS[0] = EXPREGS[1] = 0; + EXPREGS[2] = 1; // chinese is default + GenMMC3Power(); + SetWriteHandler(0x4100, 0x5FFF, M12Write); + SetReadHandler(0x4100, 0x5FFF, M12Read); +} + +static void M12Reset(void) { + EXPREGS[0] = EXPREGS[1] = 0; + EXPREGS[2] ^= 1; + MMC3RegReset(); +} + +void Mapper12_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M12CW; + isRevB = 0; + + info->Power = M12Power; + info->Reset = M12Reset; + AddExState(EXPREGS, 2, 0, "EXPR"); +} + +// ---------------------------- Mapper 37 ------------------------------- + +static void M37PW(uint32 A, uint8 V) { + if (EXPREGS[0] != 2) + V &= 0x7; + else + V &= 0xF; + V |= EXPREGS[0] << 3; + setprg8(A, V); +} + +static void M37CW(uint32 A, uint8 V) { + uint32 NV = V; + NV &= 0x7F; + NV |= EXPREGS[0] << 6; + setchr1(A, NV); +} + +static DECLFW(M37Write) { + EXPREGS[0] = (V & 6) >> 1; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M37Reset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +static void M37Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, M37Write); +} + +void Mapper37_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + pwrap = M37PW; + cwrap = M37CW; + info->Power = M37Power; + info->Reset = M37Reset; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 44 ------------------------------- + +static void M44PW(uint32 A, uint8 V) { + uint32 NV = V; + if (EXPREGS[0] >= 6) NV &= 0x1F; + else NV &= 0x0F; + NV |= EXPREGS[0] << 4; + setprg8(A, NV); +} + +static void M44CW(uint32 A, uint8 V) { + uint32 NV = V; + if (EXPREGS[0] < 6) NV &= 0x7F; + NV |= EXPREGS[0] << 7; + setchr1(A, NV); +} + +static DECLFW(M44Write) { + if (A & 1) { + EXPREGS[0] = V & 7; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else + MMC3_CMDWrite(A, V); +} + +static void M44Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0xA000, 0xBFFF, M44Write); +} + +void Mapper44_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M44CW; + pwrap = M44PW; + info->Power = M44Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 45 ------------------------------- + +static void M45CW(uint32 A, uint8 V) { + if (!UNIFchrrama) { + uint32 NV = V; + if (EXPREGS[2] & 8) + NV &= (1 << ((EXPREGS[2] & 7) + 1)) - 1; + else + if (EXPREGS[2]) + NV &= 0; // hack ;( don't know exactly how it should be + NV |= EXPREGS[0] | ((EXPREGS[2] & 0xF0) << 4); + setchr1(A, NV); + } else +// setchr8(0); // i don't know what cart need this, but a new one need other lol + setchr1(A, V); +} + +static void M45PW(uint32 A, uint8 V) { + uint32 MV = V & ((EXPREGS[3] & 0x3F) ^ 0x3F); + MV |= EXPREGS[1]; + if(UNIFchrrama) + MV |= ((EXPREGS[2] & 0x40) << 2); + setprg8(A, MV); +// FCEU_printf("1:%02x 2:%02x 3:%02x A=%04x V=%03x\n",EXPREGS[1],EXPREGS[2],EXPREGS[3],A,MV); +} + +static DECLFW(M45Write) { + if (EXPREGS[3] & 0x40) { + WRAM[A - 0x6000] = V; + return; + } + EXPREGS[EXPREGS[4]] = V; + EXPREGS[4] = (EXPREGS[4] + 1) & 3; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static DECLFR(M45Read) { + uint32 addr = 1 << (EXPREGS[5] + 4); + if (A & (addr | (addr - 1))) + return X.DB | 1; + else + return X.DB; +} + +static void M45Reset(void) { + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = EXPREGS[4] = 0; + EXPREGS[5]++; + EXPREGS[5] &= 7; + MMC3RegReset(); +} + +static void M45Power(void) { + GenMMC3Power(); + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = EXPREGS[4] = EXPREGS[5] = 0; + SetWriteHandler(0x5000, 0x7FFF, M45Write); + SetReadHandler(0x5000, 0x5FFF, M45Read); +} + +void Mapper45_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M45CW; + pwrap = M45PW; + info->Reset = M45Reset; + info->Power = M45Power; + AddExState(EXPREGS, 5, 0, "EXPR"); +} + +// ---------------------------- Mapper 47 ------------------------------- + +static void M47PW(uint32 A, uint8 V) { + V &= 0xF; + V |= EXPREGS[0] << 4; + setprg8(A, V); +} + +static void M47CW(uint32 A, uint8 V) { + uint32 NV = V; + NV &= 0x7F; + NV |= EXPREGS[0] << 7; + setchr1(A, NV); +} + +static DECLFW(M47Write) { + EXPREGS[0] = V & 1; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M47Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, M47Write); +// SetReadHandler(0x6000,0x7FFF,0); +} + +void Mapper47_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, 0); + pwrap = M47PW; + cwrap = M47CW; + info->Power = M47Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 49 ------------------------------- + +static void M49PW(uint32 A, uint8 V) { + if (EXPREGS[0] & 1) { + V &= 0xF; + V |= (EXPREGS[0] & 0xC0) >> 2; + setprg8(A, V); + } else + setprg32(0x8000, (EXPREGS[0] >> 4) & 3); +} + +static void M49CW(uint32 A, uint8 V) { + uint32 NV = V; + NV &= 0x7F; + NV |= (EXPREGS[0] & 0xC0) << 1; + setchr1(A, NV); +} + +static DECLFW(M49Write) { + if (A001B & 0x80) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +} + +static void M49Reset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +static void M49Power(void) { + M49Reset(); + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, M49Write); + SetReadHandler(0x6000, 0x7FFF, 0); +} + +void Mapper49_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 0, 0); + cwrap = M49CW; + pwrap = M49PW; + info->Reset = M49Reset; + info->Power = M49Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 52 ------------------------------- +static void M52PW(uint32 A, uint8 V) { + uint32 mask = 0x1F ^ ((EXPREGS[0] & 8) << 1); + uint32 bank = ((EXPREGS[0] & 6) | ((EXPREGS[0] >> 3) & EXPREGS[0] & 1)) << 4; + setprg8(A, bank | (V & mask)); +} + +static void M52CW(uint32 A, uint8 V) { + uint32 mask = 0xFF ^ ((EXPREGS[0] & 0x40) << 1); +// uint32 bank = (((EXPREGS[0]>>3)&4)|((EXPREGS[0]>>1)&2)|((EXPREGS[0]>>6)&(EXPREGS[0]>>4)&1))<<7; + uint32 bank = (((EXPREGS[0] >> 4) & 2) | (EXPREGS[0] & 4) | ((EXPREGS[0] >> 6) & (EXPREGS[0] >> 4) & 1)) << 7; // actually 256K CHR banks index bits is inverted! + setchr1(A, bank | (V & mask)); +} + +static DECLFW(M52Write) { + if (EXPREGS[1]) { + WRAM[A - 0x6000] = V; + return; + } + EXPREGS[1] = V & 0x80; + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M52Reset(void) { + EXPREGS[0] = EXPREGS[1] = 0; + MMC3RegReset(); +} + +static void M52Power(void) { + M52Reset(); + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, M52Write); +} + +void Mapper52_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 8, info->battery); + cwrap = M52CW; + pwrap = M52PW; + info->Reset = M52Reset; + info->Power = M52Power; + AddExState(EXPREGS, 2, 0, "EXPR"); +} + +// ---------------------------- Mapper 76 ------------------------------- + +static void M76CW(uint32 A, uint8 V) { + if (A >= 0x1000) + setchr2((A & 0xC00) << 1, V); +} + +void Mapper76_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 0, 0); + cwrap = M76CW; +} + +// ---------------------------- Mapper 74 ------------------------------- + +static void M74CW(uint32 A, uint8 V) { + if ((V == 8) || (V == 9)) //Di 4 Ci - Ji Qi Ren Dai Zhan (As).nes, Ji Jia Zhan Shi (As).nes + setchr1r(0x10, A, V); + else + setchr1r(0, A, V); +} + +void Mapper74_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M74CW; + CHRRAMSIZE = 2048; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +// ---------------------------- Mapper 114 ------------------------------ + +static uint8 cmdin; +uint8 m114_perm[8] = { 0, 3, 1, 5, 6, 7, 2, 4 }; + +static void M114PWRAP(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x80) { +// FCEU_printf("8000-C000:%02X\n",EXPREGS[0]&0xF); + setprg16(0x8000, EXPREGS[0] & 0xF); + setprg16(0xC000, EXPREGS[0] & 0xF); + } else { +// FCEU_printf("%04X:%02X\n",A,V&0x3F); + setprg8(A, V & 0x3F); + } +} + +static DECLFW(M114Write) { + switch (A & 0xE001) { + case 0x8001: MMC3_CMDWrite(0xA000, V); break; + case 0xA000: MMC3_CMDWrite(0x8000, (V & 0xC0) | (m114_perm[V & 7])); cmdin = 1; break; + case 0xC000: if (!cmdin) break; MMC3_CMDWrite(0x8001, V); cmdin = 0; break; + case 0xA001: IRQLatch = V; break; + case 0xC001: IRQReload = 1; break; + case 0xE000: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break; + case 0xE001: IRQa = 1; break; + } +} + +static DECLFW(M114ExWrite) { + if (A <= 0x7FFF) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + } +} + +static void M114Power(void) { + GenMMC3Power(); + SetWriteHandler(0x8000, 0xFFFF, M114Write); + SetWriteHandler(0x5000, 0x7FFF, M114ExWrite); +} + +static void M114Reset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +void Mapper114_Init(CartInfo *info) { + isRevB = 0; + GenMMC3_Init(info, 256, 256, 0, 0); + pwrap = M114PWRAP; + info->Power = M114Power; + info->Reset = M114Reset; + AddExState(EXPREGS, 1, 0, "EXPR"); + AddExState(&cmdin, 1, 0, "CMDI"); +} + +// ---------------------------- Mapper 115 KN-658 board ------------------------------ + +static void M115PW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x80) { + if (EXPREGS[0] & 0x20) + setprg32(0x8000, (EXPREGS[0] & 0x0F) >> 1); // real hardware tests, info 100% now lol + else { + setprg16(0x8000, (EXPREGS[0] & 0x0F)); + setprg16(0xC000, (EXPREGS[0] & 0x0F)); + } + } else + setprg8(A, V); +} + +static void M115CW(uint32 A, uint8 V) { + setchr1(A, (uint32)V | ((EXPREGS[1] & 1) << 8)); +} + +static DECLFW(M115Write) { + if (A == 0x5080) + EXPREGS[2] = V; // Extra prot hardware 2-in-1 mode + else if (A == 0x6000) + EXPREGS[0] = V; + else if (A == 0x6001) + EXPREGS[1] = V; + FixMMC3PRG(MMC3_cmd); +} + +static DECLFR(M115Read) { + return EXPREGS[2]; +} + +static void M115Power(void) { + GenMMC3Power(); + SetWriteHandler(0x4100, 0x7FFF, M115Write); + SetReadHandler(0x5000, 0x5FFF, M115Read); +} + +void Mapper115_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 512, 0, 0); + cwrap = M115CW; + pwrap = M115PW; + info->Power = M115Power; + AddExState(EXPREGS, 3, 0, "EXPR"); +} + +// ---------------------------- Mapper 118 ------------------------------ + +static uint8 PPUCHRBus; +static uint8 TKSMIR[8]; + +static void TKSPPU(uint32 A) { + A &= 0x1FFF; + A >>= 10; + PPUCHRBus = A; + setmirror(MI_0 + TKSMIR[A]); +} + +static void TKSWRAP(uint32 A, uint8 V) { + TKSMIR[A >> 10] = V >> 7; + setchr1(A, V & 0x7F); + if (PPUCHRBus == (A >> 10)) + setmirror(MI_0 + (V >> 7)); +} + +// ---------------------------- Mapper 119 ------------------------------ + +static void TQWRAP(uint32 A, uint8 V) { + setchr1r((V & 0x40) >> 2, A, V & 0x3F); +} + +void Mapper119_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 64, 0, 0); + cwrap = TQWRAP; + CHRRAMSIZE = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +// ---------------------------- Mapper 134 ------------------------------ + +static void M134PW(uint32 A, uint8 V) { + setprg8(A, (V & 0x1F) | ((EXPREGS[0] & 2) << 4)); +} + +static void M134CW(uint32 A, uint8 V) { + setchr1(A, (V & 0xFF) | ((EXPREGS[0] & 0x20) << 3)); +} + +static DECLFW(M134Write) { + EXPREGS[0] = V; + FixMMC3CHR(MMC3_cmd); + FixMMC3PRG(MMC3_cmd); +} + +static void M134Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x6001, 0x6001, M134Write); +} + +static void M134Reset(void) { + EXPREGS[0] = 0; + MMC3RegReset(); +} + +void Mapper134_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 0, 0); + pwrap = M134PW; + cwrap = M134CW; + info->Power = M134Power; + info->Reset = M134Reset; + AddExState(EXPREGS, 4, 0, "EXPR"); +} + +// ---------------------------- Mapper 165 ------------------------------ + +static void M165CW(uint32 A, uint8 V) { + if (V == 0) + setchr4r(0x10, A, 0); + else + setchr4(A, V >> 2); +} + +static void M165PPUFD(void) { + if (EXPREGS[0] == 0xFD) { + M165CW(0x0000, DRegBuf[0]); + M165CW(0x1000, DRegBuf[2]); + } +} + +static void M165PPUFE(void) { + if (EXPREGS[0] == 0xFE) { + M165CW(0x0000, DRegBuf[1]); + M165CW(0x1000, DRegBuf[4]); + } +} + +static void M165CWM(uint32 A, uint8 V) { + if (((MMC3_cmd & 0x7) == 0) || ((MMC3_cmd & 0x7) == 2)) + M165PPUFD(); + if (((MMC3_cmd & 0x7) == 1) || ((MMC3_cmd & 0x7) == 4)) + M165PPUFE(); +} + +static void M165PPU(uint32 A) { + if ((A & 0x1FF0) == 0x1FD0) { + EXPREGS[0] = 0xFD; + M165PPUFD(); + } else if ((A & 0x1FF0) == 0x1FE0) { + EXPREGS[0] = 0xFE; + M165PPUFE(); + } +} + +static void M165Power(void) { + EXPREGS[0] = 0xFD; + GenMMC3Power(); +} + +void Mapper165_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 128, 8, info->battery); + cwrap = M165CWM; + PPU_hook = M165PPU; + info->Power = M165Power; + CHRRAMSIZE = 4096; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); + AddExState(EXPREGS, 4, 0, "EXPR"); +} + +// ---------------------------- Mapper 191 ------------------------------ + +static void M191CW(uint32 A, uint8 V) { + setchr1r((V & 0x80) >> 3, A, V); +} + +void Mapper191_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 256, 8, info->battery); + cwrap = M191CW; + CHRRAMSIZE = 2048; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +// ---------------------------- Mapper 192 ------------------------------- + +static void M192CW(uint32 A, uint8 V) { + //Ying Lie Qun Xia Zhuan (Chinese), + //You Ling Xing Dong (China) (Unl) [this will be mistakenly headered as m074 sometimes] + if ((V == 8) || (V == 9) || (V == 0xA) || (V == 0xB)) + setchr1r(0x10, A, V); + else + setchr1r(0, A, V); +} + +void Mapper192_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M192CW; + CHRRAMSIZE = 4096; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +// ---------------------------- Mapper 194 ------------------------------- + +static void M194CW(uint32 A, uint8 V) { + if (V <= 1) //Dai-2-Ji - Super Robot Taisen (As).nes + setchr1r(0x10, A, V); + else + setchr1r(0, A, V); +} + +void Mapper194_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M194CW; + CHRRAMSIZE = 2048; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +// ---------------------------- Mapper 195 ------------------------------- +static void M195CW(uint32 A, uint8 V) { + if (V <= 3) // Crystalis (c).nes, Captain Tsubasa Vol 2 - Super Striker (C) + setchr1r(0x10, A, V); + else + setchr1r(0, A, V); +} + +static void M195Power(void) { + GenMMC3Power(); + setprg4r(0x10, 0x5000, 2); + SetWriteHandler(0x5000, 0x5fff, CartBW); + SetReadHandler(0x5000, 0x5fff, CartBR); +} + +void Mapper195_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 16, info->battery); + cwrap = M195CW; + info->Power = M195Power; + CHRRAMSIZE = 4096; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +// ---------------------------- Mapper 196 ------------------------------- +// MMC3 board with optional command address line connection, allows to +// make three-four different wirings to IRQ address lines and separately to +// CMD address line, Mali Boss additionally check if wiring are correct for +// game + +static void M196PW(uint32 A, uint8 V) { + if (EXPREGS[0]) + setprg32(0x8000, EXPREGS[1]); + else + setprg8(A, V); +} + +static DECLFW(Mapper196Write) { + if (A >= 0xC000) { + A = (A & 0xFFFE) | ((A >> 2) & 1) | ((A >> 3) & 1); + MMC3_IRQWrite(A, V); + } else { + A = (A & 0xFFFE) | ((A >> 2) & 1) | ((A >> 3) & 1) | ((A >> 1) & 1); + MMC3_CMDWrite(A, V); + } +} + +static DECLFW(Mapper196WriteLo) { + EXPREGS[0] = 1; // hacky + EXPREGS[1] = (V & 0xf) | (V >> 4); // this is the same as 189 mapper, but with addr permutations + FixMMC3PRG(MMC3_cmd); +} + +static void Mapper196Power(void) { + GenMMC3Power(); + EXPREGS[0] = EXPREGS[1] = 0; + SetWriteHandler(0x6000, 0x6FFF, Mapper196WriteLo); + SetWriteHandler(0x8000, 0xFFFF, Mapper196Write); +} + +void Mapper196_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 0, 0); + pwrap = M196PW; + info->Power = Mapper196Power; +} + +// ---------------------------- Mali Splash Bomb---------------------------- +// The same board as for 196 mapper games, but with additional data bit swap +// Also, it is impossible to work on the combined 196 mapper source with +// all data bits merged, because it's using one of them as 8000 reg... + +static void UNLMaliSBPW(uint32 A, uint8 V) { + setprg8(A, (V & 3) | ((V & 8) >> 1) | ((V & 4) << 1)); +} + +static void UNLMaliSBCW(uint32 A, uint8 V) { + setchr1(A, (V & 0xDD) | ((V & 0x20) >> 4) | ((V & 2) << 4)); +} + +static DECLFW(UNLMaliSBWrite) { + if (A >= 0xC000) { + A = (A & 0xFFFE) | ((A >> 2) & 1) | ((A >> 3) & 1); + MMC3_IRQWrite(A, V); + } else { + A = (A & 0xFFFE) | ((A >> 3) & 1); + MMC3_CMDWrite(A, V); + } +} + +static void UNLMaliSBPower(void) { + GenMMC3Power(); + SetWriteHandler(0x8000, 0xFFFF, UNLMaliSBWrite); +} + +void UNLMaliSB_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 0, 0); + pwrap = UNLMaliSBPW; + cwrap = UNLMaliSBCW; + info->Power = UNLMaliSBPower; +} + +// ---------------------------- Mapper 197 ------------------------------- + +static void M197CW(uint32 A, uint8 V) { + if (A == 0x0000) + setchr4(0x0000, V >> 1); + else if (A == 0x1000) + setchr2(0x1000, V); + else if (A == 0x1400) + setchr2(0x1800, V); +} + +void Mapper197_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 512, 8, 0); + cwrap = M197CW; +} + +// ---------------------------- Mapper 198 ------------------------------- + +static void M198PW(uint32 A, uint8 V) { + if (V >= 0x50) // Tenchi o Kurau II - Shokatsu Koumei Den (J) (C).nes + setprg8(A, V & 0x4F); + else + setprg8(A, V); +} + +void Mapper198_Init(CartInfo *info) { + GenMMC3_Init(info, 1024, 0, 16, info->battery); + pwrap = M198PW; + info->Power = M195Power; +} + +// ---------------------------- Mapper 205 ------------------------------ +// GN-45 BOARD + +static void M205PW(uint32 A, uint8 V) { +// GN-30A - íà÷àëüíàÿ ìàñêà äîëæíà áûòü 1F + àïïàðàòíûé ïåðåêëþ÷àòåëü íà øèíå àäðåñà + setprg8(A, (V & 0x0f) | EXPREGS[0]); +} + +static void M205CW(uint32 A, uint8 V) { +// GN-30A - íà÷àëüíàÿ ìàñêà äîëæíà áûòü FF + setchr1(A, (V & 0x7F) | (EXPREGS[0] << 3)); +} + +static DECLFW(M205Write0) { + if (EXPREGS[2] == 0) { + EXPREGS[0] = A & 0x30; + EXPREGS[2] = A & 0x80; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else + CartBW(A, V); +} + +static DECLFW(M205Write1) { + if (EXPREGS[2] == 0) { + EXPREGS[0] = V & 0x30; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else + CartBW(A, V); +} + +static void M205Reset(void) { + EXPREGS[0] = EXPREGS[2] = 0; + MMC3RegReset(); +} + +static void M205Power(void) { + GenMMC3Power(); + SetWriteHandler(0x6000, 0x6fff, M205Write0); + SetWriteHandler(0x7000, 0x7fff, M205Write1); // OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein +} + +void Mapper205_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 8, 0); + pwrap = M205PW; + cwrap = M205CW; + info->Power = M205Power; + info->Reset = M205Reset; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 245 ------------------------------ + +static void M245CW(uint32 A, uint8 V) { + if (!UNIFchrrama) // Yong Zhe Dou E Long - Dragon Quest VI (As).nes NEEDS THIS for RAM cart + setchr1(A, V & 7); + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); +} + +static void M245PW(uint32 A, uint8 V) { + setprg8(A, (V & 0x3F) | ((EXPREGS[0] & 2) << 5)); +} + +static void M245Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); +} + +void Mapper245_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M245CW; + pwrap = M245PW; + info->Power = M245Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 249 ------------------------------ + +static void M249PW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x2) { + if (V < 0x20) + V = (V & 1) | ((V >> 3) & 2) | ((V >> 1) & 4) | ((V << 2) & 8) | ((V << 2) & 0x10); + else { + V -= 0x20; + V = (V & 3) | ((V >> 1) & 4) | ((V >> 4) & 8) | ((V >> 2) & 0x10) | ((V << 3) & 0x20) | ((V << 2) & 0xC0); + } + } + setprg8(A, V); +} + +static void M249CW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x2) + V = (V & 3) | ((V >> 1) & 4) | ((V >> 4) & 8) | ((V >> 2) & 0x10) | ((V << 3) & 0x20) | ((V << 2) & 0xC0); + setchr1(A, V); +} + +static DECLFW(M249Write) { + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M249Power(void) { + EXPREGS[0] = 0; + GenMMC3Power(); + SetWriteHandler(0x5000, 0x5000, M249Write); +} + +void Mapper249_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = M249CW; + pwrap = M249PW; + info->Power = M249Power; + AddExState(EXPREGS, 1, 0, "EXPR"); +} + +// ---------------------------- Mapper 250 ------------------------------ + +static DECLFW(M250Write) { + MMC3_CMDWrite((A & 0xE000) | ((A & 0x400) >> 10), A & 0xFF); +} + +static DECLFW(M250IRQWrite) { + MMC3_IRQWrite((A & 0xE000) | ((A & 0x400) >> 10), A & 0xFF); +} + +static void M250_Power(void) { + GenMMC3Power(); + SetWriteHandler(0x8000, 0xBFFF, M250Write); + SetWriteHandler(0xC000, 0xFFFF, M250IRQWrite); +} + +void Mapper250_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + info->Power = M250_Power; +} + +// ---------------------------- Mapper 254 ------------------------------ + +static DECLFR(MR254WRAM) { + if (EXPREGS[0]) + return WRAM[A - 0x6000]; + else + return WRAM[A - 0x6000] ^ EXPREGS[1]; +} + +static DECLFW(M254Write) { + switch (A) { + case 0x8000: EXPREGS[0] = 0xff; break; + case 0xA001: EXPREGS[1] = V; break; + } + MMC3_CMDWrite(A, V); +} + +static void M254_Power(void) { + GenMMC3Power(); + SetWriteHandler(0x8000, 0xBFFF, M254Write); + SetReadHandler(0x6000, 0x7FFF, MR254WRAM); +} + +void Mapper254_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 8, info->battery); + info->Power = M254_Power; + AddExState(EXPREGS, 2, 0, "EXPR"); +} + +// ---------------------------- UNIF Boards ----------------------------- + +void TBROM_Init(CartInfo *info) { + GenMMC3_Init(info, 64, 64, 0, 0); +} + +void TEROM_Init(CartInfo *info) { + GenMMC3_Init(info, 32, 32, 0, 0); +} + +void TFROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 64, 0, 0); +} + +void TGROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 0, 0, 0); +} + +void TKROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); +} + +void TLROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 0, 0); +} + +void TSROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, 0); +} + +void TLSROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, 0); + cwrap = TKSWRAP; + mwrap = GENNOMWRAP; + PPU_hook = TKSPPU; + AddExState(&PPUCHRBus, 1, 0, "PPUC"); +} + +void TKSROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 256, 8, info->battery); + cwrap = TKSWRAP; + mwrap = GENNOMWRAP; + PPU_hook = TKSPPU; + AddExState(&PPUCHRBus, 1, 0, "PPUC"); +} + +void TQROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 64, 0, 0); + cwrap = TQWRAP; + CHRRAMSIZE = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CHRR"); +} + +void HKROM_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 512, 1, info->battery); +} diff --git a/apps/fceux/src/boards/mmc3.h b/apps/fceux/src/boards/mmc3.h new file mode 100644 index 00000000..9dfbc920 --- /dev/null +++ b/apps/fceux/src/boards/mmc3.h @@ -0,0 +1,26 @@ +extern uint8 MMC3_cmd; +extern uint8 mmc3opts; +extern uint8 A000B; +extern uint8 A001B; +extern uint8 EXPREGS[8]; +extern uint8 DRegBuf[8]; + +#undef IRQCount +#undef IRQLatch +#undef IRQa +extern uint8 IRQCount,IRQLatch,IRQa; +extern uint8 IRQReload; + +extern void (*pwrap)(uint32 A, uint8 V); +extern void (*cwrap)(uint32 A, uint8 V); +extern void (*mwrap)(uint8 V); + +void GenMMC3Power(void); +void GenMMC3Restore(int version); +void MMC3RegReset(void); +void FixMMC3PRG(int V); +void FixMMC3CHR(int V); +DECLFW(MMC3_CMDWrite); +DECLFW(MMC3_IRQWrite); + +void GenMMC3_Init(CartInfo *info, int prg, int chr, int wram, int battery); diff --git a/apps/fceux/src/boards/mmc5.cpp b/apps/fceux/src/boards/mmc5.cpp new file mode 100644 index 00000000..a80d5af6 --- /dev/null +++ b/apps/fceux/src/boards/mmc5.cpp @@ -0,0 +1,1063 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +/* None of this code should use any of the iNES bank switching wrappers. */ + +#include "mapinc.h" + +#define ABANKS MMC5SPRVPage +#define BBANKS MMC5BGVPage +#define SpriteON (PPU[1] & 0x10) //Show Sprite +#define ScreenON (PPU[1] & 0x08) //Show screen +#define PPUON (PPU[1] & 0x18) //PPU should operate +#define Sprite16 (PPU[0] & 0x20) //Sprites 8x16/8x8 + +static void (*sfun)(int P); +static void (*psfun)(void); + +void MMC5RunSound(int Count); +void MMC5RunSoundHQ(void); + +static INLINE void MMC5SPRVROM_BANK1(uint32 A, uint32 V) { + if (CHRptr[0]) { + V &= CHRmask1[0]; + MMC5SPRVPage[(A) >> 10] = &CHRptr[0][(V) << 10] - (A); + } +} + +static INLINE void MMC5BGVROM_BANK1(uint32 A, uint32 V) { + if (CHRptr[0]) { + V &= CHRmask1[0]; MMC5BGVPage[(A) >> 10] = &CHRptr[0][(V) << 10] - (A); + } +} + +static INLINE void MMC5SPRVROM_BANK2(uint32 A, uint32 V) { + if (CHRptr[0]) { + V &= CHRmask2[0]; MMC5SPRVPage[(A) >> 10] = MMC5SPRVPage[((A) >> 10) + 1] = &CHRptr[0][(V) << 11] - (A); + } +} +static INLINE void MMC5BGVROM_BANK2(uint32 A, uint32 V) { + if (CHRptr[0]) { + V &= CHRmask2[0]; MMC5BGVPage[(A) >> 10] = MMC5BGVPage[((A) >> 10) + 1] = &CHRptr[0][(V) << 11] - (A); + } +} + +static INLINE void MMC5SPRVROM_BANK4(uint32 A, uint32 V) { + if (CHRptr[0]) { + V &= CHRmask4[0]; MMC5SPRVPage[(A) >> 10] = MMC5SPRVPage[((A) >> 10) + 1] = MMC5SPRVPage[((A) >> 10) + 2] = MMC5SPRVPage[((A) >> 10) + 3] = &CHRptr[0][(V) << 12] - (A); + } +} +static INLINE void MMC5BGVROM_BANK4(uint32 A, uint32 V) { + if (CHRptr[0]) { + V &= CHRmask4[0]; MMC5BGVPage[(A) >> 10] = MMC5BGVPage[((A) >> 10) + 1] = MMC5BGVPage[((A) >> 10) + 2] = MMC5BGVPage[((A) >> 10) + 3] = &CHRptr[0][(V) << 12] - (A); + } +} + +static INLINE void MMC5SPRVROM_BANK8(uint32 V) { + if (CHRptr[0]) { + V &= CHRmask8[0]; MMC5SPRVPage[0] = MMC5SPRVPage[1] = MMC5SPRVPage[2] = MMC5SPRVPage[3] = MMC5SPRVPage[4] = MMC5SPRVPage[5] = MMC5SPRVPage[6] = MMC5SPRVPage[7] = &CHRptr[0][(V) << 13]; + } +} +static INLINE void MMC5BGVROM_BANK8(uint32 V) { + if (CHRptr[0]) { + V &= CHRmask8[0]; MMC5BGVPage[0] = MMC5BGVPage[1] = MMC5BGVPage[2] = MMC5BGVPage[3] = MMC5BGVPage[4] = MMC5BGVPage[5] = MMC5BGVPage[6] = MMC5BGVPage[7] = &CHRptr[0][(V) << 13]; + } +} + +static uint8 PRGBanks[4]; +static uint8 WRAMPage; +static uint16 CHRBanksA[8], CHRBanksB[4]; +static uint8 WRAMMaskEnable[2]; +uint8 mmc5ABMode; /* A=0, B=1 */ + +static uint8 IRQScanline, IRQEnable; +static uint8 CHRMode, NTAMirroring, NTFill, ATFill; + +static uint8 MMC5IRQR; +static uint8 MMC5LineCounter; +static uint8 mmc5psize, mmc5vsize; +static uint8 mul[2]; + +static uint32 WRAMSIZE = 0; +static uint8 *WRAM = NULL; +static uint8 *MMC5fill = NULL; +static uint8 *ExRAM = NULL; + +const int MMC5WRAMMAX = 1<<7; // 7 bits in register interface (real MMC5 has only 4 pins, however) +static uint8 MMC5WRAMsize; //configuration, not state +static uint8 MMC5WRAMIndex[MMC5WRAMMAX]; //configuration, not state + +static uint8 MMC5ROMWrProtect[4]; +static uint8 MMC5MemIn[5]; + +static void MMC5CHRA(void); +static void MMC5CHRB(void); + +typedef struct __cartdata { + uint32 crc32; + uint8 size; +} cartdata; + +#define MMC5SPRVRAMADR(V) &MMC5SPRVPage[(V) >> 10][(V)] + +uint8* MMC5BGVRAMADR(uint32 A) +{ + if(newppu) + { + if(Sprite16) + { + bool isPattern = PPUON != 0; + if (ppuphase == PPUPHASE_OBJ && isPattern) + return &ABANKS[(A) >> 10][(A)]; + if (ppuphase == PPUPHASE_BG && isPattern) + return &BBANKS[(A) >> 10][(A)]; + else if(mmc5ABMode == 0) + return &ABANKS[(A) >> 10][(A)]; + else + return &BBANKS[(A) >> 10][(A)]; + } + else return &ABANKS[(A) >> 10][(A)];; + } + + if (!Sprite16) { + if (mmc5ABMode == 0) + return &ABANKS[(A) >> 10][(A)]; + else + return &BBANKS[(A) >> 10][(A)]; + } else return &BBANKS[(A) >> 10][(A)]; +} + +static void mmc5_PPUWrite(uint32 A, uint8 V) { + uint32 tmp = A; + extern uint8 PALRAM[0x20]; + extern uint8 UPALRAM[0x03]; + + if (tmp >= 0x3F00) { + if (!(tmp & 3)) { + if (!(tmp & 0xC)) { + PALRAM[0x00] = PALRAM[0x04] = PALRAM[0x08] = PALRAM[0x0C] = V & 0x3F; + PALRAM[0x10] = PALRAM[0x14] = PALRAM[0x18] = PALRAM[0x1C] = V & 0x3F; + } + else + UPALRAM[((tmp & 0xC) >> 2) - 1] = V & 0x3F; + } else + PALRAM[tmp & 0x1F] = V & 0x3F; + } else if (tmp < 0x2000) { + if (PPUCHRRAM & (1 << (tmp >> 10))) + VPage[tmp >> 10][tmp] = V; + } else { + if (PPUNTARAM & (1 << ((tmp & 0xF00) >> 10))) + vnapage[((tmp & 0xF00) >> 10)][tmp & 0x3FF] = V; + } +} + +extern uint32 NTRefreshAddr; +uint8 FASTCALL mmc5_PPURead(uint32 A) +{ + bool split = false; + if(newppu) + { + if((MMC5HackSPMode&0x80) && !(MMC5HackCHRMode&2)) + { + int target = MMC5HackSPMode&0x1f; + int side = MMC5HackSPMode&0x40; + int ht = NTRefreshAddr&31; + + if(side==0) + { + if(ht=target) split = true; + } + } + } + + if (A < 0x2000) + { + if(Sprite16) + { + bool isPattern = !!PPUON; + if (ppuphase == PPUPHASE_OBJ && isPattern) + return ABANKS[(A) >> 10][(A)]; + if (ppuphase == PPUPHASE_BG && isPattern) + { + if(split) + return MMC5HackVROMPTR[MMC5HackSPPage*0x1000 + (A&0xFFF)]; + + //uhhh call through to this more sophisticated function, only if it's really needed? + //we should probably reuse it completely, if we can + if (MMC5HackCHRMode == 1) + return *FCEUPPU_GetCHR(A,NTRefreshAddr); + + return BBANKS[(A) >> 10][(A)]; + } + else if(mmc5ABMode == 0) + return ABANKS[(A) >> 10][(A)]; + else + return BBANKS[(A) >> 10][(A)]; + } + else + { + if (ppuphase == PPUPHASE_BG && ScreenON) + { + if(split) + return MMC5HackVROMPTR[MMC5HackSPPage*0x1000 + (A&0xFFF)]; + + //uhhh call through to this more sophisticated function, only if it's really needed? + //we should probably reuse it completely, if we can + if (MMC5HackCHRMode == 1) + return *FCEUPPU_GetCHR(A,NTRefreshAddr); + } + + return ABANKS[(A) >> 10][(A)]; + } + } + else + { + if(split) + { + static const int kHack = -1; //dunno if theres science to this or if it just fixes SDF (cant be bothered to think about it) + int linetile = (newppu_get_scanline()+kHack)/8 + MMC5HackSPScroll; + + //REF NT: return 0x2000 | (v << 0xB) | (h << 0xA) | (vt << 5) | ht; + //REF AT: return 0x2000 | (v << 0xB) | (h << 0xA) | 0x3C0 | ((vt & 0x1C) << 1) | ((ht & 0x1C) >> 2); + + if((A&0x3FF)>=0x3C0) + { + A &= ~(0x1C<<1); //mask off VT + A |= (linetile&0x1C)<<1; //mask on adjusted VT + return ExRAM[A & 0x3FF]; + } + else + { + A &= ~((0x1F<<5) | (1<<0xB)); //mask off VT and V + A |= (linetile&31)<<5; //mask on adjusted VT (V doesnt make any sense, I think) + return ExRAM[A & 0x3FF]; + } + } + + if (MMC5HackCHRMode == 1) + { + if((A&0x3FF)>=0x3C0) + { + uint8 byte = ExRAM[NTRefreshAddr & 0x3ff]; + //get attribute part and paste it 4x across the byte + byte >>= 6; + byte *= 0x55; + return byte; + } + } + + return vnapage[(A >> 10) & 0x3][A & 0x3FF]; + } +} + +cartdata MMC5CartList[] = +{ + { 0x6f4e4312, 4 }, /* Aoki Ookami to Shiroki Mejika - Genchou Hishi */ + { 0x15fe6d0f, 2 }, /* Bandit Kings of Ancient China */ + { 0x671f23a8, 0 }, /* Castlevania III - Dracula's Curse (E) */ + { 0xcd4e7430, 0 }, /* Castlevania III - Dracula's Curse (KC) */ + { 0xed2465be, 0 }, /* Castlevania III - Dracula's Curse (U) */ + { 0xfe3488d1, 2 }, /* Daikoukai Jidai */ + { 0x0ec6c023, 1 }, /* Gemfire */ + { 0x0afb395e, 0 }, /* Gun Sight */ + { 0x1ced086f, 2 }, /* Ishin no Arashi */ + { 0x9cbadc25, 1 }, /* Just Breed */ + { 0x6396b988, 2 }, /* L'Empereur (J) */ + { 0x9c18762b, 2 }, /* L'Empereur (U) */ + { 0xb0480ae9, 0 }, /* Laser Invasion */ + { 0xb4735fac, 0 }, /* Metal Slader Glory */ + { 0xf540677b, 4 }, /* Nobunaga no Yabou - Bushou Fuuun Roku */ + { 0xeee9a682, 2 }, /* Nobunaga no Yabou - Sengoku Gunyuu Den (J) (PRG0) */ + { 0xf9b4240f, 2 }, /* Nobunaga no Yabou - Sengoku Gunyuu Den (J) (PRG1) */ + { 0x8ce478db, 2 }, /* Nobunaga's Ambition 2 */ + { 0xf011e490, 4 }, /* Romance of The Three Kingdoms II */ + { 0xbc80fb52, 1 }, /* Royal Blood */ + { 0x184c2124, 4 }, /* Sangokushi II (J) (PRG0) */ + { 0xee8e6553, 4 }, /* Sangokushi II (J) (PRG1) */ + { 0xd532e98f, 1 }, /* Shin 4 Nin Uchi Mahjong - Yakuman Tengoku */ + { 0x39f2ce4b, 2 }, /* Suikoden - Tenmei no Chikai */ + { 0xbb7f829a, 0 }, /* Uchuu Keibitai SDF */ + { 0xaca15643, 2 }, /* Uncharted Waters */ +}; + +#define MMC5_NOCARTS (sizeof(MMC5CartList) / sizeof(MMC5CartList[0])) +int DetectMMC5WRAMSize(uint32 crc32) { + int x; + for (x = 0; x < (int)MMC5_NOCARTS; x++) { + if (crc32 == MMC5CartList[x].crc32) { + if(MMC5CartList[x].size > 1) + FCEU_printf(" >8KB external WRAM present. Use UNIF if you hack the ROM image.\n"); + return(MMC5CartList[x].size * 8); + } + } + return 64; +} + +static void BuildWRAMSizeTable(void) { + bool other = false; // non-standard configuration + // fill first 8 entries + int x; + for (x = 0; x < 8; x++) { + switch (MMC5WRAMsize) { + case 0: MMC5WRAMIndex[x] = 255; break; //X,X,X,X,X,X,X,X + case 1: MMC5WRAMIndex[x] = (x > 3) ? 255 : 0; break; //0,0,0,0,X,X,X,X + case 2: MMC5WRAMIndex[x] = (x & 4) >> 2; break; //0,0,0,0,1,1,1,1 + case 4: MMC5WRAMIndex[x] = (x > 3) ? 255 : (x & 3); break; //0,1,2,3,X,X,X,X + case 8: MMC5WRAMIndex[x] = x; break; //0,1,2,3,4,5,6,7 + default: MMC5WRAMIndex[x] = x; other = true; break; //0,1,2... + } + } + // extend to fill complete table + if (other) + { + for (x = 0; x < MMC5WRAMMAX && x < MMC5WRAMsize; ++x) MMC5WRAMIndex[x] = x; // linear mapping + for (x = MMC5WRAMsize; x < MMC5WRAMMAX; ++x) MMC5WRAMIndex[x] = MMC5WRAMIndex[x-MMC5WRAMsize]; // repeat to fill table + // theoretically the table fill should decompose into powers of two for possible mismatched SRAM combos, + // but I don't want to complicate the code with unnecessary hypotheticals + } + else + { + for (x = 8; x < MMC5WRAMMAX; ++x) MMC5WRAMIndex[x] = MMC5WRAMIndex[x & 7]; // fill table, repeating groups of 8 + } +} + +static void MMC5CHRA(void) { + int x; + switch (mmc5vsize & 3) { + case 0: + setchr8(CHRBanksA[7]); + MMC5SPRVROM_BANK8(CHRBanksA[7]); + break; + case 1: + setchr4(0x0000, CHRBanksA[3]); + setchr4(0x1000, CHRBanksA[7]); + MMC5SPRVROM_BANK4(0x0000, CHRBanksA[3]); + MMC5SPRVROM_BANK4(0x1000, CHRBanksA[7]); + break; + case 2: + setchr2(0x0000, CHRBanksA[1]); + setchr2(0x0800, CHRBanksA[3]); + setchr2(0x1000, CHRBanksA[5]); + setchr2(0x1800, CHRBanksA[7]); + MMC5SPRVROM_BANK2(0x0000, CHRBanksA[1]); + MMC5SPRVROM_BANK2(0x0800, CHRBanksA[3]); + MMC5SPRVROM_BANK2(0x1000, CHRBanksA[5]); + MMC5SPRVROM_BANK2(0x1800, CHRBanksA[7]); + break; + case 3: + for (x = 0; x < 8; x++) { + setchr1(x << 10, CHRBanksA[x]); + MMC5SPRVROM_BANK1(x << 10, CHRBanksA[x]); + } + break; + } +} + +static void MMC5CHRB(void) { + int x; + switch (mmc5vsize & 3) { + case 0: + setchr8(CHRBanksB[3]); + MMC5BGVROM_BANK8(CHRBanksB[3]); + break; + case 1: + setchr4(0x0000, CHRBanksB[3]); + setchr4(0x1000, CHRBanksB[3]); + MMC5BGVROM_BANK4(0x0000, CHRBanksB[3]); + MMC5BGVROM_BANK4(0x1000, CHRBanksB[3]); + break; + case 2: + setchr2(0x0000, CHRBanksB[1]); + setchr2(0x0800, CHRBanksB[3]); + setchr2(0x1000, CHRBanksB[1]); + setchr2(0x1800, CHRBanksB[3]); + MMC5BGVROM_BANK2(0x0000, CHRBanksB[1]); + MMC5BGVROM_BANK2(0x0800, CHRBanksB[3]); + MMC5BGVROM_BANK2(0x1000, CHRBanksB[1]); + MMC5BGVROM_BANK2(0x1800, CHRBanksB[3]); + break; + case 3: + for (x = 0; x < 8; x++) { + setchr1(x << 10, CHRBanksB[x & 3]); + MMC5BGVROM_BANK1(x << 10, CHRBanksB[x & 3]); + } + break; + } +} + +static void MMC5WRAM(uint32 A, uint32 V) { + V = MMC5WRAMIndex[V & (MMC5WRAMMAX-1)]; + if (V != 255) { + setprg8r(0x10, A, V); + FCEU_CheatAddRAM(8, 0x6000, (WRAM + ((V * 8192) & (WRAMSIZE - 1)))); + MMC5MemIn[(A - 0x6000) >> 13] = 1; + } else + MMC5MemIn[(A - 0x6000) >> 13] = 0; +} + +static void MMC5PRG(void) { + int x; + switch (mmc5psize & 3) { + case 0: + MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = MMC5ROMWrProtect[2] = MMC5ROMWrProtect[3] = 1; + setprg32(0x8000, ((PRGBanks[1] & 0x7F) >> 2)); + for (x = 0; x < 4; x++) + MMC5MemIn[1 + x] = 1; + break; + case 1: + if (PRGBanks[1] & 0x80) { + MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 1; + setprg16(0x8000, (PRGBanks[1] >> 1)); + MMC5MemIn[1] = MMC5MemIn[2] = 1; + } else { + MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 0; + MMC5WRAM(0x8000, PRGBanks[1] & (MMC5WRAMMAX-1) & 0xFE); + MMC5WRAM(0xA000, (PRGBanks[1] & (MMC5WRAMMAX-1) & 0xFE) + 1); + } + MMC5MemIn[3] = MMC5MemIn[4] = 1; + MMC5ROMWrProtect[2] = MMC5ROMWrProtect[3] = 1; + setprg16(0xC000, (PRGBanks[3] & 0x7F) >> 1); + break; + case 2: + if (PRGBanks[1] & 0x80) { + MMC5MemIn[1] = MMC5MemIn[2] = 1; + MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 1; + setprg16(0x8000, (PRGBanks[1] & 0x7F) >> 1); + } else { + MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 0; + MMC5WRAM(0x8000, PRGBanks[1] & (MMC5WRAMMAX-1) & 0xFE); + MMC5WRAM(0xA000, (PRGBanks[1] & (MMC5WRAMMAX-1) & 0xFE) + 1); + } + if (PRGBanks[2] & 0x80) { + MMC5ROMWrProtect[2] = 1; + MMC5MemIn[3] = 1; + setprg8(0xC000, PRGBanks[2] & 0x7F); + } else { + MMC5ROMWrProtect[2] = 0; + MMC5WRAM(0xC000, PRGBanks[2] & (MMC5WRAMMAX-1)); + } + MMC5MemIn[4] = 1; + MMC5ROMWrProtect[3] = 1; + setprg8(0xE000, PRGBanks[3] & 0x7F); + break; + case 3: + for (x = 0; x < 3; x++) + if (PRGBanks[x] & 0x80) { + MMC5ROMWrProtect[x] = 1; + setprg8(0x8000 + (x << 13), PRGBanks[x] & 0x7F); + MMC5MemIn[1 + x] = 1; + } else { + MMC5ROMWrProtect[x] = 0; + MMC5WRAM(0x8000 + (x << 13), PRGBanks[x] & (MMC5WRAMMAX-1)); + } + MMC5MemIn[4] = 1; + MMC5ROMWrProtect[3] = 1; + setprg8(0xE000, PRGBanks[3] & 0x7F); + break; + } +} + +static DECLFW(Mapper5_write) { + switch (A) { + case 0x5100: + mmc5psize = V; + MMC5PRG(); + break; + case 0x5101: + mmc5vsize = V; + if (!mmc5ABMode) { + MMC5CHRB(); + MMC5CHRA(); + } else { + MMC5CHRA(); + MMC5CHRB(); + } + break; + case 0x5102: + WRAMMaskEnable[0] = V; + break; + case 0x5103: + WRAMMaskEnable[1] = V; + break; + case 0x5104: + CHRMode = V; + MMC5HackCHRMode = V & 3; + break; + case 0x5105: + { + int x; + for (x = 0; x < 4; x++) { + switch ((V >> (x << 1)) & 3) { + case 0: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM; break; + case 1: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM + 0x400; break; + case 2: PPUNTARAM |= 1 << x; vnapage[x] = ExRAM; break; + case 3: PPUNTARAM &= ~(1 << x); vnapage[x] = MMC5fill; break; + } + } + NTAMirroring = V; + break; + } + case 0x5106: + if (V != NTFill) + FCEU_dwmemset(MMC5fill, (V | (V << 8) | (V << 16) | (V << 24)), 0x3c0); + NTFill = V; + break; + case 0x5107: + if (V != ATFill) { + unsigned char moop = V | (V << 2) | (V << 4) | (V << 6); + FCEU_dwmemset(MMC5fill + 0x3c0, moop | (moop << 8) | (moop << 16) | (moop << 24), 0x40); + } + ATFill = V; + break; + case 0x5113: + WRAMPage = V; + MMC5WRAM(0x6000, V & (MMC5WRAMMAX-1)); + break; + case 0x5114: + case 0x5115: + case 0x5116: + case 0x5117: + PRGBanks[A & 3] = V; + MMC5PRG(); + break; + case 0x5120: + case 0x5121: + case 0x5122: + case 0x5123: + case 0x5124: + case 0x5125: + case 0x5126: + case 0x5127: + mmc5ABMode = 0; + CHRBanksA[A & 7] = V | ((MMC50x5130 & 0x3) << 8); + MMC5CHRA(); + break; + case 0x5128: + case 0x5129: + case 0x512a: + case 0x512b: + mmc5ABMode = 1; + CHRBanksB[A & 3] = V | ((MMC50x5130 & 0x3) << 8); + MMC5CHRB(); + break; + case 0x5130: MMC50x5130 = V; break; + case 0x5200: MMC5HackSPMode = V; break; + case 0x5201: MMC5HackSPScroll = (V >> 3) & 0x1F; break; + case 0x5202: MMC5HackSPPage = V & 0x3F; break; + case 0x5203: X6502_IRQEnd(FCEU_IQEXT); IRQScanline = V; break; + case 0x5204: X6502_IRQEnd(FCEU_IQEXT); IRQEnable = V & 0x80; break; + case 0x5205: mul[0] = V; break; + case 0x5206: mul[1] = V; break; + } +} + +static DECLFR(MMC5_ReadROMRAM) { + if (MMC5MemIn[(A - 0x6000) >> 13]) + return Page[A >> 11][A]; + else + return X.DB; +} + +static DECLFW(MMC5_WriteROMRAM) { + if ((A >= 0x8000) && (MMC5ROMWrProtect[(A - 0x8000) >> 13])) + return; + if (MMC5MemIn[(A - 0x6000) >> 13]) + if (((WRAMMaskEnable[0] & 3) | ((WRAMMaskEnable[1] & 3) << 2)) == 6) + Page[A >> 11][A] = V; +} + +static DECLFW(MMC5_ExRAMWr) { + if (MMC5HackCHRMode != 3) + ExRAM[A & 0x3ff] = V; +} + +static DECLFR(MMC5_ExRAMRd) { + return ExRAM[A & 0x3ff]; +} + +static DECLFR(MMC5_read) { + switch (A) { + case 0x5204: { + uint8 x; + X6502_IRQEnd(FCEU_IQEXT); + x = MMC5IRQR; + #ifdef FCEUDEF_DEBUGGER + if (!fceuindbg) + #endif + MMC5IRQR &= 0x40; + return x; + } + case 0x5205: + return(mul[0] * mul[1]); + case 0x5206: + return((mul[0] * mul[1]) >> 8); + } + return(X.DB); +} + +void MMC5Synco(void) { + int x; + + MMC5PRG(); + for (x = 0; x < 4; x++) { + switch ((NTAMirroring >> (x << 1)) & 3) { + case 0: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM; break; + case 1: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM + 0x400; break; + case 2: PPUNTARAM |= 1 << x; vnapage[x] = ExRAM; break; + case 3: PPUNTARAM &= ~(1 << x); vnapage[x] = MMC5fill; break; + } + } + MMC5WRAM(0x6000, WRAMPage & (MMC5WRAMMAX-1)); + if (!mmc5ABMode) { + MMC5CHRB(); + MMC5CHRA(); + } else { + MMC5CHRA(); + MMC5CHRB(); + } + + //in case the fill register changed, we need to overwrite the fill buffer + FCEU_dwmemset(MMC5fill, NTFill | (NTFill << 8) | (NTFill << 16) | (NTFill << 24), 0x3c0); + { + unsigned char moop = ATFill | (ATFill << 2) | (ATFill << 4) | (ATFill << 6); + FCEU_dwmemset(MMC5fill + 0x3c0, moop | (moop << 8) | (moop << 16) | (moop << 24), 0x40); + } + + MMC5HackCHRMode = CHRMode & 3; + + //zero 17-apr-2013 - why the heck should this happen here? anything in a `synco` should be depending on the state. + //im going to leave it commented out to see what happens + //X6502_IRQEnd(FCEU_IQEXT); +} + +void MMC5_hb(int scanline) { + //zero 24-jul-2014 - revised for newer understanding, to fix metal slader glory credits. see r7371 in bizhawk + + int sl = scanline + 1; + int ppuon = (PPU[1] & 0x18); + + if (!ppuon || sl >= 241) + { + // whenever rendering is off for any reason (vblank or forced disable + // the irq counter resets, as well as the inframe flag (easily verifiable from software) + MMC5IRQR &= ~0x40; + MMC5IRQR &= ~0x80; + MMC5LineCounter = 0; + X6502_IRQEnd(FCEU_IQEXT); + return; + } + + if (!(MMC5IRQR&0x40)) + { + MMC5IRQR |= 0x40; + MMC5IRQR &= ~0x80; + MMC5LineCounter = 0; + X6502_IRQEnd(FCEU_IQEXT); + } + else + { + MMC5LineCounter++; + if (MMC5LineCounter == IRQScanline) + { + MMC5IRQR |= 0x80; + if (IRQEnable & 0x80) + X6502_IRQBegin(FCEU_IQEXT); + } + } + +} + +void MMC5_StateRestore(int version) { + MMC5Synco(); +} + +typedef struct { + uint16 wl[2]; + uint8 env[2]; + uint8 enable; + uint8 running; + uint8 raw; + uint8 rawcontrol; + int32 dcount[2]; + int32 BC[3]; + int32 vcount[2]; +} MMC5APU; + +static MMC5APU MMC5Sound; + + +static void Do5PCM() { + int32 V; + int32 start, end; + + start = MMC5Sound.BC[2]; + end = (SOUNDTS << 16) / soundtsinc; + if (end <= start) return; + MMC5Sound.BC[2] = end; + + if (!(MMC5Sound.rawcontrol & 0x40) && MMC5Sound.raw) + for (V = start; V < end; V++) + Wave[V >> 4] += MMC5Sound.raw << 1; +} + +static void Do5PCMHQ() { + uint32 V; + if (!(MMC5Sound.rawcontrol & 0x40) && MMC5Sound.raw) + for (V = MMC5Sound.BC[2]; V < SOUNDTS; V++) + WaveHi[V] += MMC5Sound.raw << 5; + MMC5Sound.BC[2] = SOUNDTS; +} + + +static DECLFW(Mapper5_SW) { + A &= 0x1F; + + GameExpSound.Fill = MMC5RunSound; + GameExpSound.HiFill = MMC5RunSoundHQ; + + switch (A) { + case 0x10: if (psfun) psfun(); MMC5Sound.rawcontrol = V; break; + case 0x11: if (psfun) psfun(); MMC5Sound.raw = V; break; + + case 0x0: + case 0x4: + if (sfun) sfun(A >> 2); + MMC5Sound.env[A >> 2] = V; + break; + case 0x2: + case 0x6: + if (sfun) sfun(A >> 2); + MMC5Sound.wl[A >> 2] &= ~0x00FF; + MMC5Sound.wl[A >> 2] |= V & 0xFF; + break; + case 0x3: + case 0x7: + MMC5Sound.wl[A >> 2] &= ~0x0700; + MMC5Sound.wl[A >> 2] |= (V & 0x07) << 8; + MMC5Sound.running |= 1 << (A >> 2); + break; + case 0x15: + if (sfun) { + sfun(0); + sfun(1); + } + MMC5Sound.running &= V; + MMC5Sound.enable = V; + break; + } +} + +static void Do5SQ(int P) { + static int tal[4] = { 1, 2, 4, 6 }; + int32 V, amp, rthresh, wl; + int32 start, end; + + start = MMC5Sound.BC[P]; + end = (SOUNDTS << 16) / soundtsinc; + if (end <= start) return; + MMC5Sound.BC[P] = end; + + wl = MMC5Sound.wl[P] + 1; + amp = (MMC5Sound.env[P] & 0xF) << 4; + rthresh = tal[(MMC5Sound.env[P] & 0xC0) >> 6]; + + if (wl >= 8 && (MMC5Sound.running & (P + 1))) { + int dc, vc; + + wl <<= 18; + dc = MMC5Sound.dcount[P]; + vc = MMC5Sound.vcount[P]; + + for (V = start; V < end; V++) { + if (dc < rthresh) + Wave[V >> 4] += amp; + vc -= nesincsize; + while (vc <= 0) { + vc += wl; + dc = (dc + 1) & 7; + } + } + MMC5Sound.dcount[P] = dc; + MMC5Sound.vcount[P] = vc; + } +} + +static void Do5SQHQ(int P) { + static int tal[4] = { 1, 2, 4, 6 }; + uint32 V; + int32 amp, rthresh, wl; + + wl = MMC5Sound.wl[P] + 1; + amp = ((MMC5Sound.env[P] & 0xF) << 8); + rthresh = tal[(MMC5Sound.env[P] & 0xC0) >> 6]; + + if (wl >= 8 && (MMC5Sound.running & (P + 1))) { + int dc, vc; + + wl <<= 1; + + dc = MMC5Sound.dcount[P]; + vc = MMC5Sound.vcount[P]; + for (V = MMC5Sound.BC[P]; V < SOUNDTS; V++) { + if (dc < rthresh) + WaveHi[V] += amp; + vc--; + if (vc <= 0) { /* Less than zero when first started. */ + vc = wl; + dc = (dc + 1) & 7; + } + } + MMC5Sound.dcount[P] = dc; + MMC5Sound.vcount[P] = vc; + } + MMC5Sound.BC[P] = SOUNDTS; +} + +void MMC5RunSoundHQ(void) { + Do5SQHQ(0); + Do5SQHQ(1); + Do5PCMHQ(); +} + +void MMC5HiSync(int32 ts) { + int x; + for (x = 0; x < 3; x++) + MMC5Sound.BC[x] = ts; +} + +void MMC5RunSound(int Count) { + int x; + Do5SQ(0); + Do5SQ(1); + Do5PCM(); + for (x = 0; x < 3; x++) + MMC5Sound.BC[x] = Count; +} + +void Mapper5_ESI(void) { + GameExpSound.RChange = Mapper5_ESI; + if (FSettings.SndRate) { + if (FSettings.soundq >= 1) { + sfun = Do5SQHQ; + psfun = Do5PCMHQ; + } else { + sfun = Do5SQ; + psfun = Do5PCM; + } + } else { + sfun = 0; + psfun = 0; + } + memset(MMC5Sound.BC, 0, sizeof(MMC5Sound.BC)); + memset(MMC5Sound.vcount, 0, sizeof(MMC5Sound.vcount)); + GameExpSound.HiSync = MMC5HiSync; +} + +void NSFMMC5_Init(void) { + memset(&MMC5Sound, 0, sizeof(MMC5Sound)); + mul[0] = mul[1] = 0; + ExRAM = (uint8*)FCEU_gmalloc(1024); + Mapper5_ESI(); + SetWriteHandler(0x5c00, 0x5fef, MMC5_ExRAMWr); + SetReadHandler(0x5c00, 0x5fef, MMC5_ExRAMRd); + MMC5HackCHRMode = 2; + SetWriteHandler(0x5000, 0x5015, Mapper5_SW); + SetWriteHandler(0x5205, 0x5206, Mapper5_write); + SetReadHandler(0x5205, 0x5206, MMC5_read); +} + +void NSFMMC5_Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; + FCEU_gfree(ExRAM); + ExRAM = NULL; +} + +static void GenMMC5Reset(void) { + int x; + + for (x = 0; x < 4; x++) PRGBanks[x] = ~0; + for (x = 0; x < 8; x++) CHRBanksA[x] = ~0; + for (x = 0; x < 4; x++) CHRBanksB[x] = ~0; + WRAMMaskEnable[0] = WRAMMaskEnable[1] = ~0; + + mmc5psize = mmc5vsize = 3; + CHRMode = 0; + + NTAMirroring = NTFill = ATFill = 0xFF; + + MMC5Synco(); + + SetWriteHandler(0x4020, 0x5bff, Mapper5_write); + SetReadHandler(0x4020, 0x5bff, MMC5_read); + + SetWriteHandler(0x5c00, 0x5fff, MMC5_ExRAMWr); + SetReadHandler(0x5c00, 0x5fff, MMC5_ExRAMRd); + + SetWriteHandler(0x6000, 0xFFFF, MMC5_WriteROMRAM); + SetReadHandler(0x6000, 0xFFFF, MMC5_ReadROMRAM); + + SetWriteHandler(0x5000, 0x5015, Mapper5_SW); + SetWriteHandler(0x5205, 0x5206, Mapper5_write); + SetReadHandler(0x5205, 0x5206, MMC5_read); + +// GameHBIRQHook=MMC5_hb; +// FCEU_CheatAddRAM(8, 0x6000, WRAM); + FCEU_CheatAddRAM(1, 0x5c00, ExRAM); +} + +static SFORMAT MMC5_StateRegs[] = { + { PRGBanks, 4, "PRGB" }, + { CHRBanksA, 16, "CHRA" }, + { CHRBanksB, 8, "CHRB" }, + { &WRAMPage, 1, "WRMP" }, + { WRAMMaskEnable, 2, "WRME" }, + { &mmc5ABMode, 1, "ABMD" }, + { &IRQScanline, 1, "IRQS" }, + { &IRQEnable, 1, "IRQE" }, + { &CHRMode, 1, "CHRM" }, + { &NTAMirroring, 1, "NTAM" }, + { &NTFill, 1, "NTFL" }, + { &ATFill, 1, "ATFL" }, + + //zero 17-apr-2013 - added + { &MMC5IRQR, 1, "IRQR" }, + { &MMC5LineCounter, 1, "LCTR" }, + { &mmc5psize, 1, "PSIZ" }, + { &mmc5vsize, 1, "VSIZ" }, + { mul, 2, "MUL2" }, + { MMC5ROMWrProtect, 4, "WRPR" }, + { MMC5MemIn, 5, "MEMI" }, + + { &MMC5Sound.wl[0], 2 | FCEUSTATE_RLSB, "SDW0" }, + { &MMC5Sound.wl[1], 2 | FCEUSTATE_RLSB, "SDW1" }, + { MMC5Sound.env, 2, "SDEV" }, + { &MMC5Sound.enable, 1, "SDEN" }, + { &MMC5Sound.running, 1, "SDRU" }, + { &MMC5Sound.raw, 1, "SDRW" }, + { &MMC5Sound.rawcontrol, 1, "SDRC" }, + + //zero 17-apr-2013 - added + { &MMC5Sound.dcount[0], 4 | FCEUSTATE_RLSB, "DCT0" }, + { &MMC5Sound.dcount[1], 4 | FCEUSTATE_RLSB, "DCT1" }, + { &MMC5Sound.BC[0], 4 | FCEUSTATE_RLSB, "BC00" }, + { &MMC5Sound.BC[1], 4 | FCEUSTATE_RLSB, "BC01" }, + { &MMC5Sound.BC[2], 4 | FCEUSTATE_RLSB, "BC02" }, + { &MMC5Sound.vcount[0], 4 | FCEUSTATE_RLSB, "VCT0" }, + { &MMC5Sound.vcount[1], 4 | FCEUSTATE_RLSB, "VCT1" }, + { 0 } +}; + +static void GenMMC5_Init(CartInfo *info, int wsize, int battery) { + if (wsize) { + WRAM = (uint8*)FCEU_gmalloc(wsize * 1024); + SetupCartPRGMapping(0x10, WRAM, wsize * 1024, 1); + AddExState(WRAM, wsize * 1024, 0, "WRAM"); + } + + MMC5fill = (uint8*)FCEU_gmalloc(1024); + ExRAM = (uint8*)FCEU_gmalloc(1024); + + // MMC5fill is and 8-bit tile index, and a 2-bit attribute implented as a mirrored nametable + u8 nval = MMC5fill[0x000]; + u8 aval = MMC5fill[0x3C0] & 3; aval = aval | (aval << 2) | (aval << 4) | (aval << 6); + FCEU_dwmemset(MMC5fill + 0x000, nval | (nval<<8) | (nval<<16) | (nval<<24), 0x3C0); + FCEU_dwmemset(MMC5fill + 0x3C0, aval | (aval<<8) | (aval<<16) | (aval<<24), 0x040); + + AddExState(ExRAM, 1024, 0, "ERAM"); + AddExState(&MMC5HackSPMode, 1, 0, "SPLM"); + AddExState(&MMC5HackSPScroll, 1, 0, "SPLS"); + AddExState(&MMC5HackSPPage, 1, 0, "SPLP"); + AddExState(&MMC50x5130, 1, 0, "5130"); + AddExState(MMC5_StateRegs, ~0, 0, 0); + + MMC5WRAMsize = wsize / 8; + BuildWRAMSizeTable(); + GameStateRestore = MMC5_StateRestore; + info->Power = GenMMC5Reset; + + if (battery) { + info->SaveGame[0] = WRAM; + if (info->ines2) + { + info->SaveGameLen[0] = info->battery_wram_size; + } + else + { + //this is more complex than it looks because it MUST BE, I guess. is there an assumption that only 8KB of 16KB is battery backed? That's NES mappers for you + //I added 64KB for the new 64KB homebrews + if (wsize <= 16) + info->SaveGameLen[0] = 8192; + else if(wsize == 64) + info->SaveGameLen[0] = 64*1024; + else + info->SaveGameLen[0] = 32768; + } + } + + MMC5HackVROMMask = CHRmask4[0]; + MMC5HackExNTARAMPtr = ExRAM; + MMC5Hack = 1; + MMC5HackVROMPTR = CHRptr[0]; + MMC5HackCHRMode = 0; + MMC5HackSPMode = MMC5HackSPScroll = MMC5HackSPPage = 0; + Mapper5_ESI(); + + FFCEUX_PPURead = mmc5_PPURead; + FFCEUX_PPUWrite = mmc5_PPUWrite; +} + +void Mapper5_Init(CartInfo *info) { + if (info->ines2) + { + WRAMSIZE = (info->wram_size + info->battery_wram_size) / 1024; + } + else + { + WRAMSIZE = DetectMMC5WRAMSize(info->CRC32); + } + GenMMC5_Init(info, WRAMSIZE, info->battery); +} + +// ELROM seems to have 0KB of WRAM +// EKROM seems to have 8KB of WRAM, battery-backed +// ETROM seems to have 16KB of WRAM, battery-backed +// EWROM seems to have 32KB of WRAM, battery-backed + +void ELROM_Init(CartInfo *info) { + GenMMC5_Init(info, 0, 0); +} + +void EKROM_Init(CartInfo *info) { + GenMMC5_Init(info, 8, info->battery); +} + +void ETROM_Init(CartInfo *info) { + GenMMC5_Init(info, 16, info->battery); +} + +void EWROM_Init(CartInfo *info) { + GenMMC5_Init(info, 32, info->battery); +} diff --git a/apps/fceux/src/boards/n106.cpp b/apps/fceux/src/boards/n106.cpp new file mode 100644 index 00000000..76c93405 --- /dev/null +++ b/apps/fceux/src/boards/n106.cpp @@ -0,0 +1,448 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint16 IRQCount; +static uint8 IRQa; + +static uint8 WRAM[8192]; +static uint8 IRAM[128]; + +static DECLFR(AWRAM) { + return(WRAM[A - 0x6000]); +} + +static DECLFW(BWRAM) { + WRAM[A - 0x6000] = V; +} + +void Mapper19_ESI(void); + +static uint8 NTAPage[4]; + +static uint8 dopol; +static uint8 gorfus; +static uint8 gorko; + +static void NamcoSound(int Count); +static void NamcoSoundHack(void); +static void DoNamcoSound(int32 *Wave, int Count); +static void DoNamcoSoundHQ(void); +static void SyncHQ(int32 ts); + +static int is210; /* Lesser mapper. */ + +static uint8 PRG[3]; +static uint8 CHR[8]; + +static SFORMAT N106_StateRegs[] = { + { PRG, 3, "PRG" }, + { CHR, 8, "CHR" }, + { NTAPage, 4, "NTA" }, + { &gorfus, 1, "GORF" }, + { &dopol, 1, "DOPO" }, + { &gorko, 1, "GORK" }, + { 0 } +}; + +static void SyncMirror() +{ + switch(gorko) { + case 0: setmirror(MI_0); break; + case 1: setmirror(MI_V); break; + case 2: setmirror(MI_H); break; + case 3: setmirror(MI_0); break; + } + +} + +static void SyncPRG(void) { + setprg8(0x8000, PRG[0]); + setprg8(0xa000, PRG[1]); + setprg8(0xc000, PRG[2]); + setprg8(0xe000, 0x3F); +} + +static void NamcoIRQHook(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount >= 0x7FFF) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + IRQCount = 0x7FFF; //7FFF; + } + } +} + +static DECLFR(Namco_Read4800) { + uint8 ret = IRAM[dopol & 0x7f]; + /* Maybe I should call NamcoSoundHack() here? */ + #ifdef FCEUDEF_DEBUGGER + if (!fceuindbg) + #endif + if (dopol & 0x80) + dopol = (dopol & 0x80) | ((dopol + 1) & 0x7f); + return ret; +} + +static DECLFR(Namco_Read5000) { + return(IRQCount); +} + +static DECLFR(Namco_Read5800) { + return(IRQCount >> 8); +} + +static void DoNTARAMROM(int w, uint8 V) { + NTAPage[w] = V; + if (V >= 0xE0) + setntamem(NTARAM + ((V & 1) << 10), 1, w); + else{ + V &= CHRmask1[0]; + setntamem(CHRptr[0] + (V << 10), 0, w); + } +} + +static void FixNTAR(void) { + int x; + for (x = 0; x < 4; x++) + DoNTARAMROM(x, NTAPage[x]); +} + +static void DoCHRRAMROM(int x, uint8 V) { + CHR[x] = V; + if (!is210 && !((gorfus >> ((x >> 2) + 6)) & 1) && (V >= 0xE0)) { +// printf("BLAHAHA: %d, %02x\n",x,V); +// setchr1r(0x10,x<<10,V&7); + } else + setchr1(x << 10, V); +} + +static void FixCRR(void) { + int x; + for (x = 0; x < 8; x++) + DoCHRRAMROM(x, CHR[x]); +} + +static DECLFW(Mapper19C0D8_write) { + DoNTARAMROM((A - 0xC000) >> 11, V); +} + +static uint32 FreqCache[8]; +static uint32 EnvCache[8]; +static uint32 LengthCache[8]; + +static void FixCache(int a, int V) { + int w = (a >> 3) & 0x7; + switch (a & 0x07) { + case 0x00: FreqCache[w] &= ~0x000000FF; FreqCache[w] |= V; break; + case 0x02: FreqCache[w] &= ~0x0000FF00; FreqCache[w] |= V << 8; break; + case 0x04: + FreqCache[w] &= ~0x00030000; FreqCache[w] |= (V & 3) << 16; + LengthCache[w] = (8 - ((V >> 2) & 7)) << 2; + break; + case 0x07: EnvCache[w] = (double)(V & 0xF) * 576716; break; + } +} + +static DECLFW(Mapper19_write) { + A &= 0xF800; + if (A >= 0x8000 && A <= 0xb800) + DoCHRRAMROM((A - 0x8000) >> 11, V); + else + switch (A) { + case 0x4800: + if (dopol & 0x40) { + if (FSettings.SndRate) { + NamcoSoundHack(); + GameExpSound.Fill = NamcoSound; + GameExpSound.HiFill = DoNamcoSoundHQ; + GameExpSound.HiSync = SyncHQ; + } + FixCache(dopol, V); + } + IRAM[dopol & 0x7f] = V; + if (dopol & 0x80) + dopol = (dopol & 0x80) | ((dopol + 1) & 0x7f); + break; + case 0xf800: + dopol = V; break; + case 0x5000: + IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x5800: + IRQCount &= 0x00ff; IRQCount |= (V & 0x7F) << 8; + IRQa = V & 0x80; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xE000: + PRG[0] = V & 0x3F; + if(is210) { + gorko = V>>6; + SyncMirror(); + } + SyncPRG(); + break; + case 0xE800: + gorfus = V & 0xC0; + FixCRR(); + PRG[1] = V & 0x3F; + SyncPRG(); + break; + case 0xF000: + PRG[2] = V & 0x3F; + SyncPRG(); + break; + } +} + +static int dwave = 0; + +static void NamcoSoundHack(void) { + int32 z, a; + if (FSettings.soundq >= 1) { + DoNamcoSoundHQ(); + return; + } + z = ((SOUNDTS << 16) / soundtsinc) >> 4; + a = z - dwave; + if (a) DoNamcoSound(&Wave[dwave], a); + dwave += a; +} + +static void NamcoSound(int Count) { + int32 z, a; + z = ((SOUNDTS << 16) / soundtsinc) >> 4; + a = z - dwave; + if (a) DoNamcoSound(&Wave[dwave], a); + dwave = 0; +} + +static uint32 PlayIndex[8]; +static int32 vcount[8]; +static int32 CVBC; + +#define TOINDEX (16 + 1) + +// 16:15 +static void SyncHQ(int32 ts) { + CVBC = ts; +} + + +/* Things to do: + 1 Read freq low + 2 Read freq mid + 3 Read freq high + 4 Read envelope + ...? +*/ + +static INLINE uint32 FetchDuff(uint32 P, uint32 envelope) { + uint32 duff; + duff = IRAM[((IRAM[0x46 + (P << 3)] + (PlayIndex[P] >> TOINDEX)) & 0xFF) >> 1]; + if ((IRAM[0x46 + (P << 3)] + (PlayIndex[P] >> TOINDEX)) & 1) + duff >>= 4; + duff &= 0xF; + duff = (duff * envelope) >> 16; + return(duff); +} + +static void DoNamcoSoundHQ(void) { + int32 P, V; + int32 cyclesuck = (((IRAM[0x7F] >> 4) & 7) + 1) * 15; + + for (P = 7; P >= (7 - ((IRAM[0x7F] >> 4) & 7)); P--) { + if ((IRAM[0x44 + (P << 3)] & 0xE0) && (IRAM[0x47 + (P << 3)] & 0xF)) { + uint32 freq; + int32 vco; + uint32 duff2, lengo, envelope; + + vco = vcount[P]; + freq = FreqCache[P]; + envelope = EnvCache[P]; + lengo = LengthCache[P]; + + duff2 = FetchDuff(P, envelope); + for (V = CVBC << 1; V < (int)SOUNDTS << 1; V++) { + WaveHi[V >> 1] += duff2; + if (!vco) { + PlayIndex[P] += freq; + while ((PlayIndex[P] >> TOINDEX) >= lengo) PlayIndex[P] -= lengo << TOINDEX; + duff2 = FetchDuff(P, envelope); + vco = cyclesuck; + } + vco--; + } + vcount[P] = vco; + } + } + CVBC = SOUNDTS; +} + + +static void DoNamcoSound(int32 *Wave, int Count) { + int P, V; + for (P = 7; P >= 7 - ((IRAM[0x7F] >> 4) & 7); P--) { + if ((IRAM[0x44 + (P << 3)] & 0xE0) && (IRAM[0x47 + (P << 3)] & 0xF)) { + int32 inc; + uint32 freq; + int32 vco; + uint32 duff, duff2, lengo, envelope; + + vco = vcount[P]; + freq = FreqCache[P]; + envelope = EnvCache[P]; + lengo = LengthCache[P]; + + if (!freq) { /*printf("Ack");*/ + continue; + } + + { + int c = ((IRAM[0x7F] >> 4) & 7) + 1; + inc = (long double)(FSettings.SndRate << 15) / ((long double)freq * 21477272 / ((long double)0x400000 * c * 45)); + } + + duff = IRAM[(((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 0xFF) >> 1)]; + if ((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 1) + duff >>= 4; + duff &= 0xF; + duff2 = (duff * envelope) >> 19; + for (V = 0; V < Count * 16; V++) { + if (vco >= inc) { + PlayIndex[P]++; + if (PlayIndex[P] >= lengo) + PlayIndex[P] = 0; + vco -= inc; + duff = IRAM[(((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 0xFF) >> 1)]; + if ((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 1) + duff >>= 4; + duff &= 0xF; + duff2 = (duff * envelope) >> 19; + } + Wave[V >> 4] += duff2; + vco += 0x8000; + } + vcount[P] = vco; + } + } +} + +static void Mapper19_StateRestore(int version) { + SyncPRG(); + FixNTAR(); + FixCRR(); + SyncMirror(); + int x; + for (x = 0x40; x < 0x80; x++) + FixCache(x, IRAM[x]); +} + +static void M19SC(void) { + if (FSettings.SndRate) + Mapper19_ESI(); +} + +void Mapper19_ESI(void) { + GameExpSound.RChange = M19SC; + memset(vcount, 0, sizeof(vcount)); + memset(PlayIndex, 0, sizeof(PlayIndex)); + CVBC = 0; +} + +void NSFN106_Init(void) { + SetWriteHandler(0xf800, 0xffff, Mapper19_write); + SetWriteHandler(0x4800, 0x4fff, Mapper19_write); + SetReadHandler(0x4800, 0x4fff, Namco_Read4800); + Mapper19_ESI(); +} + +static int battery = 0; + +static void N106_Power(void) { + int x; + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xffff, Mapper19_write); + SetWriteHandler(0x4020, 0x5fff, Mapper19_write); + if (!is210) { + SetWriteHandler(0xc000, 0xdfff, Mapper19C0D8_write); + SetReadHandler(0x4800, 0x4fff, Namco_Read4800); + SetReadHandler(0x5000, 0x57ff, Namco_Read5000); + SetReadHandler(0x5800, 0x5fff, Namco_Read5800); + NTAPage[0] = NTAPage[1] = NTAPage[2] = NTAPage[3] = 0xFF; + FixNTAR(); + } + + SetReadHandler(0x6000, 0x7FFF, AWRAM); + SetWriteHandler(0x6000, 0x7FFF, BWRAM); + FCEU_CheatAddRAM(8, 0x6000, WRAM); + + gorfus = 0xFF; + SyncPRG(); + FixCRR(); + + if (!battery) { + FCEU_MemoryRand(WRAM, sizeof(WRAM), true); + FCEU_MemoryRand(IRAM, sizeof(IRAM), true); + } + for (x = 0x40; x < 0x80; x++) + FixCache(x, IRAM[x]); +} + +void Mapper19_Init(CartInfo *info) { + is210 = 0; + battery = info->battery; + info->Power = N106_Power; + + MapIRQHook = NamcoIRQHook; + GameStateRestore = Mapper19_StateRestore; + GameExpSound.RChange = M19SC; + + if (FSettings.SndRate) + Mapper19_ESI(); + + FCEU_MemoryRand(WRAM, sizeof(WRAM), true); + FCEU_MemoryRand(IRAM, sizeof(IRAM), true); + AddExState(WRAM, 8192, 0, "WRAM"); + AddExState(IRAM, 128, 0, "IRAM"); + AddExState(N106_StateRegs, ~0, 0, 0); + + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = 8192; + info->SaveGame[1] = IRAM; + info->SaveGameLen[1] = 128; + } +} + +static void Mapper210_StateRestore(int version) { + SyncPRG(); + FixCRR(); +} + +void Mapper210_Init(CartInfo *info) { + is210 = 1; + GameStateRestore = Mapper210_StateRestore; + info->Power = N106_Power; + FCEU_MemoryRand(WRAM, sizeof(WRAM), true); + AddExState(WRAM, 8192, 0, "WRAM"); + AddExState(N106_StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/n625092.cpp b/apps/fceux/src/boards/n625092.cpp new file mode 100644 index 00000000..058cd3d8 --- /dev/null +++ b/apps/fceux/src/boards/n625092.cpp @@ -0,0 +1,95 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + * 700in1 and 400in1 carts + * + */ + + +#include "mapinc.h" + +static uint16 cmd, bank; + +static SFORMAT StateRegs[] = +{ + { &cmd, 2, "CMD" }, + { &bank, 2, "BANK" }, + { 0 } +}; + +static void Sync(void) { + setmirror((cmd & 1) ^ 1); + setchr8(0); + if (cmd & 2) { + if (cmd & 0x100) { + setprg16(0x8000, ((cmd & 0xfc) >> 2) | bank); + setprg16(0xC000, ((cmd & 0xfc) >> 2) | 7); + } else { + setprg16(0x8000, ((cmd & 0xfc) >> 2) | (bank & 6)); + setprg16(0xC000, ((cmd & 0xfc) >> 2) | ((bank & 6) | 1)); + } + } else { + setprg16(0x8000, ((cmd & 0xfc) >> 2) | bank); + setprg16(0xC000, ((cmd & 0xfc) >> 2) | bank); + } +} + +static uint16 ass = 0; + +static DECLFW(UNLN625092WriteCommand) { + cmd = A; + if (A == 0x80F8) { + setprg16(0x8000, ass); + setprg16(0xC000, ass); + } else { + Sync(); + } +} + +static DECLFW(UNLN625092WriteBank) { + bank = A & 7; + Sync(); +} + +static void UNLN625092Power(void) { + cmd = 0; + bank = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xBFFF, UNLN625092WriteCommand); + SetWriteHandler(0xC000, 0xFFFF, UNLN625092WriteBank); +} + +static void UNLN625092Reset(void) { + cmd = 0; + bank = 0; + ass++; + FCEU_printf("%04x\n", ass); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLN625092_Init(CartInfo *info) { + info->Reset = UNLN625092Reset; + info->Power = UNLN625092Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/novel.cpp b/apps/fceux/src/boards/novel.cpp new file mode 100644 index 00000000..c6dca068 --- /dev/null +++ b/apps/fceux/src/boards/novel.cpp @@ -0,0 +1,50 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 latch; + +static void DoNovel(void) { + setprg32(0x8000, latch & 3); + setchr8(latch & 7); +} + +static DECLFW(NovelWrite) { + latch = A & 0xFF; + DoNovel(); +} + +static void NovelReset(void) { + SetWriteHandler(0x8000, 0xFFFF, NovelWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); + setprg32(0x8000, 0); + setchr8(0); +} + +static void NovelRestore(int version) { + DoNovel(); +} + +void Novel_Init(CartInfo *info) { + AddExState(&latch, 1, 0, "L1"); + info->Power = NovelReset; + GameStateRestore = NovelRestore; +} diff --git a/apps/fceux/src/boards/onebus.cpp b/apps/fceux/src/boards/onebus.cpp new file mode 100644 index 00000000..7cd23d75 --- /dev/null +++ b/apps/fceux/src/boards/onebus.cpp @@ -0,0 +1,297 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007-2010 CaH4e3 + * + * 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 + * + * VR02/VT03 Console and OneBus System + * + * Street Dance (Dance pad) (Unl) + * 101-in-1 Arcade Action II + * DreamGEAR 75-in-1, etc. + * + */ + +#include "mapinc.h" + +// General Purpose Registers +static uint8 cpu410x[16], ppu201x[16], apu40xx[64]; + +// IRQ Registers +static uint8 IRQCount, IRQa, IRQReload; +#define IRQLatch cpu410x[0x1] // accc cccc, a = 0, AD12 switching, a = 1, HSYNC switching + +// MMC3 Registers +static uint8 inv_hack = 0; // some OneBus Systems have swapped PRG reg commans in MMC3 inplementation, + // trying to autodetect unusual behavior, due not to add a new mapper. +#define mmc3cmd cpu410x[0x5] // pcv- ----, p - program swap, c - video swap, v - internal VRAM enable +#define mirror cpu410x[0x6] // ---- ---m, m = 0 - H, m = 1 - V + +// APU Registers +static uint8 pcm_enable = 0, pcm_irq = 0; +static int16 pcm_addr, pcm_size, pcm_latch, pcm_clock = 0xE1; + +static writefunc defapuwrite[64]; +static readfunc defapuread[64]; + +static SFORMAT StateRegs[] = +{ + { cpu410x, 16, "REGC" }, + { ppu201x, 16, "REGS" }, + { apu40xx, 64, "REGA" }, + { &IRQReload, 1, "IRQR" }, + { &IRQCount, 1, "IRQC" }, + { &IRQa, 1, "IRQA" }, + { &pcm_enable, 1, "PCME" }, + { &pcm_irq, 1, "PCMI" }, + { &pcm_addr, 2, "PCMA" }, + { &pcm_size, 2, "PCMS" }, + { &pcm_latch, 2, "PCML" }, + { &pcm_clock, 2, "PCMC" }, + { 0 } +}; + +static void PSync(void) { + uint8 bankmode = cpu410x[0xb] & 7; + uint8 mask = (bankmode == 0x7) ? (0xff) : (0x3f >> bankmode); + uint32 block = ((cpu410x[0x0] & 0xf0) << 4) + (cpu410x[0xa] & (~mask)); + uint32 pswap = (mmc3cmd & 0x40) << 8; + +// uint8 bank0 = (cpu410x[0xb] & 0x40)?(~1):(cpu410x[0x7]); +// uint8 bank1 = cpu410x[0x8]; +// uint8 bank2 = (cpu410x[0xb] & 0x40)?(cpu410x[0x9]):(~1); +// uint8 bank3 = ~0; + uint8 bank0 = cpu410x[0x7 ^ inv_hack]; + uint8 bank1 = cpu410x[0x8 ^ inv_hack]; + uint8 bank2 = (cpu410x[0xb] & 0x40) ? (cpu410x[0x9]) : (~1); + uint8 bank3 = ~0; + +// FCEU_printf(" PRG: %04x [%02x]",0x8000^pswap,block | (bank0 & mask)); + setprg8(0x8000 ^ pswap, block | (bank0 & mask)); +// FCEU_printf(" %04x [%02x]",0xa000^pswap,block | (bank1 & mask)); + setprg8(0xa000, block | (bank1 & mask)); +// FCEU_printf(" %04x [%02x]",0xc000^pswap,block | (bank2 & mask)); + setprg8(0xc000 ^ pswap, block | (bank2 & mask)); +// FCEU_printf(" %04x [%02x]\n",0xe000^pswap,block | (bank3 & mask)); + setprg8(0xe000, block | (bank3 & mask)); +} + +static void CSync(void) { + static const uint8 midx[8] = { 0, 1, 2, 0, 3, 4, 5, 0 }; + uint8 mask = 0xff >> midx[ppu201x[0xa] & 7]; + uint32 block = ((cpu410x[0x0] & 0x0f) << 11) + ((ppu201x[0x8] & 0x70) << 4) + (ppu201x[0xa] & (~mask)); + uint32 cswap = (mmc3cmd & 0x80) << 5; + + uint8 bank0 = ppu201x[0x6] & (~1); + uint8 bank1 = ppu201x[0x6] | 1; + uint8 bank2 = ppu201x[0x7] & (~1); + uint8 bank3 = ppu201x[0x7] | 1; + uint8 bank4 = ppu201x[0x2]; + uint8 bank5 = ppu201x[0x3]; + uint8 bank6 = ppu201x[0x4]; + uint8 bank7 = ppu201x[0x5]; + + setchr1(0x0000 ^ cswap, block | (bank0 & mask)); + setchr1(0x0400 ^ cswap, block | (bank1 & mask)); + setchr1(0x0800 ^ cswap, block | (bank2 & mask)); + setchr1(0x0c00 ^ cswap, block | (bank3 & mask)); + setchr1(0x1000 ^ cswap, block | (bank4 & mask)); + setchr1(0x1400 ^ cswap, block | (bank5 & mask)); + setchr1(0x1800 ^ cswap, block | (bank6 & mask)); + setchr1(0x1c00 ^ cswap, block | (bank7 & mask)); + + setmirror((mirror ^ 1) & 1); +} + +static void Sync(void) { + PSync(); + CSync(); +} + +static DECLFW(UNLOneBusWriteCPU410X) { +// FCEU_printf("CPU %04x:%04x\n",A,V); + switch (A & 0xf) { + case 0x1: IRQLatch = V & 0xfe; break; // íå ïî äàòàøèòó + case 0x2: IRQReload = 1; break; + case 0x3: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break; + case 0x4: IRQa = 1; break; + default: + cpu410x[A & 0xf] = V; + Sync(); + } +} + +static DECLFW(UNLOneBusWritePPU201X) { +// FCEU_printf("PPU %04x:%04x\n",A,V); + ppu201x[A & 0x0f] = V; + Sync(); +} + +static DECLFW(UNLOneBusWriteMMC3) { +// FCEU_printf("MMC %04x:%04x\n",A,V); + switch (A & 0xe001) { + case 0x8000: mmc3cmd = (mmc3cmd & 0x38) | (V & 0xc7); Sync(); break; + case 0x8001: + { + switch (mmc3cmd & 7) { + case 0: ppu201x[0x6] = V; CSync(); break; + case 1: ppu201x[0x7] = V; CSync(); break; + case 2: ppu201x[0x2] = V; CSync(); break; + case 3: ppu201x[0x3] = V; CSync(); break; + case 4: ppu201x[0x4] = V; CSync(); break; + case 5: ppu201x[0x5] = V; CSync(); break; + case 6: cpu410x[0x7] = V; PSync(); break; + case 7: cpu410x[0x8] = V; PSync(); break; + } + break; + } + case 0xa000: mirror = V; CSync(); break; + case 0xc000: IRQLatch = V & 0xfe; break; + case 0xc001: IRQReload = 1; break; + case 0xe000: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break; + case 0xe001: IRQa = 1; break; + } +} + +static void UNLOneBusIRQHook(void) { + uint32 count = IRQCount; + if (!count || IRQReload) { + IRQCount = IRQLatch; + IRQReload = 0; + } else + IRQCount--; + if (count && !IRQCount) { + if (IRQa) + X6502_IRQBegin(FCEU_IQEXT); + } +} + +static DECLFW(UNLOneBusWriteAPU40XX) { +// if(((A & 0x3f)!=0x16) && ((apu40xx[0x30] & 0x10) || ((A & 0x3f)>0x17)))FCEU_printf("APU %04x:%04x\n",A,V); + apu40xx[A & 0x3f] = V; + switch (A & 0x3f) { + case 0x12: + if (apu40xx[0x30] & 0x10) { + pcm_addr = V << 6; + } + break; + case 0x13: + if (apu40xx[0x30] & 0x10) { + pcm_size = (V << 4) + 1; + } + break; + case 0x15: + if (apu40xx[0x30] & 0x10) { + pcm_enable = V & 0x10; + if (pcm_irq) { + X6502_IRQEnd(FCEU_IQEXT); + pcm_irq = 0; + } + if (pcm_enable) + pcm_latch = pcm_clock; + V &= 0xef; + } + break; + } + defapuwrite[A & 0x3f](A, V); +} + +static DECLFR(UNLOneBusReadAPU40XX) { + uint8 result = defapuread[A & 0x3f](A); +// FCEU_printf("read %04x, %02x\n",A,result); + switch (A & 0x3f) { + case 0x15: + if (apu40xx[0x30] & 0x10) { + result = (result & 0x7f) | pcm_irq; + } + break; + } + return result; +} + +static void UNLOneBusCpuHook(int a) { + if (pcm_enable) { + pcm_latch -= a; + if (pcm_latch <= 0) { + pcm_latch += pcm_clock; + pcm_size--; + if (pcm_size < 0) { + pcm_irq = 0x80; + pcm_enable = 0; + X6502_IRQBegin(FCEU_IQEXT); + } else { + uint16 addr = pcm_addr | ((apu40xx[0x30]^3) << 14); + uint8 raw_pcm = ARead[addr](addr) >> 1; + defapuwrite[0x11](0x4011, raw_pcm); + pcm_addr++; + pcm_addr &= 0x7FFF; + } + } + } +} + +static void UNLOneBusPower(void) { + uint32 i; + IRQReload = IRQCount = IRQa = 0; + + memset(cpu410x, 0x00, sizeof(cpu410x)); + memset(ppu201x, 0x00, sizeof(ppu201x)); + memset(apu40xx, 0x00, sizeof(apu40xx)); + + SetupCartCHRMapping(0, PRGptr[0], PRGsize[0], 0); + + for (i = 0; i < 64; i++) { + defapuread[i] = GetReadHandler(0x4000 | i); + defapuwrite[i] = GetWriteHandler(0x4000 | i); + } + SetReadHandler(0x4000, 0x403f, UNLOneBusReadAPU40XX); + SetWriteHandler(0x4000, 0x403f, UNLOneBusWriteAPU40XX); + + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x2010, 0x201f, UNLOneBusWritePPU201X); + SetWriteHandler(0x4100, 0x410f, UNLOneBusWriteCPU410X); + SetWriteHandler(0x8000, 0xffff, UNLOneBusWriteMMC3); + + Sync(); +} + +static void UNLOneBusReset(void) { + IRQReload = IRQCount = IRQa = 0; + + memset(cpu410x, 0x00, sizeof(cpu410x)); + memset(ppu201x, 0x00, sizeof(ppu201x)); + memset(apu40xx, 0x00, sizeof(apu40xx)); + + Sync(); +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLOneBus_Init(CartInfo *info) { + info->Power = UNLOneBusPower; + info->Reset = UNLOneBusReset; + + if (((*(uint32*)&(info->MD5)) == 0x305fcdc3) || // PowerJoy Supermax Carts + ((*(uint32*)&(info->MD5)) == 0x6abfce8e)) + inv_hack = 0xf; + + GameHBIRQHook = UNLOneBusIRQHook; + MapIRQHook = UNLOneBusCpuHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/pec-586.cpp b/apps/fceux/src/boards/pec-586.cpp new file mode 100644 index 00000000..b7559c5f --- /dev/null +++ b/apps/fceux/src/boards/pec-586.cpp @@ -0,0 +1,131 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + * + * PEC-586 FC based computer series by DUNDA + * + */ + +#include "mapinc.h" + +static uint8 reg[8]; +static uint32 lastnt = 0; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { reg, 2, "REG" }, + { &lastnt, 4, "LNT" }, + { 0 } +}; + +static uint8 bs_tbl[128] = { + 0x03, 0x13, 0x23, 0x33, 0x03, 0x13, 0x23, 0x33, 0x03, 0x13, 0x23, 0x33, 0x03, 0x13, 0x23, 0x33, // 00 + 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, // 10 + 0x03, 0x13, 0x23, 0x33, 0x03, 0x13, 0x23, 0x33, 0x03, 0x13, 0x23, 0x33, 0x03, 0x13, 0x23, 0x33, // 20 + 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, // 30 + 0x02, 0x12, 0x22, 0x32, 0x02, 0x12, 0x22, 0x32, 0x02, 0x12, 0x22, 0x32, 0x02, 0x12, 0x22, 0x32, // 40 + 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, 0x45, 0x67, // 50 + 0x02, 0x12, 0x22, 0x32, 0x02, 0x12, 0x22, 0x32, 0x02, 0x12, 0x22, 0x32, 0x00, 0x10, 0x20, 0x30, // 60 + 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, 0x47, 0x67, // 70 +}; + +static uint8 br_tbl[16] = { + 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +}; + +static void Sync(void) { + setchr8(0); + setprg8r(0x10, 0x6000, 0); + if(PRGsize[0] == 512 * 1024) { + if(reg[0] & 0x010) { + setprg32(0x8000, reg[0] & 7); + } else { + if(reg[0] & 0x40) + setprg8(0x8000, (reg[0] & 0x0F) | 0x20 | ((reg[0] & 0x20) >> 1)); + } + if((reg[0] & 0x18) == 0x18) + setmirror(MI_H); + else + setmirror(MI_V); + } else { + setprg16(0x8000, bs_tbl[reg[0] & 0x7f] >> 4); + setprg16(0xc000, bs_tbl[reg[0] & 0x7f] & 0xf); + setmirror(MI_V); + } +} + +static DECLFW(UNLPEC586Write) { + reg[(A & 0x700) >> 8] = V; + PEC586Hack = (reg[0] & 0x80) >> 7; +// FCEU_printf("bs %04x %02x\n", A, V); + Sync(); +} + +static DECLFR(UNLPEC586Read) { +// FCEU_printf("read %04x\n", A); + return (X.DB & 0xD8) | br_tbl[reg[4] >> 4]; +} + +static DECLFR(UNLPEC586ReadHi) { + if((reg[0] & 0x10) || ((reg[0] & 0x40) && (A < 0xA000))) + return CartBR(A); + else + return PRGptr[0][((0x0107 | ((A >> 7) & 0x0F8)) << 10) | (A & 0x3FF)]; +} + +static void UNLPEC586Power(void) { + if(PRGsize[0] == 512 * 1024) + reg[0] = 0x00; + else + reg[0] = 0x0E; + Sync(); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + if(PRGsize[0] == 512 * 1024) + SetReadHandler(0x8000, 0xFFFF, UNLPEC586ReadHi); + else + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x5000, 0x5fff, UNLPEC586Write); + SetReadHandler(0x5000, 0x5fff, UNLPEC586Read); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void UNLPEC586Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLPEC586Init(CartInfo *info) { + info->Power = UNLPEC586Power; + info->Close = UNLPEC586Close; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/rt-01.cpp b/apps/fceux/src/boards/rt-01.cpp new file mode 100644 index 00000000..b6363794 --- /dev/null +++ b/apps/fceux/src/boards/rt-01.cpp @@ -0,0 +1,55 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2016 CaH4e3 + * + * 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 + * + * Test Ver. 1.01 Dlya Proverki TV Pristavok (RT-01, by SS aka Snake) + * A simple board with 16K PRG ROM + 2K CHR ROM with no mapper, hardwired mirroring + * PRG EPROM has copy protected areas with "weak bits", which is tested at some + * points of the program. Trying to simalate "weak bits" behaviour + * + */ + +#include "mapinc.h" + +extern u64 xoroshiro128plus_next(); // deterministic random + +static DECLFR(UNLRT01Read) { +// u16 i, prot_areas[2][2] = { +// { 0x8E80, 0x8EFF }, +// { 0xFE80, 0xFEFF }, +// }; + if(((A >= 0xCE80) && (A < 0xCF00)) || + ((A >= 0xFE80) && (A < 0xFF00))) { + return 0xF2 | (xoroshiro128plus_next() & 0x0D); + } else + return CartBR(A); +} + +static void UNLRT01Power(void) { + setprg16(0x8000, 0); + setprg16(0xC000, 0); + setchr2(0x0000,0); + setchr2(0x0800,0); + setchr2(0x1000,0); + setchr2(0x1800,0); + SetReadHandler(0x8000, 0xFFFF, UNLRT01Read); +} + +void UNLRT01_Init(CartInfo *info) { + info->Power = UNLRT01Power; +} diff --git a/apps/fceux/src/boards/sa-9602b.cpp b/apps/fceux/src/boards/sa-9602b.cpp new file mode 100644 index 00000000..a6a19bbe --- /dev/null +++ b/apps/fceux/src/boards/sa-9602b.cpp @@ -0,0 +1,61 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static void SA9602BPW(uint32 A, uint8 V) { + setprg8r(EXPREGS[1], A, V & 0x3F); + if (MMC3_cmd & 0x40) + setprg8r(0, 0x8000, ~(1)); + else + setprg8r(0, 0xc000, ~(1)); + setprg8r(0, 0xe000, ~(0)); +} + +static DECLFW(SA9602BWrite) { + switch (A & 0xe001) { + case 0x8000: EXPREGS[0] = V; break; + case 0x8001: + if ((EXPREGS[0] & 7) < 6) { + EXPREGS[1] = V >> 6; + FixMMC3PRG(MMC3_cmd); + } + break; + } + MMC3_CMDWrite(A, V); +} + +static void SA9602BPower(void) { + EXPREGS[0] = EXPREGS[1] = 0; + GenMMC3Power(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xBFFF, SA9602BWrite); +} + +void SA9602B_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 0, 0, 0); + pwrap = SA9602BPW; + mmc3opts |= 2; + info->SaveGame[0] = UNIFchrrama; + info->SaveGameLen[0] = 32 * 1024; + info->Power = SA9602BPower; + AddExState(EXPREGS, 2, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/sachen.cpp b/apps/fceux/src/boards/sachen.cpp new file mode 100644 index 00000000..0f73e407 --- /dev/null +++ b/apps/fceux/src/boards/sachen.cpp @@ -0,0 +1,409 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 cmd, dip; +static uint8 latch[8]; + +static void S74LS374MSync(uint8 mirr) { + switch (mirr & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirrorw(0, 1, 1, 1); break; + case 3: setmirror(MI_0); break; + } +} + +static void S74LS374NSynco(void) { + setprg32(0x8000, latch[0]); + setchr8(latch[1] | latch[3] | latch[4]); + S74LS374MSync(latch[2]); +} + +static DECLFW(S74LS374NWrite) { + A &= 0x4101; + if (A == 0x4100) + cmd = V & 7; + else{ + switch (cmd) { + case 2: latch[0] = V & 1; latch[3] = (V & 1) << 3; break; + case 4: latch[4] = (V & 1) << 2; break; + case 5: latch[0] = V & 7; break; + case 6: latch[1] = V & 3; break; + case 7: latch[2] = V >> 1; break; + } + S74LS374NSynco(); + } +} + +static DECLFR(S74LS374NRead) { + uint8 ret; + if ((A & 0x4100) == 0x4100) +// ret=(X.DB&0xC0)|((~cmd)&0x3F); + ret = ((~cmd) & 0x3F) ^ dip; + else + ret = X.DB; + return ret; +} + +static void S74LS374NPower(void) { + dip = 0; + latch[0] = latch[1] = latch[2] = latch[3] = latch[4] = 0; + S74LS374NSynco(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0x7FFF, S74LS374NWrite); + SetReadHandler(0x4100, 0x5fff, S74LS374NRead); +} + +static void S74LS374NReset(void) { + dip ^= 1; + latch[0] = latch[1] = latch[2] = latch[3] = latch[4] = 0; + S74LS374NSynco(); +} + +static void S74LS374NRestore(int version) { + S74LS374NSynco(); +} + +void S74LS374N_Init(CartInfo *info) { + info->Power = S74LS374NPower; + info->Reset = S74LS374NReset; + GameStateRestore = S74LS374NRestore; + AddExState(latch, 5, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + AddExState(&dip, 1, 0, "DIP"); +} + +static void S74LS374NASynco(void) { + setprg32(0x8000, latch[0]); + setchr8(latch[1]); + S74LS374MSync(latch[2]); +} + +static DECLFW(S74LS374NAWrite) { + A &= 0x4101; + if (A == 0x4100) + cmd = V & 7; + else{ + switch (cmd) { + case 0: latch[0] = 0; latch[1] = 3; break; + case 2: latch[3] = (V & 1) << 3; break; + case 4: latch[1] = (latch[1] & 6) | (V & 3); break; + case 5: latch[0] = V & 1; break; + case 6: latch[1] = (latch[1] & 1) | latch[3] | ((V & 3) << 1); break; + case 7: latch[2] = V & 1; break; + } + S74LS374NASynco(); + } +} + +static void S74LS374NAPower(void) { + latch[0] = latch[2] = latch[3] = latch[4] = 0; + latch[1] = 3; + S74LS374NASynco(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0x7FFF, S74LS374NAWrite); +} + +void S74LS374NA_Init(CartInfo *info) { + info->Power = S74LS374NAPower; + GameStateRestore = S74LS374NRestore; + AddExState(latch, 5, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); +} + +static int type; +static void S8259Synco(void) { + int x; + setprg32(0x8000, latch[5] & 7); + + if (!UNIFchrrama) { // No CHR RAM? Then BS'ing is ok. + for (x = 0; x < 4; x++) { + int bank; + if (latch[7] & 1) + bank = (latch[0] & 0x7) | ((latch[4] & 7) << 3); + else + bank = (latch[x] & 0x7) | ((latch[4] & 7) << 3); + switch (type) { + case 00: bank = (bank << 1) | (x & 1); setchr2(0x800 * x, bank); break; + case 01: setchr2(0x800 * x, bank); break; + case 02: bank = (bank << 2) | (x & 3); setchr2(0x800 * x, bank); break; + case 03: + bank = latch[x] & 7; + switch (x & 3) { + case 01: bank |= (latch[4] & 1) << 4; break; + case 02: bank |= (latch[4] & 2) << 3; break; + case 03: bank |= ((latch[4] & 4) << 2) | ((latch[6] & 1) << 3); break; + } + setchr1(0x400 * x, bank); + setchr4(0x1000, ~0); + break; + } + } + } + if (!(latch[7] & 1)) + S74LS374MSync(latch[7] >> 1); + else + setmirror(MI_V); +} + +static DECLFW(S8259Write) { + A &= 0x4101; + if (A == 0x4100) + cmd = V; + else{ + latch[cmd & 7] = V; + S8259Synco(); + } +} + +static void S8259Reset(void) { + int x; + cmd = 0; + + for (x = 0; x < 8; x++) latch[x] = 0; + setchr8(0); + + S8259Synco(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0x7FFF, S8259Write); +} + +static void S8259Restore(int version) { + S8259Synco(); +} + +void S8259A_Init(CartInfo *info) { // Kevin's Horton 141 mapper + info->Power = S8259Reset; + GameStateRestore = S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type = 0; +} + +void S8259B_Init(CartInfo *info) { // Kevin's Horton 138 mapper + info->Power = S8259Reset; + GameStateRestore = S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type = 1; +} + +void S8259C_Init(CartInfo *info) { // Kevin's Horton 139 mapper + info->Power = S8259Reset; + GameStateRestore = S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type = 2; +} + +void S8259D_Init(CartInfo *info) { // Kevin's Horton 137 mapper + info->Power = S8259Reset; + GameStateRestore = S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type = 3; +} + +static void (*WSync)(void); + +static DECLFW(SAWrite) { + if (A & 0x100) { + latch[0] = V; + WSync(); + } +} + +static void SAPower(void) { + latch[0] = 0; + WSync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0x5FFF, SAWrite); +} + +static void SARestore(int version) { + WSync(); +} + +static DECLFW(SADWrite) { + latch[0] = V; + WSync(); +} + +static void SADPower(void) { + latch[0] = 0; + WSync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, SADWrite); +} + +static void SA0161MSynco() { + setprg32(0x8000, (latch[0] >> 3) & 1); + setchr8(latch[0] & 7); +} + +static void SA72007Synco() { + setprg32(0x8000, 0); + setchr8(latch[0] >> 7); +} + +static void SA009Synco() { + setprg32(0x8000, 0); + setchr8(latch[0] & 1); +} + +static void SA72008Synco() { + setprg32(0x8000, (latch[0] >> 2) & 1); + setchr8(latch[0] & 3); +} + +void SA0161M_Init(CartInfo *info) { + WSync = SA0161MSynco; + GameStateRestore = SARestore; + info->Power = SAPower; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA72007_Init(CartInfo *info) { + WSync = SA72007Synco; + GameStateRestore = SARestore; + info->Power = SAPower; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA72008_Init(CartInfo *info) { + WSync = SA72008Synco; + GameStateRestore = SARestore; + info->Power = SAPower; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA009_Init(CartInfo *info) { + WSync = SA009Synco; + GameStateRestore = SARestore; + info->Power = SAPower; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA0036_Init(CartInfo *info) { + WSync = SA72007Synco; + GameStateRestore = SARestore; + info->Power = SADPower; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA0037_Init(CartInfo *info) { + WSync = SA0161MSynco; + GameStateRestore = SARestore; + info->Power = SADPower; + AddExState(&latch[0], 1, 0, "LATC"); +} + +// ----------------------------------------------- + +static void TCU01Synco() { + setprg32(0x8000, ((latch[0] & 0x80) >> 6) | ((latch[0] >> 2) & 1)); + setchr8((latch[0] >> 3) & 0xF); +} + +static DECLFW(TCU01Write) { + if ((A & 0x103) == 0x102) { + latch[0] = V; + TCU01Synco(); + } +} + +static void TCU01Power(void) { + latch[0] = 0; + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4100, 0xFFFF, TCU01Write); + TCU01Synco(); +} + +static void TCU01Restore(int version) { + TCU01Synco(); +} + +void TCU01_Init(CartInfo *info) { + GameStateRestore = TCU01Restore; + info->Power = TCU01Power; + AddExState(&latch[0], 1, 0, "LATC"); +} + +//----------------------------------------------- + +static void TCU02Synco() { + setprg32(0x8000, 0); + setchr8(latch[0] & 3); +} + +static DECLFW(TCU02Write) { + if ((A & 0x103) == 0x102) { + latch[0] = V + 3; + TCU02Synco(); + } +} + +static DECLFR(TCU02Read) { + return (latch[0] & 0x3F) | (X.DB & 0xC0); +} + +static void TCU02Power(void) { + latch[0] = 0; + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0x4100, 0x4100, TCU02Read); + SetWriteHandler(0x4100, 0xFFFF, TCU02Write); + TCU02Synco(); +} + +static void TCU02Restore(int version) { + TCU02Synco(); +} + +void TCU02_Init(CartInfo *info) { + GameStateRestore = TCU02Restore; + info->Power = TCU02Power; + AddExState(&latch[0], 1, 0, "LATC"); +} + +// --------------------------------------------- + +static DECLFR(TCA01Read) { + uint8 ret; + if ((A & 0x4100) == 0x4100) + ret = (X.DB & 0xC0) | ((~A) & 0x3F); + else + ret = X.DB; + return ret; +} + +static void TCA01Power(void) { + setprg16(0x8000, 0); + setprg16(0xC000, 1); + setchr8(0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0x4100, 0x5FFF, TCA01Read); +} + +void TCA01_Init(CartInfo *info) { + info->Power = TCA01Power; +} + diff --git a/apps/fceux/src/boards/sb-2000.cpp b/apps/fceux/src/boards/sb-2000.cpp new file mode 100644 index 00000000..c2ea454a --- /dev/null +++ b/apps/fceux/src/boards/sb-2000.cpp @@ -0,0 +1,195 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2014 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 preg[8]; +static uint8 IRQa; +static int16 IRQCount, IRQLatch; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +/* +static uint8 *CHRRAM = NULL; +static uint32 CHRRAMSIZE; +*/ + +static SFORMAT StateRegs[] = +{ + { preg, 8, "PREG" }, + { &IRQa, 1, "IRQA" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 2, "IRQL" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg8r(0x10, 0x6000, 0); + if(preg[0] & 0x80) + setprg4r(0x10,0x8000,preg[0] & 0x7f); + else + setprg4(0x8000,preg[0] & 0x7f); + if(preg[1] & 0x80) + setprg4r(0x10,0x9000,preg[1] & 0x7f); + else + setprg4(0x9000,preg[1] & 0x7f); + if(preg[2] & 0x80) + setprg4r(0x10,0xa000,preg[2] & 0x7f); + else + setprg4(0xa000,preg[2] & 0x7f); + if(preg[3] & 0x80) + setprg4r(0x10,0xb000,preg[3] & 0x7f); + else + setprg4(0xb000,preg[3] & 0x7f); +/* + if(preg[4] & 0x80) + setprg4r(0x10,0xc000,preg[4] & 0x7f); + else + setprg4(0xc000,preg[4] & 0x7f); + if(preg[5] & 0x80) + setprg4r(0x10,0xd000,preg[5] & 0x7f); + else + setprg4(0xd000,preg[5] & 0x7f); + if(preg[6] & 0x80) + setprg4r(0x10,0xe000,preg[6] & 0x7f); + else + setprg4(0xe000,preg[6] & 0x7f); + if(preg[7] & 0x80) + setprg4r(0x10,0xf000,preg[7] & 0x7f); + else + setprg4(0xf000,preg[7] & 0x7f); +*/ + setprg16(0xC000,1); +} + +static DECLFR(UNLSB2000Read) { + switch(A) { + case 0x4033: // IRQ flags + X6502_IRQEnd(FCEU_IQFCOUNT); + return 0xff; +// case 0x4204: // unk +// return 0xff; +// case 0x4205: // unk +// return 0xff; + default: + FCEU_printf("unk read: %04x\n",A); +// break; + return 0xff; // needed to prevent C4715 warning? + } +} + +static DECLFW(UNLSB2000Write) { + switch(A) { + case 0x4027: // PCM output + BWrite[0x4015](0x4015, 0x10); + BWrite[0x4011](0x4011, V >> 1); + break; + case 0x4032: // IRQ mask + IRQa &= ~V; +// X6502_IRQEnd(FCEU_IQEXT); + break; + case 0x4040: + case 0x4041: + case 0x4042: + case 0x4043: + case 0x4044: + case 0x4045: + case 0x4046: + case 0x4047: +// FCEU_printf("bank write: %04x:%02x\n",A,V); + preg[A&7] = V; + Sync(); + break; + default: +// FCEU_printf("unk write: %04x:%02x\n",A,V); + break; + } +} + +static void UNLSB2000Reset(void) { + preg[0] = 0; + preg[1] = 1; + preg[2] = 2; + preg[3] = 3; + preg[4] = 4; + preg[5] = 5; + preg[6] = 6; + preg[7] = 7; + IRQa = 0; +// BWrite[0x4017](0x4017,0xC0); +// BWrite[0x4015](0x4015,0x1F); +} + +static void UNLSB2000Power(void) { + UNLSB2000Reset(); + Sync(); + SetReadHandler(0x6000, 0x7fff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x8000, 0xbfff, CartBW); + SetWriteHandler(0x4020, 0x5fff, UNLSB2000Write); + SetReadHandler(0x4020, 0x5fff, UNLSB2000Read); +} + +static void UNLSB2000Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); +/* + if (CHRRAM) + FCEU_gfree(CHRRAM); +*/ + WRAM = /*CHRRAM = */NULL; +} +/* +static void UNLSB2000IRQHook() { + X6502_IRQBegin(FCEU_IQEXT); +} +*/ +static void StateRestore(int version) { + Sync(); +} + +void UNLSB2000_Init(CartInfo *info) { + info->Reset = UNLSB2000Reset; + info->Power = UNLSB2000Power; + info->Close = UNLSB2000Close; +// GameHBIRQHook = UNLSB2000IRQHook; + GameStateRestore = StateRestore; +/* + CHRRAMSIZE = 8192; + CHRRAM = (uint8*)FCEU_gmalloc(CHRRAMSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRRAMSIZE, 1); + AddExState(CHRRAM, CHRRAMSIZE, 0, "CRAM"); +*/ + +// SetupCartCHRMapping(0, PRGptr[0], PRGsize[0], 0); + + WRAMSIZE = 512 * 1024; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/sc-127.cpp b/apps/fceux/src/boards/sc-127.cpp new file mode 100644 index 00000000..2d2d968c --- /dev/null +++ b/apps/fceux/src/boards/sc-127.cpp @@ -0,0 +1,123 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + * + * Wario Land II (Kirby hack) + */ + +#include "mapinc.h" + +static uint8 reg[8], chr[8]; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; +static uint16 IRQCount, IRQa; + +static SFORMAT StateRegs[] = +{ + { reg, 8, "REGS" }, + { chr, 8, "CHRS" }, + { &IRQCount, 2, "IRQc" }, + { &IRQa, 2, "IRQa" }, + { 0 } +}; + +static void Sync(void) { + int i; + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, reg[0]); + setprg8(0xA000, reg[1]); + setprg8(0xC000, reg[2]); + setprg8(0xE000, ~0); + for (i = 0; i < 8; i++) + setchr1(i << 10, chr[i]); + setmirror(reg[3] ^ 1); +} + +static DECLFW(UNLSC127Write) { + switch (A) { + case 0x8000: reg[0] = V; break; + case 0x8001: reg[1] = V; break; + case 0x8002: reg[2] = V; break; + case 0x9000: chr[0] = V; break; + case 0x9001: chr[1] = V; break; + case 0x9002: chr[2] = V; break; + case 0x9003: chr[3] = V; break; + case 0x9004: chr[4] = V; break; + case 0x9005: chr[5] = V; break; + case 0x9006: chr[6] = V; break; + case 0x9007: chr[7] = V; break; + case 0xC002: IRQa = 0; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xC005: IRQCount = V; break; + case 0xC003: IRQa = 1; break; + case 0xD001: reg[3] = V; break; + } + Sync(); +} + +static DECLFR(UNLSC127ProtRead) { + return 0x20; +} + +static void UNLSC127Power(void) { + IRQCount = IRQa = 0; + Sync(); + SetReadHandler(0x5800, 0x5800, UNLSC127ProtRead); + SetReadHandler(0x6000, 0x7fff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLSC127Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void UNLSC127IRQ(void) { + if (IRQa) { + if(IRQCount > 0) + IRQCount--; + if (!IRQCount) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + } + } +} + +static void UNLSC127Reset(void) { + IRQCount = IRQa = 0; +} + +static void UNLSC127Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLSC127_Init(CartInfo *info) { + info->Reset = UNLSC127Reset; + info->Power = UNLSC127Power; + info->Close = UNLSC127Close; + GameHBIRQHook = UNLSC127IRQ; + GameStateRestore = StateRestore; + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/sheroes.cpp b/apps/fceux/src/boards/sheroes.cpp new file mode 100644 index 00000000..c536c1d5 --- /dev/null +++ b/apps/fceux/src/boards/sheroes.cpp @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 *CHRRAM; +static uint8 tekker; + +static void MSHCW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x40) + setchr8r(0x10, 0); + else{ + if (A < 0x800) + setchr1(A, V | ((EXPREGS[0] & 8) << 5)); + else if (A < 0x1000) + setchr1(A, V | ((EXPREGS[0] & 4) << 6)); + else if (A < 0x1800) + setchr1(A, V | ((EXPREGS[0] & 1) << 8)); + else + setchr1(A, V | ((EXPREGS[0] & 2) << 7)); + } +} + +static DECLFW(MSHWrite) { + EXPREGS[0] = V; + FixMMC3CHR(MMC3_cmd); +} + +static DECLFR(MSHRead) { + return(tekker); +} + +static void MSHReset(void) { + MMC3RegReset(); + tekker ^= 0xFF; +} + +static void MSHPower(void) { + tekker = 0x00; + GenMMC3Power(); + SetWriteHandler(0x4100, 0x4100, MSHWrite); + SetReadHandler(0x4100, 0x4100, MSHRead); +} + +static void MSHClose(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +void UNLSHeroes_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 512, 0, 0); + cwrap = MSHCW; + info->Power = MSHPower; + info->Reset = MSHReset; + info->Close = MSHClose; + CHRRAM = (uint8*)FCEU_gmalloc(8192); + SetupCartCHRMapping(0x10, CHRRAM, 8192, 1); + AddExState(EXPREGS, 4, 0, "EXPR"); + AddExState(&tekker, 1, 0, "DIPSW"); +} diff --git a/apps/fceux/src/boards/sl1632.cpp b/apps/fceux/src/boards/sl1632.cpp new file mode 100644 index 00000000..80643c5f --- /dev/null +++ b/apps/fceux/src/boards/sl1632.cpp @@ -0,0 +1,107 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 chrcmd[8], prg0, prg1, bbrk, mirr, swap; +static SFORMAT StateRegs[] = +{ + { chrcmd, 8, "CHRC" }, + { &prg0, 1, "PRG0" }, + { &prg1, 1, "PRG1" }, + { &bbrk, 1, "BRK" }, + { &mirr, 1, "MIRR" }, + { &swap, 1, "SWAP" }, + { 0 } +}; + +static void Sync(void) { + int i; + setprg8(0x8000, prg0); + setprg8(0xA000, prg1); + setprg8(0xC000, ~1); + setprg8(0xE000, ~0); + for (i = 0; i < 8; i++) + setchr1(i << 10, chrcmd[i]); + setmirror(mirr ^ 1); +} + +static void UNLSL1632CW(uint32 A, uint8 V) { + int cbase = (MMC3_cmd & 0x80) << 5; + int page0 = (bbrk & 0x08) << 5; + int page1 = (bbrk & 0x20) << 3; + int page2 = (bbrk & 0x80) << 1; + setchr1(cbase ^ 0x0000, page0 | (DRegBuf[0] & (~1))); + setchr1(cbase ^ 0x0400, page0 | DRegBuf[0] | 1); + setchr1(cbase ^ 0x0800, page0 | (DRegBuf[1] & (~1))); + setchr1(cbase ^ 0x0C00, page0 | DRegBuf[1] | 1); + setchr1(cbase ^ 0x1000, page1 | DRegBuf[2]); + setchr1(cbase ^ 0x1400, page1 | DRegBuf[3]); + setchr1(cbase ^ 0x1800, page2 | DRegBuf[4]); + setchr1(cbase ^ 0x1c00, page2 | DRegBuf[5]); +} + +static DECLFW(UNLSL1632CMDWrite) { + if (A == 0xA131) { + bbrk = V; + } + if (bbrk & 2) { + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + if (A < 0xC000) + MMC3_CMDWrite(A, V); + else + MMC3_IRQWrite(A, V); + } else { + if ((A >= 0xB000) && (A <= 0xE003)) { + int ind = ((((A & 2) | (A >> 10)) >> 1) + 2) & 7; + int sar = ((A & 1) << 2); + chrcmd[ind] = (chrcmd[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar); + } else + switch (A & 0xF003) { + case 0x8000: prg0 = V; break; + case 0xA000: prg1 = V; break; + case 0x9000: mirr = V & 1; break; + } + Sync(); + } +} + +static void StateRestore(int version) { + if (bbrk & 2) { + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } else + Sync(); +} + +static void UNLSL1632Power(void) { + GenMMC3Power(); + SetWriteHandler(0x4100, 0xFFFF, UNLSL1632CMDWrite); +} + +void UNLSL1632_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 512, 0, 0); + cwrap = UNLSL1632CW; + info->Power = UNLSL1632Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/subor.cpp b/apps/fceux/src/boards/subor.cpp new file mode 100644 index 00000000..00d9a997 --- /dev/null +++ b/apps/fceux/src/boards/subor.cpp @@ -0,0 +1,88 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 is167, regs[4]; + +static SFORMAT StateRegs[] = +{ + { regs, 4, "DREG" }, + { 0 } +}; + +static void Sync(void) { + int base, bank; + base = ((regs[0] ^ regs[1]) & 0x10) << 1; + bank = (regs[2] ^ regs[3]) & 0x1f; + + if (regs[1] & 0x08) { + bank &= 0xFE; + if (is167) { + setprg16(0x8000, base + bank + 1); + setprg16(0xC000, base + bank + 0); + } else { + setprg16(0x8000, base + bank + 0); + setprg16(0xC000, base + bank + 1); + } + } else { + if (regs[1] & 0x04) { + setprg16(0x8000, 0x1F); + setprg16(0xC000, base + bank); + } else { + setprg16(0x8000, base + bank); + if (is167) + setprg16(0xC000, 0x20); + else + setprg16(0xC000, 0x07); + } + } + setchr8(0); +} + +static DECLFW(M166Write) { + regs[(A >> 13) & 0x03] = V; + Sync(); +} + +static void M166Power(void) { + regs[0] = regs[1] = regs[2] = regs[3] = 0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M166Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper166_Init(CartInfo *info) { + is167 = 0; + info->Power = M166Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper167_Init(CartInfo *info) { + is167 = 1; + info->Power = M166Power; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/super24.cpp b/apps/fceux/src/boards/super24.cpp new file mode 100644 index 00000000..f62fbe85 --- /dev/null +++ b/apps/fceux/src/boards/super24.cpp @@ -0,0 +1,93 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 *CHRRAM = NULL; +static int masko8[8] = { 63, 31, 15, 1, 3, 0, 0, 0 }; + +static void Super24PW(uint32 A, uint8 V) { + uint32 NV = V & masko8[EXPREGS[0] & 7]; + NV |= (EXPREGS[1] << 1); + setprg8r((NV >> 6) & 0xF, A, NV); +} + +static void Super24CW(uint32 A, uint8 V) { + if (EXPREGS[0] & 0x20) + setchr1r(0x10, A, V); + else{ + uint32 NV = V | (EXPREGS[2] << 3); + setchr1r((NV >> 9) & 0xF, A, NV); + } +} + +static DECLFW(Super24Write) { + switch (A) { + case 0x5FF0: + EXPREGS[0] = V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + break; + case 0x5FF1: + EXPREGS[1] = V; + FixMMC3PRG(MMC3_cmd); + break; + case 0x5FF2: + EXPREGS[2] = V; + FixMMC3CHR(MMC3_cmd); + break; + } +} + +static void Super24Power(void) { + EXPREGS[0] = 0x24; + EXPREGS[1] = 159; + EXPREGS[2] = 0; + GenMMC3Power(); + SetWriteHandler(0x5000, 0x7FFF, Super24Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void Super24Reset(void) { + EXPREGS[0] = 0x24; + EXPREGS[1] = 159; + EXPREGS[2] = 0; + MMC3RegReset(); +} + +static void Super24Close(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; +} + +void Super24_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 256, 0, 0); + info->Power = Super24Power; + info->Reset = Super24Reset; + info->Close = Super24Close; + cwrap = Super24CW; + pwrap = Super24PW; + CHRRAM = (uint8*)FCEU_gmalloc(8192); + SetupCartCHRMapping(0x10, CHRRAM, 8192, 1); + AddExState(CHRRAM, 8192, 0, "CHRR"); + AddExState(EXPREGS, 3, 0, "BIG2"); +} diff --git a/apps/fceux/src/boards/supervision.cpp b/apps/fceux/src/boards/supervision.cpp new file mode 100644 index 00000000..46be8ed2 --- /dev/null +++ b/apps/fceux/src/boards/supervision.cpp @@ -0,0 +1,87 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 cmd0, cmd1; +static SFORMAT StateRegs[] = +{ + { &cmd0, 1, "L1" }, + { &cmd1, 1, "L2" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + if (PRGptr[1]) + setprg8r((cmd0 & 0xC) >> 2, 0x6000, ((cmd0 & 0x3) << 4) | 0xF); + else + setprg8(0x6000, (((cmd0 & 0xF) << 4) | 0xF) + 4); + if (cmd0 & 0x10) { + if (PRGptr[1]) { + setprg16r((cmd0 & 0xC) >> 2, 0x8000, ((cmd0 & 0x3) << 3) | (cmd1 & 7)); + setprg16r((cmd0 & 0xC) >> 2, 0xc000, ((cmd0 & 0x3) << 3) | 7); + } else { + setprg16(0x8000, (((cmd0 & 0xF) << 3) | (cmd1 & 7)) + 2); + setprg16(0xc000, (((cmd0 & 0xF) << 3) | 7) + 2); + } + } else + if (PRGptr[4]) + setprg32r(4, 0x8000, 0); + else + setprg32(0x8000, 0); + setmirror(((cmd0 & 0x20) >> 5) ^ 1); +} + +static DECLFW(SuperWriteLo) { + if (!(cmd0 & 0x10)) { + cmd0 = V; + Sync(); + } +} + +static DECLFW(SuperWriteHi) { + cmd1 = V; + Sync(); +} + +static void SuperPower(void) { + SetWriteHandler(0x6000, 0x7FFF, SuperWriteLo); + SetWriteHandler(0x8000, 0xFFFF, SuperWriteHi); + SetReadHandler(0x6000, 0xFFFF, CartBR); + cmd0 = cmd1 = 0; + Sync(); +} + +static void SuperReset(void) { + cmd0 = cmd1 = 0; + Sync(); +} + +static void SuperRestore(int version) { + Sync(); +} + +void Supervision16_Init(CartInfo *info) { + info->Power = SuperPower; + info->Reset = SuperReset; + GameStateRestore = SuperRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/t-227-1.cpp b/apps/fceux/src/boards/t-227-1.cpp new file mode 100644 index 00000000..ca99b6f9 --- /dev/null +++ b/apps/fceux/src/boards/t-227-1.cpp @@ -0,0 +1,100 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2008 CaH4e3 + * + * 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 + */ + +// T-227-1, 820632, MMC3 based, multimenu, 60000in1 (0010) dip switches + +#include "mapinc.h" +#include "mmc3.h" + +static uint8 reset_flag = 0x07; + +static void BMCT2271CW(uint32 A, uint8 V) { + uint32 va = V; + if (EXPREGS[0] & 0x20) { + va |= 0x200; + va |= (EXPREGS[0] & 0x10) << 4; + } else { + va &= 0x7F; + va |= (EXPREGS[0] & 0x18) << 4; + } + setchr1(A, va); +} + +static void BMCT2271PW(uint32 A, uint8 V) { + uint32 va = V & 0x3F; + if (EXPREGS[0] & 0x20) { + va &= 0x1F; + va |= 0x40; + va |= (EXPREGS[0] & 0x10) << 1; + } else { + va &= 0x0F; + va |= (EXPREGS[0] & 0x18) << 1; + } + switch (EXPREGS[0] & 3) { + case 0x00: setprg8(A, va); break; + case 0x02: + { + va = (va & 0xFD) | ((EXPREGS[0] & 4) >> 1); + if (A < 0xC000) { + setprg16(0x8000, va >> 1); + setprg16(0xC000, va >> 1); + } + break; + } + case 0x01: + case 0x03: if (A < 0xC000) setprg32(0x8000, va >> 2); break; + } +} + +static DECLFW(BMCT2271LoWrite) { + if (!(EXPREGS[0] & 0x80)) + EXPREGS[0] = A & 0xFF; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static DECLFR(BMCT2271HiRead) { + uint32 av = A; + if (EXPREGS[0] & 0x40) av = (av & 0xFFF0) | reset_flag; + return CartBR(av); +} + +static void BMCT2271Reset(void) { + EXPREGS[0] = 0x00; + reset_flag++; + reset_flag &= 0x0F; + MMC3RegReset(); +} + +static void BMCT2271Power(void) { + EXPREGS[0] = 0x00; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, BMCT2271LoWrite); + SetReadHandler(0x8000, 0xFFFF, BMCT2271HiRead); +} + +void BMCT2271_Init(CartInfo *info) { + GenMMC3_Init(info, 128, 128, 8, 0); + pwrap = BMCT2271PW; + cwrap = BMCT2271CW; + info->Power = BMCT2271Power; + info->Reset = BMCT2271Reset; + AddExState(EXPREGS, 1, 0, "EXPR"); +} diff --git a/apps/fceux/src/boards/t-262.cpp b/apps/fceux/src/boards/t-262.cpp new file mode 100644 index 00000000..01b873b3 --- /dev/null +++ b/apps/fceux/src/boards/t-262.cpp @@ -0,0 +1,73 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 bank, base, lock, mirr, mode; +static SFORMAT StateRegs[] = +{ + { &bank, 1, "BANK" }, + { &base, 1, "BASE" }, + { &lock, 1, "LOCK" }, + { &mirr, 1, "MIRR" }, + { &mode, 1, "MODE" }, + { 0 } +}; + +static void Sync(void) { + setchr8(0); + setprg16(0x8000, base | bank); + setprg16(0xC000, base | (mode ? bank : 7)); + setmirror(mirr); +} + +static DECLFW(BMCT262Write) { + if (!lock) { + base = ((A & 0x60) >> 2) | ((A & 0x100) >> 3); + mode = A & 0x80; + mirr = ((A & 2) >> 1) ^ 1; + lock = (A & 0x2000) >> 13; + } + bank = V & 7; + Sync(); +} + +static void BMCT262Power(void) { + lock = bank = base = mode = 0; + Sync(); + SetWriteHandler(0x8000, 0xFFFF, BMCT262Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void BMCT262Reset(void) { + lock = bank = base = mode = 0; + Sync(); +} + +static void BMCT262Restore(int version) { + Sync(); +} + +void BMCT262_Init(CartInfo *info) { + info->Power = BMCT262Power; + info->Reset = BMCT262Reset; + GameStateRestore = BMCT262Restore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/tengen.cpp b/apps/fceux/src/boards/tengen.cpp new file mode 100644 index 00000000..b47afff2 --- /dev/null +++ b/apps/fceux/src/boards/tengen.cpp @@ -0,0 +1,139 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +#include "mapinc.h" + +static uint8 cmd, mirr, regs[11]; +static uint8 rmode, IRQmode, IRQCount, IRQa, IRQLatch; + +static SFORMAT StateRegs[] = { + { regs, 11, "REGS" }, + { &cmd, 1, "CMDR" }, + { &mirr, 1, "MIRR" }, + { &rmode, 1, "RMOD" }, + { &IRQmode, 1, "IRQM" }, + { &IRQCount, 1, "IRQC" }, + { &IRQa, 1, "IRQA" }, + { &IRQLatch, 1, "IRQL" }, + { 0 } +}; + +static void M64IRQHook(int a) { + static int32 smallcount; + if (IRQmode) { + smallcount += a; + while (smallcount >= 4) { + smallcount -= 4; + IRQCount--; + if (IRQCount == 0xFF) + if (IRQa) X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void M64HBHook(void) { + if ((!IRQmode) && (scanline != 240)) { + rmode = 0; + IRQCount--; + if (IRQCount == 0xFF) { + if (IRQa) { + rmode = 1; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } +} + +static void Sync(void) { + if (cmd & 0x20) { + setchr1(0x0000, regs[0]); + setchr1(0x0400, regs[8]); + setchr1(0x0800, regs[1]); + setchr1(0x0C00, regs[9]); + } else { + setchr2(0x0000, regs[0] >> 1); + setchr2(0x0800, regs[1] >> 1); + } + setchr1(0x1000, regs[2]); + setchr1(0x1400, regs[3]); + setchr1(0x1800, regs[4]); + setchr1(0x1C00, regs[5]); + setprg8(0x8000, regs[6]); + setprg8(0xA000, regs[7]); + setprg8(0xC000, regs[10]); + setprg8(0xE000, ~0); + setmirror(mirr); +} + +static DECLFW(M64Write) { + switch (A & 0xF001) { + case 0xA000: mirr = (V & 1) ^ 1; Sync(); break;\ + case 0x8000: cmd = V; break; + case 0x8001: + if ((cmd & 0xF) < 10) + regs[cmd & 0xF] = V; + else if ((cmd & 0xF) == 0xF) + regs[10] = V; + Sync(); + break; + case 0xC000: + IRQLatch = V; + if (rmode == 1) + IRQCount = IRQLatch; + break; + case 0xC001: + rmode = 1; + IRQCount = IRQLatch; + IRQmode = V & 1; + break; + case 0xE000: + IRQa = 0; + X6502_IRQEnd(FCEU_IQEXT); + if (rmode == 1) + IRQCount = IRQLatch; + break; + case 0xE001: + IRQa = 1; + if (rmode == 1) + IRQCount = IRQLatch; + break; + } +} + +static void M64Power(void) { + cmd = mirr = 0; + regs[0] = regs[1] = regs[2] = regs[3] = regs[4] = regs[5] = ~0; + regs[6] = regs[7] = regs[8] = regs[9] = regs[10] = ~0; + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, M64Write); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper64_Init(CartInfo *info) { + info->Power = M64Power; + GameHBIRQHook = M64HBHook; + MapIRQHook = M64IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/tf-1201.cpp b/apps/fceux/src/boards/tf-1201.cpp new file mode 100644 index 00000000..24e8a771 --- /dev/null +++ b/apps/fceux/src/boards/tf-1201.cpp @@ -0,0 +1,109 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 CaH4e3 + * + * 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 + * + * Lethal Weapon (VRC4 mapper) + */ + +#include "mapinc.h" + +static uint8 prg0, prg1, mirr, swap; +static uint8 chr[8]; +static uint8 IRQCount; +static uint8 IRQPre; +static uint8 IRQa; + +static SFORMAT StateRegs[] = +{ + { &prg0, 1, "PRG0" }, + { &prg0, 1, "PRG1" }, + { &mirr, 1, "MIRR" }, + { &swap, 1, "SWAP" }, + { chr, 8, "CHR" }, + { &IRQCount, 1, "IRQC" }, + { &IRQPre, 1, "IRQP" }, + { &IRQa, 1, "IRQA" }, + { 0 } +}; + +static void SyncPrg(void) { + if (swap & 3) { + setprg8(0x8000, ~1); + setprg8(0xC000, prg0); + } else { + setprg8(0x8000, prg0); + setprg8(0xC000, ~1); + } + setprg8(0xA000, prg1); + setprg8(0xE000, ~0); +} + +static void SyncChr(void) { + int i; + for (i = 0; i < 8; i++) + setchr1(i << 10, chr[i]); + setmirror(mirr ^ 1); +} + +static void StateRestore(int version) { + SyncPrg(); + SyncChr(); +} + +static DECLFW(UNLTF1201Write) { + A = (A & 0xF003) | ((A & 0xC) >> 2); + if ((A >= 0xB000) && (A <= 0xE003)) { + int ind = (((A >> 11) - 6) | (A & 1)) & 7; + int sar = ((A & 2) << 1); + chr[ind] = (chr[ind] & (0xF0 >> sar)) | ((V & 0x0F) << sar); + SyncChr(); + } else switch (A & 0xF003) { + case 0x8000: prg0 = V; SyncPrg(); break; + case 0xA000: prg1 = V; SyncPrg(); break; + case 0x9000: mirr = V & 1; SyncChr(); break; + case 0x9001: swap = V & 3; SyncPrg(); break; + case 0xF000: IRQCount = ((IRQCount & 0xF0) | (V & 0xF)); break; + case 0xF002: IRQCount = ((IRQCount & 0x0F) | ((V & 0xF) << 4)); break; + case 0xF001: + case 0xF003: IRQa = V & 2; X6502_IRQEnd(FCEU_IQEXT); if (scanline < 240) IRQCount -= 8; break; + } +} + +static void UNLTF1201IRQCounter(void) { + if (IRQa) { + IRQCount++; + if (IRQCount == 237) { + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + +static void UNLTF1201Power(void) { + IRQPre = IRQCount = IRQa = 0; + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLTF1201Write); + SyncPrg(); + SyncChr(); +} + +void UNLTF1201_Init(CartInfo *info) { + info->Power = UNLTF1201Power; + GameHBIRQHook = UNLTF1201IRQCounter; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/transformer.cpp b/apps/fceux/src/boards/transformer.cpp new file mode 100644 index 00000000..b1252552 --- /dev/null +++ b/apps/fceux/src/boards/transformer.cpp @@ -0,0 +1,97 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +unsigned int *GetKeyboard(void); // FIXME: 10/28 - now implemented in SDL as well. should we rename this to a FCEUI_* function? + +static unsigned int *TransformerKeys, oldkeys[256]; +static int TransformerCycleCount, TransformerChar = 0; + +static void TransformerIRQHook(int a) { + TransformerCycleCount += a; + if (TransformerCycleCount >= 1000) { + uint32 i; + TransformerCycleCount -= 1000; + TransformerKeys = GetKeyboard(); + + for (i = 0; i < 256; i++) { + if (oldkeys[i] != TransformerKeys[i]) { + if (oldkeys[i] == 0) + TransformerChar = i; + else + TransformerChar = i | 0x80; + X6502_IRQBegin(FCEU_IQEXT); + memcpy((void*)&oldkeys[0], (void*)TransformerKeys, sizeof(oldkeys)); + break; + } + } + } +} + +static DECLFR(TransformerRead) { + uint8 ret = 0; + switch (A & 3) { + case 0: ret = TransformerChar & 15; break; + case 1: ret = (TransformerChar >> 4); break; + case 2: break; + case 4: break; + } + X6502_IRQEnd(FCEU_IQEXT); + return ret; +} + +static void TransformerPower(void) { + setprg8r(0x10, 0x6000, 0); + setprg16(0x8000, 0); + setprg16(0xC000, ~0); + setchr8(0); + + SetReadHandler(0x5000, 0x5004, TransformerRead); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x8000, 0xFFFF, CartBR); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + + MapIRQHook = TransformerIRQHook; +} + +static void TransformerClose(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +void Transformer_Init(CartInfo *info) { + info->Power = TransformerPower; + info->Close = TransformerClose; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); +} diff --git a/apps/fceux/src/boards/unrom512.cpp b/apps/fceux/src/boards/unrom512.cpp new file mode 100644 index 00000000..670596c1 --- /dev/null +++ b/apps/fceux/src/boards/unrom512.cpp @@ -0,0 +1,283 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2014 CaitSith2 + * + * 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 + */ + +/* + * Roms still using NES 1.0 format should be loaded as 32K CHR RAM. + * Roms defined under NES 2.0 should use the VRAM size field, defining 7, 8 or 9, based on how much VRAM should be present. + * UNIF doesn't have this problem, because unique board names can define this information. + * The UNIF names are UNROM-512-8K, UNROM-512-16K and UNROM-512-32K + * + * The battery flag in the NES header enables flash, Mirrror mode 2 Enables MI_0 and MI_1 mode. + * Known games to use this board are: + * Battle Kid 2: Mountain of Torment (512K PRG, 8K CHR RAM, Horizontal Mirroring, Flash disabled) + * Study Hall (128K PRG (in 512K flash chip), 8K CHR RAM, Horizontal Mirroring, Flash enabled) + * Although Xmas 2013 uses a different board, where LEDs can be controlled (with writes to the $8000-BFFF space), + * it otherwise functions identically. +*/ + +#include "mapinc.h" +#include "../ines.h" + +static uint8 latche, latcheinit, bus_conflict, chrram_mask, software_id=false; +static uint16 latcha; +static uint8 *flashdata; +static uint32 *flash_write_count; +static uint8 *FlashPage[32]; + +static uint8 flash_save=0, flash_state=0, flash_mode=0, flash_bank; +static void (*WLSync)(void); +static void (*WHSync)(void); + +static INLINE void setfpageptr(int s, uint32 A, uint8 *p) { + uint32 AB = A >> 11; + int x; + + if (p) + for (x = (s >> 1) - 1; x >= 0; x--) { + FlashPage[AB + x] = p - A; + } + else + for (x = (s >> 1) - 1; x >= 0; x--) { + FlashPage[AB + x] = 0; + } +} + +void setfprg16(uint32 A, uint32 V) { + if (PRGsize[0] >= 16384) { + V &= PRGmask16[0]; + setfpageptr(16, A, flashdata ? (&flashdata[V << 14]) : 0); + } else { + uint32 VA = V << 3; + int x; + + for (x = 0; x < 8; x++) + setfpageptr(2, A + (x << 11), flashdata ? (&flashdata[((VA + x) & PRGmask2[0]) << 11]) : 0); + } +} + +void inc_flash_write_count(uint8 bank, uint32 A) +{ + flash_write_count[(bank*4) + ((A&0x3000)>>12)]++; + if(!flash_write_count[(bank*4) + ((A&0x3000)>>12)]) + flash_write_count[(bank*4) + ((A&0x3000)>>12)]++; +} + +uint32 GetFlashWriteCount(uint8 bank, uint32 A) +{ + return flash_write_count[(bank*4) + ((A&0x3000)>>12)]; +} + +static void StateRestore(int version) { + WHSync(); +} + +static DECLFW(UNROM512LLatchWrite) +{ + latche = V; + latcha = A; + WLSync(); +} + +static DECLFW(UNROM512HLatchWrite) +{ + if (bus_conflict) + latche = (V == CartBR(A)) ? V : 0; + else + latche = V; + latcha = A; + WHSync(); +} + +static DECLFR(UNROM512LatchRead) +{ + uint8 flash_id[3]={0xB5,0xB6,0xB7}; + if(software_id) + { + if(A&1) + return flash_id[ROM_size>>4]; + else + return 0xBF; + } + if(flash_save) + { + if(A < 0xC000) + { + if(GetFlashWriteCount(flash_bank,A)) + return FlashPage[A >> 11][A]; + } + else + { + if(GetFlashWriteCount(ROM_size-1,A)) + return FlashPage[A >> 11][A]; + } + } + return Page[A >> 11][A]; +} + +static void UNROM512LatchPower(void) { + latche = latcheinit; + WHSync(); + SetReadHandler(0x8000, 0xFFFF, UNROM512LatchRead); + if(!flash_save) + SetWriteHandler(0x8000, 0xFFFF, UNROM512HLatchWrite); + else + { + SetWriteHandler(0x8000,0xBFFF,UNROM512LLatchWrite); + SetWriteHandler(0xC000,0xFFFF,UNROM512HLatchWrite); + } +} + +static void UNROM512LatchClose(void) { + if(flash_write_count) + FCEU_gfree(flash_write_count); + if(flashdata) + FCEU_gfree(flashdata); + flash_write_count = NULL; + flashdata = NULL; +} + + +static void UNROM512LSync() { + int erase_a[5]={0x9555,0xAAAA,0x9555,0x9555,0xAAAA}; + int erase_d[5]={0xAA,0x55,0x80,0xAA,0x55}; + int erase_b[5]={1,0,1,1,0}; + + if(flash_mode==0) + { + if((latcha == erase_a[flash_state]) && (latche == erase_d[flash_state]) && (flash_bank == erase_b[flash_state])) + { + flash_state++; + if(flash_state == 5) + { + flash_mode=1; + } + } + else if ((flash_state==2)&&(latcha==0x9555)&&(latche==0xA0)&&(flash_bank==1)) + { + flash_state++; + flash_mode=2; + } + else if ((flash_state==2)&&(latcha==0x9555)&&(latche==0x90)&&(flash_bank==1)) + { + flash_state=0; + software_id=true; + } + else + { + if(latche==0xF0) + software_id=false; + flash_state=0; + } + } + else if(flash_mode==1) //Chip Erase or Sector Erase + { + if(latche==0x30) + { + inc_flash_write_count(flash_bank,latcha); + memset(&FlashPage[(latcha & 0xF000) >> 11][latcha & 0xF000],0xFF,0x1000); + } + else if (latche==0x10) + { + for(uint32 i=0;i<(ROM_size*4);i++) + inc_flash_write_count(i>>2,i<<12); + memset(flashdata,0xFF,ROM_size*0x4000); //Erasing the rom chip as instructed. Crash rate calulated to be 99.9% :) + } + flash_state=0; + flash_mode=0; + } + else if(flash_mode==2) //Byte Program + { + if(!GetFlashWriteCount(flash_bank,latcha)) + { + inc_flash_write_count(flash_bank,latcha); + memcpy(&FlashPage[(latcha & 0xF000) >> 11][latcha & 0xF000],&Page[(latcha & 0xF000)>>11][latcha & 0xF000],0x1000); + } + FlashPage[latcha>>11][latcha]&=latche; + flash_state=0; + flash_mode=0; + } +} + +static void UNROM512HSync() +{ + flash_bank=latche&(ROM_size-1); + + setprg16(0x8000, flash_bank); + setprg16(0xc000, ~0); + setfprg16(0x8000, flash_bank); + setfprg16(0xC000, ~0); + setchr8r(0, (latche & chrram_mask) >> 5); + setmirror(MI_0+(latche>>7)); +} + +void UNROM512_Init(CartInfo *info) { + flash_state=0; + flash_bank=0; + flash_save=info->battery; + + if(info->vram_size == 8192) + chrram_mask = 0; + else if (info->vram_size == 16384) + chrram_mask = 0x20; + else + chrram_mask = 0x60; + + int mirror = (head.ROM_type & 1) | ((head.ROM_type & 8) >> 2); + switch (mirror) + { + case 0: // hard horizontal, internal + SetupCartMirroring(MI_H, 1, NULL); + break; + case 1: // hard vertical, internal + SetupCartMirroring(MI_V, 1, NULL); + break; + case 2: // switchable 1-screen, internal (flags: 4-screen + horizontal) + SetupCartMirroring(MI_0, 0, NULL); + break; + case 3: // hard four screen, last 8k of 32k RAM (flags: 4-screen + vertical) + SetupCartMirroring( 4, 1, VROM + (info->vram_size - 8192)); + break; + } + + bus_conflict = !info->battery; + latcheinit = 0; + WLSync = UNROM512LSync; + WHSync = UNROM512HSync; + info->Power = UNROM512LatchPower; + info->Close = UNROM512LatchClose; + GameStateRestore = StateRestore; + if(flash_save) + { + flashdata = (uint8*)FCEU_gmalloc(ROM_size*0x4000); + flash_write_count = (uint32*)FCEU_gmalloc(ROM_size*4*sizeof(uint32)); + info->SaveGame[0] = (uint8*)flash_write_count; + info->SaveGame[1] = flashdata; + info->SaveGameLen[0] = ROM_size*4*sizeof(uint32); + info->SaveGameLen[1] = ROM_size*0x4000; + AddExState(flash_write_count,ROM_size*4*sizeof(uint32),0,"FLASH_WRITE_COUNT"); + AddExState(flashdata,ROM_size*0x4000,0,"FLASH_DATA"); + AddExState(&flash_state,1,0,"FLASH_STATE"); + AddExState(&flash_mode,1,0,"FLASH_MODE"); + AddExState(&flash_bank,1,0,"FLASH_BANK"); + AddExState(&latcha,2,0,"LATA"); + } + AddExState(&latche, 1, 0, "LATC"); + AddExState(&bus_conflict, 1, 0, "BUSC"); +} diff --git a/apps/fceux/src/boards/vrc1.cpp b/apps/fceux/src/boards/vrc1.cpp new file mode 100644 index 00000000..899bcfb5 --- /dev/null +++ b/apps/fceux/src/boards/vrc1.cpp @@ -0,0 +1,70 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * VRC-1 + * + */ + +#include "mapinc.h" + +static uint8 preg[3], creg[2], mode; +static SFORMAT StateRegs[] = +{ + { &mode, 1, "MODE" }, + { creg, 2, "CREG" }, + { preg, 3, "PREG" }, + { 0 } +}; + +static void Sync(void) { + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, ~0); + setchr4(0x0000, creg[0] | ((mode & 2) << 3)); + setchr4(0x1000, creg[1] | ((mode & 4) << 2)); + setmirror((mode & 1) ^ 1); +} + +static DECLFW(M75Write) { + switch (A & 0xF000) { + case 0x8000: preg[0] = V; Sync(); break; + case 0x9000: mode = V; Sync(); break; + case 0xA000: preg[1] = V; Sync(); break; + case 0xC000: preg[2] = V; Sync(); break; + case 0xE000: creg[0] = V & 0xF; Sync(); break; + case 0xF000: creg[1] = V & 0xF; Sync(); break; + } +} + +static void M75Power(void) { + Sync(); + SetWriteHandler(0x8000, 0xFFFF, M75Write); + SetReadHandler(0x8000, 0xFFFF, CartBR); +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper75_Init(CartInfo *info) { + info->Power = M75Power; + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/vrc2and4.cpp b/apps/fceux/src/boards/vrc2and4.cpp new file mode 100644 index 00000000..e95b7d40 --- /dev/null +++ b/apps/fceux/src/boards/vrc2and4.cpp @@ -0,0 +1,232 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2007 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static bool isPirate; +static uint8 is22, reg1mask, reg2mask; +static uint16 IRQCount; +static uint8 IRQLatch, IRQa; +static uint8 prgreg[2], chrreg[8]; +static uint16 chrhi[8]; +static uint8 regcmd, irqcmd, mirr, big_bank; +static uint16 acount = 0; + +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { prgreg, 2, "PREG" }, + { chrreg, 8, "CREG" }, + { chrhi, 16, "CRGH" }, + { ®cmd, 1, "CMDR" }, + { &irqcmd, 1, "CMDI" }, + { &mirr, 1, "MIRR" }, + { &big_bank, 1, "BIGB" }, + { &IRQCount, 2, "IRQC" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQa, 1, "IRQA" }, + { 0 } +}; + +static void Sync(void) { + if (regcmd & 2) { + setprg8(0xC000, prgreg[0] | big_bank); + setprg8(0x8000, ((~1) & 0x1F) | big_bank); + } else { + setprg8(0x8000, prgreg[0] | big_bank); + setprg8(0xC000, ((~1) & 0x1F) | big_bank); + } + setprg8(0xA000, prgreg[1] | big_bank); + setprg8(0xE000, ((~0) & 0x1F) | big_bank); + if (UNIFchrrama) + setchr8(0); + else{ + uint8 i; + //if(!weirdo) + for (i = 0; i < 8; i++) + setchr1(i << 10, (chrhi[i] | chrreg[i]) >> is22); + //else { + // setchr1(0x0000, 0xFC); + // setchr1(0x0400, 0xFD); + // setchr1(0x0800, 0xFF); + // weirdo--; + //} + } + switch (mirr & 0x3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(VRC24Write) { + A = (A & 0xF000) | !!(A & reg2mask) << 1 | !!(A & reg1mask); + if ((A >= 0xB000) && (A <= 0xE003)) { + if (UNIFchrrama) + big_bank = (V & 8) << 2; // my personally many-in-one feature ;) just for support pirate cart 2-in-1 + else{ + uint16 i = ((A >> 1) & 1) | ((A - 0xB000) >> 11); + uint16 nibble = ((A & 1) << 2); + chrreg[i] = (chrreg[i] & (0xF0 >> nibble)) | ((V & 0xF) << nibble); + if(nibble) + chrhi[i] = (V & 0x10) << 4; // another one many in one feature from pirate carts + } + Sync(); + } else + switch (A & 0xF003) { + case 0x8000: + case 0x8001: + case 0x8002: + case 0x8003: + if (!isPirate) { + prgreg[0] = V & 0x1F; + Sync(); + } + break; + case 0xA000: + case 0xA001: + case 0xA002: + case 0xA003: + if (!isPirate) + prgreg[1] = V & 0x1F; + else{ + prgreg[0] = (V & 0x1F) << 1; + prgreg[1] = ((V & 0x1F) << 1) | 1; + } + Sync(); + break; + case 0x9000: + case 0x9001: if (V != 0xFF) mirr = V; Sync(); break; + case 0x9002: + case 0x9003: regcmd = V; Sync(); break; + case 0xF000: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0xF0; IRQLatch |= V & 0xF; break; + case 0xF001: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0x0F; IRQLatch |= V << 4; break; + case 0xF002: X6502_IRQEnd(FCEU_IQEXT); acount = 0; IRQCount = IRQLatch; IRQa = V & 2; irqcmd = V & 1; break; + case 0xF003: X6502_IRQEnd(FCEU_IQEXT); IRQa = irqcmd; break; + } +} + +static void VRC24Power(void) { + big_bank = 0x20; + Sync(); + if (WRAM) { + setprg8r(0x10, 0x6000, 0); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + } + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, VRC24Write); +} + +void VRC24IRQHook(int a) { + #define LCYCS 341 + if (IRQa) { + acount += a * 3; + if (acount >= LCYCS) { + while (acount >= LCYCS) { + acount -= LCYCS; + IRQCount++; + if (IRQCount & 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +static void VRC24Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void VRC24_Init(CartInfo *info) { + info->Power = VRC24Power; + info->Close = VRC24Close; + MapIRQHook = VRC24IRQHook; + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if(info->battery) { + info->SaveGame[0]=WRAM; + info->SaveGameLen[0]=WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper21_Init(CartInfo *info) { + isPirate = false; + is22 = 0; + reg1mask = 0x42; + reg2mask = 0x84; + VRC24_Init(info); +} + +void Mapper22_Init(CartInfo *info) { + isPirate = false; + is22 = 1; + reg1mask = 2; + reg2mask = 1; + + // no IRQ (all mapper 22 games are VRC2) + // no WRAM + info->Power = VRC24Power; + GameStateRestore = StateRestore; + + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper23_Init(CartInfo *info) { + isPirate = false; + is22 = 0; + reg1mask = 0x15; + reg2mask = 0x2a; + VRC24_Init(info); +} + +void Mapper25_Init(CartInfo *info) { + isPirate = false; + is22 = 0; + reg1mask = 0xa; + reg2mask = 0x5; + VRC24_Init(info); +} + +void UNLT230_Init(CartInfo *info) { + isPirate = true; + is22 = 0; + reg1mask = 0x15; + reg2mask = 0x2a; + VRC24_Init(info); +} diff --git a/apps/fceux/src/boards/vrc3.cpp b/apps/fceux/src/boards/vrc3.cpp new file mode 100644 index 00000000..f6471ae1 --- /dev/null +++ b/apps/fceux/src/boards/vrc3.cpp @@ -0,0 +1,135 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + * + * VRC-3 + * + */ + +#include "mapinc.h" + +static uint8 preg; +static uint8 IRQx; //autoenable +static uint8 IRQm; //mode +static uint8 IRQa; +static uint16 IRQReload, IRQCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { &preg, 1, "PREG" }, + { &IRQa, 1, "IRQA" }, + { &IRQx, 1, "IRQX" }, + { &IRQm, 1, "IRQM" }, + { &IRQReload, 2, "IRQR" }, + { &IRQCount, 2, "IRQC" }, + { 0 } +}; + +static void Sync(void) { + setprg8r(0x10, 0x6000, 0); + setprg16(0x8000, preg); + setprg16(0xC000, ~0); + setchr8(0); +} + +static DECLFW(M73Write) { + switch (A & 0xF000) { + case 0x8000: IRQReload &= 0xFFF0; IRQReload |= (V & 0xF) << 0; break; + case 0x9000: IRQReload &= 0xFF0F; IRQReload |= (V & 0xF) << 4; break; + case 0xA000: IRQReload &= 0xF0FF; IRQReload |= (V & 0xF) << 8; break; + case 0xB000: IRQReload &= 0x0FFF; IRQReload |= (V & 0xF) << 12; break; + case 0xC000: + IRQm = V & 4; + IRQx = V & 1; + IRQa = V & 2; + if (IRQa) { + if (IRQm) { + IRQCount &= 0xFFFF; + IRQCount |= (IRQReload & 0xFF); + } else + IRQCount = IRQReload; + } + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xD000: X6502_IRQEnd(FCEU_IQEXT); IRQa = IRQx; break; + case 0xF000: preg = V; Sync(); break; + } +} + +static void M73IRQHook(int a) { + int32 i; + if (!IRQa) return; + for (i = 0; i < a; i++) { + if (IRQm) { + uint16 temp = IRQCount; + temp &= 0xFF; + IRQCount &= 0xFF00; + if (temp == 0xFF) { + IRQCount = IRQReload; + IRQCount |= (uint16)(IRQReload & 0xFF); + X6502_IRQBegin(FCEU_IQEXT); + } else { + temp++; + IRQCount |= temp; + } + } else { + //16 bit mode + if (IRQCount == 0xFFFF) { + IRQCount = IRQReload; + X6502_IRQBegin(FCEU_IQEXT); + } else + IRQCount++; + } + } +} + +static void M73Power(void) { + IRQReload = IRQm = IRQx = 0; + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0xFFFF, M73Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void M73Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper73_Init(CartInfo *info) { + info->Power = M73Power; + info->Close = M73Close; + MapIRQHook = M73IRQHook; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); + GameStateRestore = StateRestore; +} diff --git a/apps/fceux/src/boards/vrc5.cpp b/apps/fceux/src/boards/vrc5.cpp new file mode 100644 index 00000000..07de8010 --- /dev/null +++ b/apps/fceux/src/boards/vrc5.cpp @@ -0,0 +1,265 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005-2019 CaH4e3 + * + * 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 + * + * VRC-V (CAI Shogakko no Sansu) + * + */ + +#include "mapinc.h" + +//#define CAI_DEBUG + +// main tiles RAM is 8K in size, but unless other non-CHR ROM type carts, +// this one accesses the $0000 and $1000 pages based on extra NT RAM on board +// which is similar to MMC5 but much simpler because there are no additional +// bankings here. +// extra NT RAM handling is in PPU code now. + +static uint16 CHRSIZE = 8192; +// there are two separate WRAMs 8K each, on main system cartridge (not battery +// backed), and one on the daughter cart (with battery). both are accessed +// via the same registers with additional selector flags. +static uint16 WRAMSIZE = 8192 + 8192; +static uint8 *CHRRAM = NULL; +static uint8 *WRAM = NULL; + +static uint8 IRQa, K4IRQ; +static uint32 IRQLatch, IRQCount; + +// some kind of 16-bit text encoding (actually 14-bit) used in game resources +// may be converted by the hardware into the tile indexes for internal CHR ROM +// not sure whey they made it hardware, because most of calculations are just +// bit shifting. the main purpose of this table is to calculate actual CHR ROM +// bank for every character. there is a some kind of regularity, so this table +// may be calculated in software easily. + +// table read out from hardware registers as is + +///* +static uint8 conv_tbl[4][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x40, 0x10, 0x28, 0x00, 0x18, 0x30 }, + { 0x00, 0x00, 0x48, 0x18, 0x30, 0x08, 0x20, 0x38 }, + { 0x00, 0x00, 0x80, 0x20, 0x38, 0x10, 0x28, 0xB0 } +}; +//*/ +/* +static uint8 conv_tbl[64][4] = { + { 0x40, 0x40, 0x40, 0x40 }, // 00 | A - 40 41 42 43 44 45 46 47 + { 0x41, 0x41, 0x41, 0x41 }, // 02 | B - 48 49 4A 4B 4C 4D 4E 4F + { 0x42, 0x42, 0x42, 0x42 }, // 04 | C - 50 51 52 53 54 55 56 57 + { 0x43, 0x43, 0x43, 0x43 }, // 06 | D - 58 59 5A 5B 5C 5D 5E 5F + { 0x44, 0x44, 0x44, 0x44 }, // 08 | E - 60 61 62 63 64 65 66 67 + { 0x45, 0x45, 0x45, 0x45 }, // 0A | F - 68 69 6A 6B 6C 6D 6E 6F + { 0x46, 0x46, 0x46, 0x46 }, // 0C | G - 70 71 72 73 74 75 76 77 + { 0x47, 0x47, 0x47, 0x47 }, // 0E | H - 78 79 7A 7B 7C 7D 7E 7F + { 0x40, 0x40, 0x40, 0x40 }, // 10 | + { 0x41, 0x41, 0x41, 0x41 }, // 12 +---------------------------- + { 0x42, 0x42, 0x42, 0x42 }, // 14 | A A A A + { 0x43, 0x43, 0x43, 0x43 }, // 16 | A A A A + { 0x44, 0x44, 0x44, 0x44 }, // 18 | A A' B' A" + { 0x45, 0x45, 0x45, 0x45 }, // 1A | A C D E + { 0x46, 0x46, 0x46, 0x46 }, // 1C | A F G H + { 0x47, 0x47, 0x47, 0x47 }, // 1E | A A B C + { 0x40, 0x40, 0x48, 0x44 }, // 20 | A D E F + { 0x41, 0x41, 0x49, 0x45 }, // 22 | A G H G" + { 0x42, 0x42, 0x4A, 0x46 }, // 24 +---------------------------- + { 0x43, 0x43, 0x4B, 0x47 }, // 26 | A' - 40 41 42 43 40 41 42 43 + { 0x44, 0x40, 0x48, 0x44 }, // 28 | A" - 44 45 46 47 44 45 46 47 + { 0x45, 0x41, 0x49, 0x45 }, // 2A | B' - 48 49 4A 4B 48 49 4A 4B + { 0x46, 0x42, 0x4A, 0x46 }, // 2C | G" - 74 75 76 77 74 75 76 77 + { 0x47, 0x43, 0x4B, 0x47 }, // 2E + { 0x40, 0x50, 0x58, 0x60 }, // 30 + { 0x41, 0x51, 0x59, 0x61 }, // 32 + { 0x42, 0x52, 0x5A, 0x62 }, // 34 + { 0x43, 0x53, 0x5B, 0x63 }, // 36 + { 0x44, 0x54, 0x5C, 0x64 }, // 38 + { 0x45, 0x55, 0x5D, 0x65 }, // 3A + { 0x46, 0x56, 0x5E, 0x66 }, // 3C + { 0x47, 0x57, 0x5F, 0x67 }, // 3E + { 0x40, 0x68, 0x70, 0x78 }, // 40 + { 0x41, 0x69, 0x71, 0x79 }, // 42 + { 0x42, 0x6A, 0x72, 0x7A }, // 44 + { 0x43, 0x6B, 0x73, 0x7B }, // 46 + { 0x44, 0x6C, 0x74, 0x7C }, // 48 + { 0x45, 0x6D, 0x75, 0x7D }, // 4A + { 0x46, 0x6E, 0x76, 0x7E }, // 4C + { 0x47, 0x6F, 0x77, 0x7F }, // 4E + { 0x40, 0x40, 0x48, 0x50 }, // 50 + { 0x41, 0x41, 0x49, 0x51 }, // 52 + { 0x42, 0x42, 0x4A, 0x52 }, // 54 + { 0x43, 0x43, 0x4B, 0x53 }, // 56 + { 0x44, 0x44, 0x4C, 0x54 }, // 58 + { 0x45, 0x45, 0x4D, 0x55 }, // 5A + { 0x46, 0x46, 0x4E, 0x56 }, // 5C + { 0x47, 0x47, 0x4F, 0x57 }, // 5E + { 0x40, 0x58, 0x60, 0x68 }, // 60 + { 0x41, 0x59, 0x61, 0x69 }, // 62 + { 0x42, 0x5A, 0x62, 0x6A }, // 64 + { 0x43, 0x5B, 0x63, 0x6B }, // 66 + { 0x44, 0x5C, 0x64, 0x6C }, // 68 + { 0x45, 0x5D, 0x65, 0x6D }, // 6A + { 0x46, 0x5E, 0x66, 0x6E }, // 6C + { 0x47, 0x5F, 0x67, 0x6F }, // 6E + { 0x40, 0x70, 0x78, 0x74 }, // 70 + { 0x41, 0x71, 0x79, 0x75 }, // 72 + { 0x42, 0x72, 0x7A, 0x76 }, // 74 + { 0x43, 0x73, 0x7B, 0x77 }, // 76 + { 0x44, 0x74, 0x7C, 0x74 }, // 78 + { 0x45, 0x75, 0x7D, 0x75 }, // 7A + { 0x46, 0x76, 0x7E, 0x76 }, // 7C + { 0x47, 0x77, 0x7F, 0x77 }, // 7E +}; +*/ + +static uint8 regs[16]; +static SFORMAT StateRegs[] = +{ + { &IRQCount, 1, "IRQC" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQa, 1, "IRQA" }, + { &K4IRQ, 1, "KIRQ" }, + { regs, 16, "REGS" }, + { 0 } +}; + +static void chrSync(void) { + setchr4r(0x10, 0x0000, regs[5] & 1); + // 30.06.19 CaH4e3 there is much more complicated behaviour with second banking register, you may actually + // view the content of the internal character CHR rom via this window, but it is useless because hardware + // does not use this area to access the internal ROM. not sure why they did this, but I see no need to + // emulate this behaviour carefully, unless I find something that I missed... + setchr4r(0x10, 0x1000, 1); +} + +static void Sync(void) { + chrSync(); + setprg4r(0x10, 0x6000, (regs[0] & 1) | (regs[0] >> 2)); // two 4K banks are identical, either internal or excernal + setprg4r(0x10, 0x7000, (regs[1] & 1) | (regs[1] >> 2)); // SRAMs may be mapped in any bank independently + if (PRGptr[1] == NULL) { // for iNES 2.0 version it even more hacky lol + setprg8(0x8000, (regs[2] & 0x3F) + ((regs[2] & 0x40) >> 2)); + setprg8(0xA000, (regs[3] & 0x3F) + ((regs[3] & 0x40) >> 2)); + setprg8(0xC000, (regs[4] & 0x3F) + ((regs[4] & 0x40) >> 2)); + setprg8(0xE000, 0x10 + 0x3F); + } else { + setprg8r((regs[2] >> 6) & 1, 0x8000, (regs[2] & 0x3F)); + setprg8r((regs[3] >> 6) & 1, 0xA000, (regs[3] & 0x3F)); + setprg8r((regs[4] >> 6) & 1, 0xC000, (regs[4] & 0x3F)); + setprg8r(1, 0xE000, ~0); // always sees the last bank of the external cart, so can't be booted without it. + } + setmirror(((regs[0xA]&2)>>1)^1); +} + +static DECLFW(QTAiWrite) { + regs[(A & 0x0F00) >> 8] = V; // IRQ pretty the same as in other VRC mappers by Konami + switch (A) { + case 0xd600: IRQLatch &= 0xFF00; IRQLatch |= V; break; + case 0xd700: IRQLatch &= 0x00FF; IRQLatch |= V << 8; break; + case 0xd900: IRQCount = IRQLatch; IRQa = V & 2; K4IRQ = V & 1; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xd800: IRQa = K4IRQ; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xda00: qtaintramreg = regs[0xA] & 3; break; // register shadow to share it with ppu + } + Sync(); +} + +static DECLFR(QTAiRead) { + +// uint8 res1 = conv_tbl[(regs[0xD] & 0x7F) >> 1][(regs[0xC] >> 5) & 3]; +// uint8 res2 = ((regs[0xD] & 1) << 7) | ((regs[0xC] & 0x1F) << 2) | (regs[0xB] & 3); + + uint8 tabl = conv_tbl[(regs[0xC] >> 5) & 3][(regs[0xD] & 0x7F) >> 4]; + uint8 res1 = 0x40 | (tabl & 0x3F) | ((regs[0xD] >> 1) & 7) | ((regs[0xB] & 4) << 5); + uint8 res2 = ((regs[0xD] & 1) << 7) | ((regs[0xC] & 0x1F) << 2) | (regs[0xB] & 3); + + if (tabl & 0x40) + res1 &= 0xFB; + else if (tabl & 0x80) + res1 |= 0x04; + + if (A == 0xDD00) { + return res1; + } else if (A == 0xDC00) { +#ifdef CAI_DEBUG + FCEU_printf("%02x:%02x+%d -> %02x:%02x\n", regs[0xD], regs[0xC], regs[0xB], res1, res2); +#endif + return res2; + } else + return 0; +} + +static void VRC5IRQ(int a) { + if (IRQa) { + IRQCount += a; + if (IRQCount & 0x10000) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } +} + +static void QTAiPower(void) { + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0xFFFF, QTAiWrite); + SetReadHandler(0xDC00, 0xDC00, QTAiRead); + SetReadHandler(0xDD00, 0xDD00, QTAiRead); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); + Sync(); +} + +static void QTAiClose(void) { + if (CHRRAM) + FCEU_gfree(CHRRAM); + CHRRAM = NULL; + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +void QTAi_Init(CartInfo *info) { + QTAIHack = 1; + + info->Power = QTAiPower; + info->Close = QTAiClose; + GameStateRestore = StateRestore; + + MapIRQHook = VRC5IRQ; + + CHRRAM = (uint8*)FCEU_gmalloc(CHRSIZE); + SetupCartCHRMapping(0x10, CHRRAM, CHRSIZE, 1); + AddExState(CHRRAM, CHRSIZE, 0, "CRAM"); + + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + if (info->battery) { + info->SaveGame[0] = WRAM; + // note, only extrnal cart's SRAM is battery backed, the the part on the main cartridge is just + // an additional work ram. so we may save only half here, but I forgot what part is saved lol, will + // find out later. + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/vrc6.cpp b/apps/fceux/src/boards/vrc6.cpp new file mode 100644 index 00000000..43cd3824 --- /dev/null +++ b/apps/fceux/src/boards/vrc6.cpp @@ -0,0 +1,378 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + * + * VRC-6 + * + */ + +#include "mapinc.h" + +static uint8 is26; +static uint8 prg[2], chr[8], mirr; +static uint8 IRQLatch, IRQa, IRQd; +static int32 IRQCount, CycleCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static SFORMAT StateRegs[] = +{ + { prg, 2, "PRG" }, + { chr, 8, "CHR" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQd, 1, "IRQD" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQCount, 4, "IRQC" }, + { &CycleCount, 4, "CYCC" }, + { 0 } +}; + +static void(*sfun[3]) (void); +static uint8 vpsg1[8]; +static uint8 vpsg2[4]; +static int32 cvbc[3]; +static int32 vcount[3]; +static int32 dcount[2]; + +static SFORMAT SStateRegs[] = +{ + { vpsg1, 8, "PSG1" }, + { vpsg2, 4, "PSG2" }, + { 0 } +}; + +static void Sync(void) { + uint8 i; + if (is26) + setprg8r(0x10, 0x6000, 0); + setprg16(0x8000, prg[0]); + setprg8(0xc000, prg[1]); + setprg8(0xe000, ~0); + for (i = 0; i < 8; i++) + setchr1(i << 10, chr[i]); + switch (mirr & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(VRC6SW) { + A &= 0xF003; + if (A >= 0x9000 && A <= 0x9002) { + vpsg1[A & 3] = V; + if (sfun[0]) sfun[0](); + } else if (A >= 0xA000 && A <= 0xA002) { + vpsg1[4 | (A & 3)] = V; + if (sfun[1]) sfun[1](); + } else if (A >= 0xB000 && A <= 0xB002) { + vpsg2[A & 3] = V; + if (sfun[2]) sfun[2](); + } +} + +static DECLFW(VRC6Write) { + if (is26) + A = (A & 0xFFFC) | ((A >> 1) & 1) | ((A << 1) & 2); + if (A >= 0x9000 && A <= 0xB002) { + VRC6SW(A, V); + return; + } + switch (A & 0xF003) { + case 0x8000: prg[0] = V; Sync(); break; + case 0xB003: mirr = (V >> 2) & 3; Sync(); break; + case 0xC000: prg[1] = V; Sync(); break; + case 0xD000: chr[0] = V; Sync(); break; + case 0xD001: chr[1] = V; Sync(); break; + case 0xD002: chr[2] = V; Sync(); break; + case 0xD003: chr[3] = V; Sync(); break; + case 0xE000: chr[4] = V; Sync(); break; + case 0xE001: chr[5] = V; Sync(); break; + case 0xE002: chr[6] = V; Sync(); break; + case 0xE003: chr[7] = V; Sync(); break; + case 0xF000: IRQLatch = V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xF001: + IRQa = V & 2; + IRQd = V & 1; + if (V & 2) + IRQCount = IRQLatch; + CycleCount = 0; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xF002: + IRQa = IRQd; + X6502_IRQEnd(FCEU_IQEXT); + } +} + +static void VRC6Power(void) { + Sync(); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetWriteHandler(0x8000, 0xFFFF, VRC6Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void VRC6IRQHook(int a) { + if (IRQa) { + CycleCount += a * 3; + while(CycleCount >= 341) { + CycleCount -= 341; + IRQCount++; + if (IRQCount == 0x100) { + IRQCount = IRQLatch; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } +} + +static void VRC6Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void StateRestore(int version) { + Sync(); +} + +// VRC6 Sound + +static void DoSQV1(void); +static void DoSQV2(void); +static void DoSawV(void); + +static INLINE void DoSQV(int x) { + int32 V; + int32 amp = (((vpsg1[x << 2] & 15) << 8) * 6 / 8) >> 4; + int32 start, end; + + start = cvbc[x]; + end = (SOUNDTS << 16) / soundtsinc; + if (end <= start) return; + cvbc[x] = end; + + if (vpsg1[(x << 2) | 0x2] & 0x80) { + if (vpsg1[x << 2] & 0x80) { + for (V = start; V < end; V++) + Wave[V >> 4] += amp; + } else { + int32 thresh = (vpsg1[x << 2] >> 4) & 7; + int32 freq = ((vpsg1[(x << 2) | 0x1] | ((vpsg1[(x << 2) | 0x2] & 15) << 8)) + 1) << 17; + for (V = start; V < end; V++) { + if (dcount[x] > thresh) + Wave[V >> 4] += amp; + vcount[x] -= nesincsize; + while (vcount[x] <= 0) { + vcount[x] += freq; + dcount[x] = (dcount[x] + 1) & 15; + } + } + } + } +} + +static void DoSQV1(void) { + DoSQV(0); +} + +static void DoSQV2(void) { + DoSQV(1); +} + +static void DoSawV(void) { + int V; + int32 start, end; + + start = cvbc[2]; + end = (SOUNDTS << 16) / soundtsinc; + if (end <= start) return; + cvbc[2] = end; + + if (vpsg2[2] & 0x80) { + static int32 saw1phaseacc = 0; + uint32 freq3; + static uint8 b3 = 0; + static int32 phaseacc = 0; + static uint32 duff = 0; + + freq3 = (vpsg2[1] + ((vpsg2[2] & 15) << 8) + 1); + + for (V = start; V < end; V++) { + saw1phaseacc -= nesincsize; + if (saw1phaseacc <= 0) { + int32 t; + rea: + t = freq3; + t <<= 18; + saw1phaseacc += t; + phaseacc += vpsg2[0] & 0x3f; + b3++; + if (b3 == 7) { + b3 = 0; + phaseacc = 0; + } + if (saw1phaseacc <= 0) + goto rea; + duff = (((phaseacc >> 3) & 0x1f) << 4) * 6 / 8; + } + Wave[V >> 4] += duff; + } + } +} + +static INLINE void DoSQVHQ(int x) { + int32 V; + int32 amp = ((vpsg1[x << 2] & 15) << 8) * 6 / 8; + + if (vpsg1[(x << 2) | 0x2] & 0x80) { + if (vpsg1[x << 2] & 0x80) { + for (V = cvbc[x]; V < (int)SOUNDTS; V++) + WaveHi[V] += amp; + } else { + int32 thresh = (vpsg1[x << 2] >> 4) & 7; + for (V = cvbc[x]; V < (int)SOUNDTS; V++) { + if (dcount[x] > thresh) + WaveHi[V] += amp; + vcount[x]--; + if (vcount[x] <= 0) { + vcount[x] = (vpsg1[(x << 2) | 0x1] | ((vpsg1[(x << 2) | 0x2] & 15) << 8)) + 1; + dcount[x] = (dcount[x] + 1) & 15; + } + } + } + } + cvbc[x] = SOUNDTS; +} + +static void DoSQV1HQ(void) { + DoSQVHQ(0); +} + +static void DoSQV2HQ(void) { + DoSQVHQ(1); +} + +static void DoSawVHQ(void) { + static uint8 b3 = 0; + static int32 phaseacc = 0; + int32 V; + + if (vpsg2[2] & 0x80) { + for (V = cvbc[2]; V < (int)SOUNDTS; V++) { + WaveHi[V] += (((phaseacc >> 3) & 0x1f) << 8) * 6 / 8; + vcount[2]--; + if (vcount[2] <= 0) { + vcount[2] = (vpsg2[1] + ((vpsg2[2] & 15) << 8) + 1) << 1; + phaseacc += vpsg2[0] & 0x3f; + b3++; + if (b3 == 7) { + b3 = 0; + phaseacc = 0; + } + } + } + } + cvbc[2] = SOUNDTS; +} + + +void VRC6Sound(int Count) { + int x; + + DoSQV1(); + DoSQV2(); + DoSawV(); + for (x = 0; x < 3; x++) + cvbc[x] = Count; +} + +void VRC6SoundHQ(void) { + DoSQV1HQ(); + DoSQV2HQ(); + DoSawVHQ(); +} + +void VRC6SyncHQ(int32 ts) { + int x; + for (x = 0; x < 3; x++) cvbc[x] = ts; +} + +static void VRC6_ESI(void) { + GameExpSound.RChange = VRC6_ESI; + GameExpSound.Fill = VRC6Sound; + GameExpSound.HiFill = VRC6SoundHQ; + GameExpSound.HiSync = VRC6SyncHQ; + + memset(cvbc, 0, sizeof(cvbc)); + memset(vcount, 0, sizeof(vcount)); + memset(dcount, 0, sizeof(dcount)); + if (FSettings.SndRate) { + if (FSettings.soundq >= 1) { + sfun[0] = DoSQV1HQ; + sfun[1] = DoSQV2HQ; + sfun[2] = DoSawVHQ; + } else { + sfun[0] = DoSQV1; + sfun[1] = DoSQV2; + sfun[2] = DoSawV; + } + } else + memset(sfun, 0, sizeof(sfun)); + AddExState(&SStateRegs, ~0, 0, 0); +} + +// VRC6 Sound + +void Mapper24_Init(CartInfo *info) { + is26 = 0; + info->Power = VRC6Power; + MapIRQHook = VRC6IRQHook; + VRC6_ESI(); + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper26_Init(CartInfo *info) { + is26 = 1; + info->Power = VRC6Power; + info->Close = VRC6Close; + MapIRQHook = VRC6IRQHook; + VRC6_ESI(); + GameStateRestore = StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + + AddExState(&StateRegs, ~0, 0, 0); +} + +void NSFVRC6_Init(void) { + VRC6_ESI(); + SetWriteHandler(0x8000, 0xbfff, VRC6SW); +} diff --git a/apps/fceux/src/boards/vrc7.cpp b/apps/fceux/src/boards/vrc7.cpp new file mode 100644 index 00000000..d4df33a9 --- /dev/null +++ b/apps/fceux/src/boards/vrc7.cpp @@ -0,0 +1,205 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2012 CaH4e3 + * + * 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 + */ + +#include "mapinc.h" + +static uint8 vrc7idx, preg[3], creg[8], mirr; +static uint8 IRQLatch, IRQa, IRQd; +static int32 IRQCount, CycleCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +#include "emu2413.h" + +static int32 dwave = 0; +static OPLL *VRC7Sound = NULL; +static OPLL **VRC7Sound_saveptr = &VRC7Sound; + +static SFORMAT StateRegs[] = +{ + { &vrc7idx, 1, "VRCI" }, + { preg, 3, "PREG" }, + { creg, 8, "CREG" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQd, 1, "IRQD" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQCount, 4, "IRQC" }, + { &CycleCount, 4, "CYCC" }, + { (void**)VRC7Sound_saveptr, sizeof(*VRC7Sound) | FCEUSTATE_INDIRECT, "VRC7" }, + {0} +}; + +// VRC7 Sound + +void DoVRC7Sound(void) { + int32 z, a; + if (FSettings.soundq >= 1) + return; + z = ((SOUNDTS << 16) / soundtsinc) >> 4; + a = z - dwave; + OPLL_fillbuf(VRC7Sound, &Wave[dwave], a, 1); + dwave += a; +} + +void UpdateOPLNEO(int32 *Wave, int Count) { + OPLL_fillbuf(VRC7Sound, Wave, Count, 4); +} + +void UpdateOPL(int Count) { + int32 z, a; + z = ((SOUNDTS << 16) / soundtsinc) >> 4; + a = z - dwave; + if (VRC7Sound && a) + OPLL_fillbuf(VRC7Sound, &Wave[dwave], a, 1); + dwave = 0; +} + +static void VRC7SC(void) { + if (VRC7Sound) + OPLL_set_rate(VRC7Sound, FSettings.SndRate); +} + +static void VRC7SKill(void) { + if (VRC7Sound) + OPLL_delete(VRC7Sound); + VRC7Sound = NULL; +} + +static void VRC7_ESI(void) { + GameExpSound.RChange = VRC7SC; + GameExpSound.Kill = VRC7SKill; + VRC7Sound = OPLL_new(3579545, FSettings.SndRate ? FSettings.SndRate : 48000); + OPLL_reset(VRC7Sound); + OPLL_reset(VRC7Sound); +} + +// VRC7 Sound + +static void Sync(void) { + uint8 i; + setprg8r(0x10, 0x6000, 0); + setprg8(0x8000, preg[0]); + setprg8(0xA000, preg[1]); + setprg8(0xC000, preg[2]); + setprg8(0xE000, ~0); + for (i = 0; i < 8; i++) + setchr1(i << 10, creg[i]); + switch (mirr & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(VRC7SW) { + if (FSettings.SndRate) { + OPLL_writeReg(VRC7Sound, vrc7idx, V); + GameExpSound.Fill = UpdateOPL; + GameExpSound.NeoFill = UpdateOPLNEO; + } +} + +static DECLFW(VRC7Write) { + A |= (A & 8) << 1; // another two-in-oooone + if (A >= 0xA000 && A <= 0xDFFF) { + A &= 0xF010; + creg[((A >> 4) & 1) | ((A - 0xA000) >> 11)] = V; + Sync(); + } else if (A == 0x9030) { + VRC7SW(A, V); + } else switch (A & 0xF010) { + case 0x8000: preg[0] = V; Sync(); break; + case 0x8010: preg[1] = V; Sync(); break; + case 0x9000: preg[2] = V; Sync(); break; + case 0x9010: vrc7idx = V; break; + case 0xE000: mirr = V & 3; Sync(); break; + case 0xE010: IRQLatch = V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xF000: + IRQa = V & 2; + IRQd = V & 1; + if (V & 2) + IRQCount = IRQLatch; + CycleCount = 0; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xF010: + IRQa = IRQd; + X6502_IRQEnd(FCEU_IQEXT); + break; + } +} + +static void VRC7Power(void) { + Sync(); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, VRC7Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void VRC7Close(void) +{ + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void VRC7IRQHook(int a) { + if (IRQa) { + CycleCount += a * 3; + while(CycleCount >= 341) { + CycleCount -= 341; + IRQCount++; + if (IRQCount == 0x100) { + IRQCount = IRQLatch; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper85_Init(CartInfo *info) { + info->Power = VRC7Power; + info->Close = VRC7Close; + MapIRQHook = VRC7IRQHook; + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + if (info->battery) { + info->SaveGame[0] = WRAM; + info->SaveGameLen[0] = WRAMSIZE; + } + GameStateRestore = StateRestore; + VRC7_ESI(); + AddExState(&StateRegs, ~0, 0, 0); +} + +void NSFVRC7_Init(void) { + SetWriteHandler(0x9010, 0x901F, VRC7Write); + SetWriteHandler(0x9030, 0x903F, VRC7Write); + VRC7_ESI(); +} diff --git a/apps/fceux/src/boards/vrc7p.cpp b/apps/fceux/src/boards/vrc7p.cpp new file mode 100644 index 00000000..5a886cb0 --- /dev/null +++ b/apps/fceux/src/boards/vrc7p.cpp @@ -0,0 +1,120 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2009 CaH4e3 + * + * 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 + * + * YOKO Mortal Kombat V Pro, VRC7 pirate clone + */ + +#include "mapinc.h" + +static uint8 prg[3], chr[8], mirr; +static uint8 IRQLatch, IRQa, IRQd; +static int32 IRQCount, CycleCount; + +static SFORMAT StateRegs[] = +{ + { prg, 3, "PRG" }, + { chr, 8, "CHR" }, + { &mirr, 1, "MIRR" }, + { &IRQa, 1, "IRQA" }, + { &IRQd, 1, "IRQD" }, + { &IRQLatch, 1, "IRQL" }, + { &IRQCount, 4, "IRQC" }, + { &CycleCount, 4, "CYCC" }, + { 0 } +}; + +static void Sync(void) { + uint8 i; + setprg8(0x8000, prg[0]); + setprg8(0xa000, prg[1]); + setprg8(0xc000, prg[2]); + setprg8(0xe000, ~0); + for (i = 0; i < 8; i++) + setchr1(i << 10, chr[i]); + switch (mirr & 3) { + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } +} + +static DECLFW(UNLVRC7Write) { + switch (A & 0xF008) { + case 0x8000: prg[0] = V; Sync(); break; + case 0x8008: prg[1] = V; Sync(); break; + case 0x9000: prg[2] = V; Sync(); break; + case 0xa000: chr[0] = V; Sync(); break; + case 0xa008: chr[1] = V; Sync(); break; + case 0xb000: chr[2] = V; Sync(); break; + case 0xb008: chr[3] = V; Sync(); break; + case 0xc000: chr[4] = V; Sync(); break; + case 0xc008: chr[5] = V; Sync(); break; + case 0xd000: chr[6] = V; Sync(); break; + case 0xd008: chr[7] = V; Sync(); break; + case 0xe000: mirr = V; Sync(); break; + case 0xe008: IRQLatch = V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0xf000: + IRQa = V & 2; + IRQd = V & 1; + if (V & 2) + IRQCount = IRQLatch; + CycleCount = 0; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xf008: + if (IRQd) + IRQa = 1; + else + IRQa = 0; + X6502_IRQEnd(FCEU_IQEXT); + break; + } +} + +static void UNLVRC7Power(void) { + Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLVRC7Write); +} + +static void UNLVRC7IRQHook(int a) { + if (IRQa) { + CycleCount += a * 3; + while (CycleCount >= 341) { + CycleCount -= 341; + IRQCount++; + if (IRQCount == 248) { + IRQCount = IRQLatch; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } +} + +static void StateRestore(int version) { + Sync(); +} + +void UNLVRC7_Init(CartInfo *info) { + info->Power = UNLVRC7Power; + MapIRQHook = UNLVRC7IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/boards/yoko.cpp b/apps/fceux/src/boards/yoko.cpp new file mode 100644 index 00000000..f1905487 --- /dev/null +++ b/apps/fceux/src/boards/yoko.cpp @@ -0,0 +1,237 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2006 CaH4e3 + * + * 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 + * + * YOKO mapper, almost the same as 83, TODO: figure out difference + * Mapper 83 - 30-in-1 mapper, two modes for single game carts, one mode for + * multigame Dragon Ball Z Party + * + * Mortal Kombat 2 YOKO + * N-CXX(M), XX - PRG+CHR, 12 - 128+256, 22 - 256+256, 14 - 128+512 + * + */ + +#include "mapinc.h" + +static uint8 mode, bank, reg[11], low[4], dip, IRQa; +static int32 IRQCount; +static uint8 *WRAM = NULL; +static uint32 WRAMSIZE; + +static uint8 is2kbank, isnot2kbank; + +static SFORMAT StateRegs[] = +{ + { &mode, 1, "MODE" }, + { &bank, 1, "BANK" }, + { &IRQCount, 4, "IRQC" }, + { &IRQa, 1, "IRQA" }, + { reg, 11, "REGS" }, + { low, 4, "LOWR" }, + { &is2kbank, 1, "IS2K" }, + { &isnot2kbank, 1, "NT2K" }, + { 0 } +}; + +static void UNLYOKOSync(void) { + setmirror((mode & 1) ^ 1); + setchr2(0x0000, reg[3]); + setchr2(0x0800, reg[4]); + setchr2(0x1000, reg[5]); + setchr2(0x1800, reg[6]); + if (mode & 0x10) { + uint32 base = (bank & 8) << 1; + setprg8(0x8000, (reg[0] & 0x0f) | base); + setprg8(0xA000, (reg[1] & 0x0f) | base); + setprg8(0xC000, (reg[2] & 0x0f) | base); + setprg8(0xE000, 0x0f | base); + } else { + if (mode & 8) + setprg32(0x8000, bank >> 1); + else{ + setprg16(0x8000, bank); + setprg16(0xC000, ~0); + } + } +} + +static void M83Sync(void) { + switch (mode & 3) { // check if it is truth + case 0: setmirror(MI_V); break; + case 1: setmirror(MI_H); break; + case 2: setmirror(MI_0); break; + case 3: setmirror(MI_1); break; + } + if (is2kbank && !isnot2kbank) { + setchr2(0x0000, reg[0]); + setchr2(0x0800, reg[1]); + setchr2(0x1000, reg[6]); + setchr2(0x1800, reg[7]); + } else { + int x; + for (x = 0; x < 8; x++) + setchr1(x << 10, reg[x] | ((bank & 0x30) << 4)); + } + setprg8r(0x10, 0x6000, 0); + if (mode & 0x40) { + setprg16(0x8000, (bank & 0x3F)); // DBZ Party [p1] + setprg16(0xC000, (bank & 0x30) | 0xF); + } else { + setprg8(0x8000, reg[8]); + setprg8(0xA000, reg[9]); + setprg8(0xC000, reg[10]); + setprg8(0xE000, ~0); + } +} + +static DECLFW(UNLYOKOWrite) { + switch (A & 0x8C17) { + case 0x8000: bank = V; UNLYOKOSync(); break; + case 0x8400: mode = V; UNLYOKOSync(); break; + case 0x8800: IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x8801: IRQa = mode & 0x80; IRQCount &= 0xFF; IRQCount |= V << 8; break; + case 0x8c00: reg[0] = V; UNLYOKOSync(); break; + case 0x8c01: reg[1] = V; UNLYOKOSync(); break; + case 0x8c02: reg[2] = V; UNLYOKOSync(); break; + case 0x8c10: reg[3] = V; UNLYOKOSync(); break; + case 0x8c11: reg[4] = V; UNLYOKOSync(); break; + case 0x8c16: reg[5] = V; UNLYOKOSync(); break; + case 0x8c17: reg[6] = V; UNLYOKOSync(); break; + } +} + +static DECLFW(M83Write) { + switch (A) { + case 0x8000: is2kbank = 1; + case 0xB000: // Dragon Ball Z Party [p1] BMC + case 0xB0FF: // Dragon Ball Z Party [p1] BMC + case 0xB1FF: bank = V; mode |= 0x40; M83Sync(); break; // Dragon Ball Z Party [p1] BMC + case 0x8100: mode = V | (mode & 0x40); M83Sync(); break; + case 0x8200: IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break; + case 0x8201: IRQa = mode & 0x80; IRQCount &= 0xFF; IRQCount |= V << 8; break; + case 0x8300: reg[8] = V; mode &= 0xBF; M83Sync(); break; + case 0x8301: reg[9] = V; mode &= 0xBF; M83Sync(); break; + case 0x8302: reg[10] = V; mode &= 0xBF; M83Sync(); break; + case 0x8310: reg[0] = V; M83Sync(); break; + case 0x8311: reg[1] = V; M83Sync(); break; + case 0x8312: reg[2] = V; isnot2kbank = 1; M83Sync(); break; + case 0x8313: reg[3] = V; isnot2kbank = 1; M83Sync(); break; + case 0x8314: reg[4] = V; isnot2kbank = 1; M83Sync(); break; + case 0x8315: reg[5] = V; isnot2kbank = 1; M83Sync(); break; + case 0x8316: reg[6] = V; M83Sync(); break; + case 0x8317: reg[7] = V; M83Sync(); break; + } +} + +static DECLFR(UNLYOKOReadDip) { + return (X.DB & 0xFC) | dip; +} + +static DECLFR(UNLYOKOReadLow) { + return low[A & 3]; +} + +static DECLFW(UNLYOKOWriteLow) { + low[A & 3] = V; +} + +static void UNLYOKOPower(void) { + mode = bank = 0; + dip = 3; + UNLYOKOSync(); + SetReadHandler(0x5000, 0x53FF, UNLYOKOReadDip); + SetReadHandler(0x5400, 0x5FFF, UNLYOKOReadLow); + SetWriteHandler(0x5400, 0x5FFF, UNLYOKOWriteLow); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x8000, 0xFFFF, UNLYOKOWrite); +} + +static void M83Power(void) { + is2kbank = 0; + isnot2kbank = 0; + mode = bank = 0; + dip = 0; + M83Sync(); + SetReadHandler(0x5000, 0x5000, UNLYOKOReadDip); + SetReadHandler(0x5100, 0x5103, UNLYOKOReadLow); + SetWriteHandler(0x5100, 0x5103, UNLYOKOWriteLow); + SetReadHandler(0x6000, 0x7fff, CartBR); + SetWriteHandler(0x6000, 0x7fff, CartBW); // Pirate Dragon Ball Z Party [p1] used if for saves instead of seraial EEPROM + SetReadHandler(0x8000, 0xffff, CartBR); + SetWriteHandler(0x8000, 0xffff, M83Write); + FCEU_CheatAddRAM(WRAMSIZE >> 10, 0x6000, WRAM); +} + +static void UNLYOKOReset(void) { + dip = (dip + 1) & 3; + mode = bank = 0; + UNLYOKOSync(); +} + +static void M83Reset(void) { + dip ^= 1; + M83Sync(); +} + +static void M83Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + WRAM = NULL; +} + +static void UNLYOKOIRQHook(int a) { + if (IRQa) { + IRQCount -= a; + if (IRQCount < 0) { + X6502_IRQBegin(FCEU_IQEXT); + IRQa = 0; + IRQCount = 0xFFFF; + } + } +} + +static void UNLYOKOStateRestore(int version) { + UNLYOKOSync(); +} + +static void M83StateRestore(int version) { + M83Sync(); +} + +void UNLYOKO_Init(CartInfo *info) { + info->Power = UNLYOKOPower; + info->Reset = UNLYOKOReset; + MapIRQHook = UNLYOKOIRQHook; + GameStateRestore = UNLYOKOStateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper83_Init(CartInfo *info) { + info->Power = M83Power; + info->Reset = M83Reset; + info->Close = M83Close; + MapIRQHook = UNLYOKOIRQHook; + GameStateRestore = M83StateRestore; + + WRAMSIZE = 8192; + WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); + SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); + AddExState(WRAM, WRAMSIZE, 0, "WRAM"); + + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/apps/fceux/src/cart.cpp b/apps/fceux/src/cart.cpp new file mode 100644 index 00000000..4646eabd --- /dev/null +++ b/apps/fceux/src/cart.cpp @@ -0,0 +1,575 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +/// \file +/// \brief This file contains all code for coordinating the mapping in of the address space external to the NES. + +#include "types.h" +#include "fceu.h" +#include "ppu.h" +#include "driver.h" + +#include "cart.h" +#include "x6502.h" + +#include "file.h" +#include "utils/memory.h" + + +#include +#include +#include +#include + +uint8 *Page[32], *VPage[8]; +uint8 **VPageR = VPage; +uint8 *VPageG[8]; +uint8 *MMC5SPRVPage[8]; +uint8 *MMC5BGVPage[8]; + +static uint8 PRGIsRAM[32]; /* This page is/is not PRG RAM. */ + +/* 16 are (sort of) reserved for UNIF/iNES and 16 to map other stuff. */ +uint8 CHRram[32]; +uint8 PRGram[32]; + +uint8 *PRGptr[32]; +uint8 *CHRptr[32]; + +uint32 PRGsize[32]; +uint32 CHRsize[32]; + +uint32 PRGmask2[32]; +uint32 PRGmask4[32]; +uint32 PRGmask8[32]; +uint32 PRGmask16[32]; +uint32 PRGmask32[32]; + +uint32 CHRmask1[32]; +uint32 CHRmask2[32]; +uint32 CHRmask4[32]; +uint32 CHRmask8[32]; + +int geniestage = 0; + +int modcon; + +uint8 genieval[3]; +uint8 geniech[3]; + +uint32 genieaddr[3]; + +CartInfo *currCartInfo; + +static INLINE void setpageptr(int s, uint32 A, uint8 *p, int ram) { + uint32 AB = A >> 11; + int x; + + if (p) + for (x = (s >> 1) - 1; x >= 0; x--) { + PRGIsRAM[AB + x] = ram; + Page[AB + x] = p - A; + } + else + for (x = (s >> 1) - 1; x >= 0; x--) { + PRGIsRAM[AB + x] = 0; + Page[AB + x] = 0; + } +} + +static uint8 nothing[8192]; +void ResetCartMapping(void) { + int x; + + PPU_ResetHooks(); + + for (x = 0; x < 32; x++) { + Page[x] = nothing - x * 2048; + PRGptr[x] = CHRptr[x] = 0; + PRGsize[x] = CHRsize[x] = 0; + } + for (x = 0; x < 8; x++) { + MMC5SPRVPage[x] = MMC5BGVPage[x] = VPageR[x] = nothing - 0x400 * x; + } +} + +void SetupCartPRGMapping(int chip, uint8 *p, uint32 size, int ram) { + PRGptr[chip] = p; + PRGsize[chip] = size; + + PRGmask2[chip] = (size >> 11) - 1; + PRGmask4[chip] = (size >> 12) - 1; + PRGmask8[chip] = (size >> 13) - 1; + PRGmask16[chip] = (size >> 14) - 1; + PRGmask32[chip] = (size >> 15) - 1; + + PRGram[chip] = ram ? 1 : 0; +} + +void SetupCartCHRMapping(int chip, uint8 *p, uint32 size, int ram) { + CHRptr[chip] = p; + CHRsize[chip] = size; + + CHRmask1[chip] = (size >> 10) - 1; + CHRmask2[chip] = (size >> 11) - 1; + CHRmask4[chip] = (size >> 12) - 1; + CHRmask8[chip] = (size >> 13) - 1; + + CHRram[chip] = ram; +} + +DECLFR(CartBR) { + return Page[A >> 11][A]; +} + +DECLFW(CartBW) { + //printf("Ok: %04x:%02x, %d\n",A,V,PRGIsRAM[A>>11]); + if (PRGIsRAM[A >> 11] && Page[A >> 11]) + Page[A >> 11][A] = V; +} + +DECLFR(CartBROB) { + if (!Page[A >> 11]) + return(X.DB); + else + return Page[A >> 11][A]; +} + +void setprg2r(int r, uint32 A, uint32 V) { + V &= PRGmask2[r]; + setpageptr(2, A, PRGptr[r] ? (&PRGptr[r][V << 11]) : 0, PRGram[r]); +} + +void setprg2(uint32 A, uint32 V) { + setprg2r(0, A, V); +} + +void setprg4r(int r, uint32 A, uint32 V) { + V &= PRGmask4[r]; + setpageptr(4, A, PRGptr[r] ? (&PRGptr[r][V << 12]) : 0, PRGram[r]); +} + +void setprg4(uint32 A, uint32 V) { + setprg4r(0, A, V); +} + +void setprg8r(int r, uint32 A, uint32 V) { + if (PRGsize[r] >= 8192) { + V &= PRGmask8[r]; + setpageptr(8, A, PRGptr[r] ? (&PRGptr[r][V << 13]) : 0, PRGram[r]); + } else { + uint32 VA = V << 2; + int x; + for (x = 0; x < 4; x++) + setpageptr(2, A + (x << 11), PRGptr[r] ? (&PRGptr[r][((VA + x) & PRGmask2[r]) << 11]) : 0, PRGram[r]); + } +} + +void setprg8(uint32 A, uint32 V) { + setprg8r(0, A, V); +} + +void setprg16r(int r, uint32 A, uint32 V) { + if (PRGsize[r] >= 16384) { + V &= PRGmask16[r]; + setpageptr(16, A, PRGptr[r] ? (&PRGptr[r][V << 14]) : 0, PRGram[r]); + } else { + uint32 VA = V << 3; + int x; + + for (x = 0; x < 8; x++) + setpageptr(2, A + (x << 11), PRGptr[r] ? (&PRGptr[r][((VA + x) & PRGmask2[r]) << 11]) : 0, PRGram[r]); + } +} + +void setprg16(uint32 A, uint32 V) { + setprg16r(0, A, V); +} + +void setprg32r(int r, uint32 A, uint32 V) { + if (PRGsize[r] >= 32768) { + V &= PRGmask32[r]; + setpageptr(32, A, PRGptr[r] ? (&PRGptr[r][V << 15]) : 0, PRGram[r]); + } else { + uint32 VA = V << 4; + int x; + + for (x = 0; x < 16; x++) + setpageptr(2, A + (x << 11), PRGptr[r] ? (&PRGptr[r][((VA + x) & PRGmask2[r]) << 11]) : 0, PRGram[r]); + } +} + +void setprg32(uint32 A, uint32 V) { + setprg32r(0, A, V); +} + +void setchr1r(int r, uint32 A, uint32 V) { + if (!CHRptr[r]) return; + FCEUPPU_LineUpdate(); + V &= CHRmask1[r]; + if (CHRram[r]) + PPUCHRRAM |= (1 << (A >> 10)); + else + PPUCHRRAM &= ~(1 << (A >> 10)); + VPageR[(A) >> 10] = &CHRptr[r][(V) << 10] - (A); +} + +void setchr2r(int r, uint32 A, uint32 V) { + if (!CHRptr[r]) return; + FCEUPPU_LineUpdate(); + V &= CHRmask2[r]; + VPageR[(A) >> 10] = VPageR[((A) >> 10) + 1] = &CHRptr[r][(V) << 11] - (A); + if (CHRram[r]) + PPUCHRRAM |= (3 << (A >> 10)); + else + PPUCHRRAM &= ~(3 << (A >> 10)); +} + +void setchr4r(int r, unsigned int A, unsigned int V) { + if (!CHRptr[r]) return; + FCEUPPU_LineUpdate(); + V &= CHRmask4[r]; + VPageR[(A) >> 10] = VPageR[((A) >> 10) + 1] = + VPageR[((A) >> 10) + 2] = VPageR[((A) >> 10) + 3] = &CHRptr[r][(V) << 12] - (A); + if (CHRram[r]) + PPUCHRRAM |= (15 << (A >> 10)); + else + PPUCHRRAM &= ~(15 << (A >> 10)); +} + +void setchr8r(int r, uint32 V) { + int x; + + if (!CHRptr[r]) return; + FCEUPPU_LineUpdate(); + V &= CHRmask8[r]; + for (x = 7; x >= 0; x--) + VPageR[x] = &CHRptr[r][V << 13]; + if (CHRram[r]) + PPUCHRRAM |= (255); + else + PPUCHRRAM = 0; +} + +void setchr1(uint32 A, uint32 V) { + setchr1r(0, A, V); +} + +void setchr2(uint32 A, uint32 V) { + setchr2r(0, A, V); +} + +void setchr4(uint32 A, uint32 V) { + setchr4r(0, A, V); +} + +void setchr8(uint32 V) { + setchr8r(0, V); +} + +/* This function can be called without calling SetupCartMirroring(). */ + +void setntamem(uint8 *p, int ram, uint32 b) { + FCEUPPU_LineUpdate(); + vnapage[b] = p; + PPUNTARAM &= ~(1 << b); + if (ram) + PPUNTARAM |= 1 << b; +} + +static int mirrorhard = 0; +void setmirrorw(int a, int b, int c, int d) { + FCEUPPU_LineUpdate(); + vnapage[0] = NTARAM + a * 0x400; + vnapage[1] = NTARAM + b * 0x400; + vnapage[2] = NTARAM + c * 0x400; + vnapage[3] = NTARAM + d * 0x400; +} + +void setmirror(int t) { + FCEUPPU_LineUpdate(); + if (!mirrorhard) { + switch (t) { + case MI_H: + vnapage[0] = vnapage[1] = NTARAM; vnapage[2] = vnapage[3] = NTARAM + 0x400; + break; + case MI_V: + vnapage[0] = vnapage[2] = NTARAM; vnapage[1] = vnapage[3] = NTARAM + 0x400; + break; + case MI_0: + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = NTARAM; + break; + case MI_1: + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = NTARAM + 0x400; + break; + } + PPUNTARAM = 0xF; + } +} + +void SetupCartMirroring(int m, int hard, uint8 *extra) { + if (m < 4) { + mirrorhard = 0; + setmirror(m); + } else { + vnapage[0] = NTARAM; + vnapage[1] = NTARAM + 0x400; + vnapage[2] = extra; + vnapage[3] = extra + 0x400; + PPUNTARAM = 0xF; + } + mirrorhard = hard; +} + +static uint8 *GENIEROM = 0; + +void FixGenieMap(void); + +// Called when a game(file) is opened successfully. Returns TRUE on error. +bool FCEU_OpenGenie(void) +{ + FILE *fp; + int x; + + if (!GENIEROM) + { + char *fn; + + if (!(GENIEROM = (uint8*)FCEU_malloc(4096 + 1024))) + return true; + + fn = strdup(FCEU_MakeFName(FCEUMKF_GGROM, 0, 0).c_str()); + fp = FCEUD_UTF8fopen(fn, "rb"); + if (!fp) + { + FCEU_PrintError("Error opening Game Genie ROM image!\nIt should be named \"gg.rom\"!"); + free(GENIEROM); + GENIEROM = 0; + return true; + } + if (fread(GENIEROM, 1, 16, fp) != 16) + { + grerr: + FCEU_PrintError("Error reading from Game Genie ROM image!"); + free(GENIEROM); + GENIEROM = 0; + fclose(fp); + return true; + } + if (GENIEROM[0] == 0x4E) + { + /* iNES ROM image */ + if (fread(GENIEROM, 1, 4096, fp) != 4096) + goto grerr; + if (fseek(fp, 16384 - 4096, SEEK_CUR)) + goto grerr; + if (fread(GENIEROM + 4096, 1, 256, fp) != 256) + goto grerr; + } else + { + if (fread(GENIEROM + 16, 1, 4352 - 16, fp) != (4352 - 16)) + goto grerr; + } + fclose(fp); + + /* Workaround for the FCE Ultra CHR page size only being 1KB */ + for (x = 0; x < 4; x++) + { + memcpy(GENIEROM + 4096 + (x << 8), GENIEROM + 4096, 256); + } + } + + geniestage = 1; + return false; +} + +/* Called when a game is closed. */ +void FCEU_CloseGenie(void) { + /* No good reason to free() the Game Genie ROM image data. */ + geniestage = 0; + FlushGenieRW(); + VPageR = VPage; +} + +void FCEU_KillGenie(void) { + if (GENIEROM) { + free(GENIEROM); + GENIEROM = 0; + } +} + +static DECLFR(GenieRead) { + return GENIEROM[A & 4095]; +} + +static DECLFW(GenieWrite) { + switch (A) { + case 0x800c: + case 0x8008: + case 0x8004: genieval[((A - 4) & 0xF) >> 2] = V; break; + + case 0x800b: + case 0x8007: + case 0x8003: geniech[((A - 3) & 0xF) >> 2] = V; break; + + case 0x800a: + case 0x8006: + case 0x8002: genieaddr[((A - 2) & 0xF) >> 2] &= 0xFF00; genieaddr[((A - 2) & 0xF) >> 2] |= V; break; + + case 0x8009: + case 0x8005: + case 0x8001: genieaddr[((A - 1) & 0xF) >> 2] &= 0xFF; genieaddr[((A - 1) & 0xF) >> 2] |= (V | 0x80) << 8; break; + + case 0x8000: + if (!V) + FixGenieMap(); + else { + modcon = V ^ 0xFF; + if (V == 0x71) + modcon = 0; + } + break; + } +} + +static readfunc GenieBackup[3]; + +static DECLFR(GenieFix1) { + uint8 r = GenieBackup[0](A); + + if ((modcon >> 1) & 1) // No check + return genieval[0]; + else if (r == geniech[0]) + return genieval[0]; + + return r; +} + +static DECLFR(GenieFix2) { + uint8 r = GenieBackup[1](A); + + if ((modcon >> 2) & 1) // No check + return genieval[1]; + else if (r == geniech[1]) + return genieval[1]; + + return r; +} + +static DECLFR(GenieFix3) { + uint8 r = GenieBackup[2](A); + + if ((modcon >> 3) & 1) // No check + return genieval[2]; + else if (r == geniech[2]) + return genieval[2]; + + return r; +} + + +void FixGenieMap(void) { + int x; + + geniestage = 2; + + for (x = 0; x < 8; x++) + VPage[x] = VPageG[x]; + + VPageR = VPage; + FlushGenieRW(); + //printf("Rightyo\n"); + for (x = 0; x < 3; x++) + if ((modcon >> (4 + x)) & 1) { + readfunc tmp[3] = { GenieFix1, GenieFix2, GenieFix3 }; + GenieBackup[x] = GetReadHandler(genieaddr[x]); + SetReadHandler(genieaddr[x], genieaddr[x], tmp[x]); + } +} + +void FCEU_GeniePower(void) { + uint32 x; + + if (!geniestage) + return; + + geniestage = 1; + for (x = 0; x < 3; x++) { + genieval[x] = 0xFF; + geniech[x] = 0xFF; + genieaddr[x] = 0xFFFF; + } + modcon = 0; + + SetWriteHandler(0x8000, 0xFFFF, GenieWrite); + SetReadHandler(0x8000, 0xFFFF, GenieRead); + + for (x = 0; x < 8; x++) + VPage[x] = GENIEROM + 4096 - 0x400 * x; + + if (AllocGenieRW()) + VPageR = VPageG; + else + geniestage = 2; +} + + +void FCEU_SaveGameSave(CartInfo *LocalHWInfo) { + if (LocalHWInfo->battery && LocalHWInfo->SaveGame[0]) { + FILE *sp; + + std::string soot = FCEU_MakeFName(FCEUMKF_SAV, 0, "sav"); + if ((sp = FCEUD_UTF8fopen(soot, "wb")) == NULL) { + FCEU_PrintError("WRAM file \"%s\" cannot be written to.\n", soot.c_str()); + } else { + for (int x = 0; x < 4; x++) + if (LocalHWInfo->SaveGame[x]) { + fwrite(LocalHWInfo->SaveGame[x], 1, + LocalHWInfo->SaveGameLen[x], sp); + } + } + } +} + +// hack, movie.cpp has to communicate with this function somehow +int disableBatteryLoading = 0; + +void FCEU_LoadGameSave(CartInfo *LocalHWInfo) { + if (LocalHWInfo->battery && LocalHWInfo->SaveGame[0] && !disableBatteryLoading) { + FILE *sp; + + std::string soot = FCEU_MakeFName(FCEUMKF_SAV, 0, "sav"); + sp = FCEUD_UTF8fopen(soot, "rb"); + if (sp != NULL) { + for (int x = 0; x < 4; x++) + if (LocalHWInfo->SaveGame[x]) + fread(LocalHWInfo->SaveGame[x], 1, LocalHWInfo->SaveGameLen[x], sp); + } + } +} + +//clears all save memory. call this if you want to pretend the saveram has been reset (it doesnt touch what is on disk though) +void FCEU_ClearGameSave(CartInfo *LocalHWInfo) { + if (LocalHWInfo->battery && LocalHWInfo->SaveGame[0]) { + for (int x = 0; x < 4; x++) + if (LocalHWInfo->SaveGame[x]) + memset(LocalHWInfo->SaveGame[x], 0, LocalHWInfo->SaveGameLen[x]); + } +} diff --git a/apps/fceux/src/cart.h b/apps/fceux/src/cart.h new file mode 100644 index 00000000..e78c87ea --- /dev/null +++ b/apps/fceux/src/cart.h @@ -0,0 +1,107 @@ +#ifndef CART_H +#define CART_H + +typedef struct { + // Set by mapper/board code: + void (*Power)(void); + void (*Reset)(void); + void (*Close)(void); + uint8 *SaveGame[4]; // Pointers to memory to save/load. + uint32 SaveGameLen[4]; // How much memory to save/load. + + // Set by iNES/UNIF loading code. + int mirror; // As set in the header or chunk. + // iNES/UNIF specific. Intended + // to help support games like "Karnov" + // that are not really MMC3 but are + // set to mapper 4. + int battery; // Presence of an actual battery. + int ines2; + int submapper; // Submappers as defined by NES 2.0 + int wram_size; + int battery_wram_size; + int vram_size; + int battery_vram_size; + uint8 MD5[16]; + uint32 CRC32; // Should be set by the iNES/UNIF loading + // code, used by mapper/board code, maybe + // other code in the future. +} CartInfo; + +extern CartInfo *currCartInfo; + +void FCEU_SaveGameSave(CartInfo *LocalHWInfo); +void FCEU_LoadGameSave(CartInfo *LocalHWInfo); +void FCEU_ClearGameSave(CartInfo *LocalHWInfo); + +extern uint8 *Page[32], *VPage[8], *MMC5SPRVPage[8], *MMC5BGVPage[8]; + +void ResetCartMapping(void); +void SetupCartPRGMapping(int chip, uint8 *p, uint32 size, int ram); +void SetupCartCHRMapping(int chip, uint8 *p, uint32 size, int ram); +void SetupCartMirroring(int m, int hard, uint8 *extra); + +DECLFR(CartBROB); +DECLFR(CartBR); +DECLFW(CartBW); + +extern uint8 PRGram[32]; +extern uint8 CHRram[32]; + +extern uint8 *PRGptr[32]; +extern uint8 *CHRptr[32]; + +extern uint32 PRGsize[32]; +extern uint32 CHRsize[32]; + +extern uint32 PRGmask2[32]; +extern uint32 PRGmask4[32]; +extern uint32 PRGmask8[32]; +extern uint32 PRGmask16[32]; +extern uint32 PRGmask32[32]; + +extern uint32 CHRmask1[32]; +extern uint32 CHRmask2[32]; +extern uint32 CHRmask4[32]; +extern uint32 CHRmask8[32]; + +void setprg2(uint32 A, uint32 V); +void setprg4(uint32 A, uint32 V); +void setprg8(uint32 A, uint32 V); +void setprg16(uint32 A, uint32 V); +void setprg32(uint32 A, uint32 V); + +void setprg2r(int r, unsigned int A, unsigned int V); +void setprg4r(int r, unsigned int A, unsigned int V); +void setprg8r(int r, unsigned int A, unsigned int V); +void setprg16r(int r, unsigned int A, unsigned int V); +void setprg32r(int r, unsigned int A, unsigned int V); + +void setchr1r(int r, unsigned int A, unsigned int V); +void setchr2r(int r, unsigned int A, unsigned int V); +void setchr4r(int r, unsigned int A, unsigned int V); +void setchr8r(int r, unsigned int V); + +void setchr1(unsigned int A, unsigned int V); +void setchr2(unsigned int A, unsigned int V); +void setchr4(unsigned int A, unsigned int V); +void setchr8(unsigned int V); + +void setmirror(int t); +void setmirrorw(int a, int b, int c, int d); +void setntamem(uint8 *p, int ram, uint32 b); + +#define MI_H 0 +#define MI_V 1 +#define MI_0 2 +#define MI_1 3 + +extern int geniestage; + +void FCEU_GeniePower(void); + +bool FCEU_OpenGenie(void); +void FCEU_CloseGenie(void); +void FCEU_KillGenie(void); + +#endif diff --git a/apps/fceux/src/cheat.cpp b/apps/fceux/src/cheat.cpp new file mode 100644 index 00000000..1a7c1dbc --- /dev/null +++ b/apps/fceux/src/cheat.cpp @@ -0,0 +1,904 @@ +/* FCE Ultra - NES/Famicom Emulator +* +* Copyright notice for this file: +* Copyright (C) 2002 Xodnizel +* +* 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 +*/ + + +#include "types.h" +#include "x6502.h" +#include "cheat.h" +#include "fceu.h" +#include "file.h" +#include "cart.h" +#include "driver.h" +#include "utils/memory.h" + +#include +#include +#include +#include +#include + +using namespace std; + +static uint8 *CheatRPtrs[64]; + +vector FrozenAddresses; //List of addresses that are currently frozen +void UpdateFrozenList(void); //Function that populates the list of frozen addresses +unsigned int FrozenAddressCount = 0; //Keeps up with the Frozen address count, necessary for using in other dialogs (such as hex editor) + +void FCEU_CheatResetRAM(void) +{ + int x; + + for(x=0;x<64;x++) + CheatRPtrs[x]=0; +} + +void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p) +{ + uint32 AB=A>>10; + int x; + + for(x=s-1;x>=0;x--) + CheatRPtrs[AB+x]=p-A; +} + + +CHEATF_SUBFAST SubCheats[256] = { 0 }; +uint32 numsubcheats = 0; +int globalCheatDisabled = 0; +int disableAutoLSCheats = 0; +struct CHEATF *cheats = 0, *cheatsl = 0; + + +#define CHEATC_NONE 0x8000 +#define CHEATC_EXCLUDED 0x4000 +#define CHEATC_NOSHOW 0xC000 + +static uint16 *CheatComp = 0; +int savecheats = 0; + +static DECLFR(SubCheatsRead) +{ + CHEATF_SUBFAST *s = SubCheats; + int x=numsubcheats; + + do + { + if(s->addr==A) + { + if(s->compare>=0) + { + uint8 pv=s->PrevRead(A); + + if(pv==s->compare) + return(s->val); + else return(pv); + } + else return(s->val); + } + s++; + } while(--x); + return(0); /* We should never get here. */ +} + +void RebuildSubCheats(void) +{ + uint32 x; + struct CHEATF *c = cheats; + for(x = 0; x < numsubcheats; x++) + SetReadHandler(SubCheats[x].addr, SubCheats[x].addr, SubCheats[x].PrevRead); + + numsubcheats = 0; + if (!globalCheatDisabled) + while(c) + { + if(c->type == 1 && c->status && GetReadHandler(c->addr) != SubCheatsRead) + { + SubCheats[numsubcheats].PrevRead = GetReadHandler(c->addr); + SubCheats[numsubcheats].addr = c->addr; + SubCheats[numsubcheats].val = c->val; + SubCheats[numsubcheats].compare = c->compare; + SetReadHandler(c->addr, c->addr, SubCheatsRead); + numsubcheats++; + } + c = c->next; + } + + FrozenAddressCount = numsubcheats; //Update the frozen address list + UpdateFrozenList(); + +} + +void FCEU_PowerCheats() +{ + numsubcheats = 0; /* Quick hack to prevent setting of ancient read addresses. */ + RebuildSubCheats(); +} + +int FCEU_CalcCheatAffectedBytes(uint32 address, uint32 size) { + uint32 count = 0; + for (uint32 i = 0; i < numsubcheats && count < size; ++i) + if (SubCheats[i].addr >= address && SubCheats[i].addr < address + size) + ++count; + return count; +} + +static int AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int status, int type); +static void CheatMemErr(void) +{ + FCEUD_PrintError("Error allocating memory for cheat data."); +} + +static int AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int status, int type) +{ + struct CHEATF *temp; + if(!(temp = (struct CHEATF *)FCEU_dmalloc(sizeof(struct CHEATF)))) + { + CheatMemErr(); + return(0); + } + + temp->name = strcpy((char*) FCEU_dmalloc(strlen(name) + 1), name); + temp->addr = addr; + temp->val = val; + temp->status = status; + temp->compare = compare; + temp->type = type; + temp->next = 0; + + if(cheats) + { + cheatsl->next = temp; + cheatsl = temp; + } + else + cheats = cheatsl = temp; + + return (1); +} + +/* The "override_existing" parameter is used only in cheat dialog import. + Since the default behaviour will reset numsubcheats to 0 everytime, + In game loading, this is absolutely right, but when importing in cheat window, + resetting numsubcheats to 0 will override existed cheat items to make them + invalid. +*/ +void FCEU_LoadGameCheats(FILE *override, int override_existing) +{ + FILE *fp; + unsigned int addr; + unsigned int val; + unsigned int status; + unsigned int type; + unsigned int compare; + int x; + + char linebuf[2048] = { 0 }; + char namebuf[128] = { 0 }; + int tc = 0; + char *fn; + + savecheats = 0; + if (override_existing) + numsubcheats = 0; + + if(override) + fp = override; + else + { + fn = strdup(FCEU_MakeFName(FCEUMKF_CHEAT, 0, 0).c_str()); + fp = FCEUD_UTF8fopen(fn, "rb"); + free(fn); + if (!fp) { + return; + } + } + + while(fgets(linebuf, 2048, fp) != nullptr) + { + char *tbuf = linebuf; + int doc = 0; + + addr = val = compare = status = type = 0; + + if(tbuf[0] == 'S') + { + tbuf++; + type = 1; + } + else + type = 0; + + if(tbuf[0] == 'C') + { + tbuf++; + doc = 1; + } + + if(tbuf[0] == ':') + { + tbuf++; + status = 0; + } + else status = 1; + + if(doc) + { + char *neo = &tbuf[4+2+2+1+1+1]; + if(sscanf(tbuf, "%04x%*[:]%02x%*[:]%02x", &addr, &val, &compare) != 3) + continue; + strcpy(namebuf, neo); + } + else + { + char *neo = &tbuf[4+2+1+1]; + if(sscanf(tbuf, "%04x%*[:]%02x", &addr, &val) != 2) + continue; + strcpy(namebuf, neo); + } + + for(x = 0; x < (int)strlen(namebuf); x++) + { + if(namebuf[x] == 10 || namebuf[x] == 13) + { + namebuf[x] = 0; + break; + } + else if(namebuf[x] > 0x00 && namebuf[x] < 0x20) + namebuf[x] = 0x20; + } + + AddCheatEntry(namebuf, addr, val, doc ? compare : -1, status, type); + tc++; + } + + RebuildSubCheats(); + + FCEU_DispMessage("Cheats file loaded.", 0); //Tells user a cheats file was loaded. + + if(!override) + fclose(fp); +} + +void FCEU_SaveGameCheats(FILE* fp, int release) +{ + struct CHEATF *next = cheats; + while (next) + { + if (next->type) + fputc('S', fp); + if (next->compare >= 0) + fputc('C', fp); + + if (!next->status) + fputc(':', fp); + + if (next->compare >= 0) + fprintf(fp, "%04x:%02x:%02x:%s\n", next->addr, next->val, next->compare, next->name); + else + fprintf(fp, "%04x:%02x:%s\n", next->addr, next->val, next->name); + + if (release) free(next->name); + struct CHEATF *t = next; + next = next->next; + if (release) free(t); + } +} + +void FCEU_FlushGameCheats(FILE *override, int nosave) +{ + if(CheatComp) + { + free(CheatComp); + CheatComp=0; + } + if((!savecheats || nosave) && !override) /* Always save cheats if we're being overridden. */ + { + if(cheats) + { + struct CHEATF *next=cheats; + for(;;) + { + struct CHEATF *last=next; + next=next->next; + free(last->name); + free(last); + if(!next) break; + } + cheats=cheatsl=0; + } + } + else + { + char *fn = 0; + + if(!override) + fn = strdup(FCEU_MakeFName(FCEUMKF_CHEAT,0,0).c_str()); + + if(cheats) + { + FILE *fp; + + if(override) + fp = override; + else + fp=FCEUD_UTF8fopen(fn,"wb"); + + if(fp) + { + FCEU_SaveGameCheats(fp, 1); + if(!override) + fclose(fp); + } + else + FCEUD_PrintError("Error saving cheats."); + cheats=cheatsl=0; + } + else if(!override) + remove(fn); + if(!override) + free(fn); + } + + RebuildSubCheats(); /* Remove memory handlers. */ + +} + + +int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type) +{ + + if(!AddCheatEntry(name, addr, val, compare, 1, type)) + return 0; + savecheats = 1; + RebuildSubCheats(); + + return 1; +} + +int FCEUI_DelCheat(uint32 which) +{ + struct CHEATF *prev; + struct CHEATF *cur; + uint32 x=0; + + for(prev=0,cur=cheats;;) + { + if(x==which) // Remove this cheat. + { + if(prev) // Update pointer to this cheat. + { + if(cur->next) // More cheats. + prev->next=cur->next; + else // No more. + { + prev->next=0; + cheatsl=prev; // Set the previous cheat as the last cheat. + } + } + else // This is the first cheat. + { + if(cur->next) // More cheats + cheats=cur->next; + else + cheats=cheatsl=0; // No (more) cheats. + } + free(cur->name); // Now that all references to this cheat are removed, + free(cur); // free the memory. + break; + } // *END REMOVE THIS CHEAT* + + + if(!cur->next) // No more cheats to go through(this shouldn't ever happen...) + return(0); + prev=cur; + cur=prev->next; + x++; + } + + savecheats=1; + RebuildSubCheats(); + return(1); +} + +void FCEU_ApplyPeriodicCheats(void) +{ + struct CHEATF *cur=cheats; + if(!cur) return; + + for(;;) + { + if(cur->status && !(cur->type)) + if(CheatRPtrs[cur->addr>>10]) + CheatRPtrs[cur->addr>>10][cur->addr]=cur->val; + if(cur->next) + cur=cur->next; + else + break; + } +} + + +void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data) +{ + struct CHEATF *next=cheats; + + while(next) + { + if(!callb(next->name,next->addr,next->val,next->compare,next->status,next->type,data)) break; + next=next->next; + } +} + +int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *compare, int *s, int *type) +{ + struct CHEATF *next=cheats; + uint32 x=0; + + while(next) + { + if(x==which) + { + if(name) + *name=next->name; + if(a) + *a=next->addr; + if(v) + *v=next->val; + if(s) + *s=next->status; + if(compare) + *compare=next->compare; + if(type) + *type=next->type; + return(1); + } + next=next->next; + x++; + } + return(0); +} + +static int GGtobin(char c) +{ + static char lets[16]={'A','P','Z','L','G','I','T','Y','E','O','X','U','K','S','V','N'}; + int x; + + for(x=0;x<16;x++) + if(lets[x] == toupper(c)) return(x); + return(0); +} + +/* Returns 1 on success, 0 on failure. Sets *a,*v,*c. */ +int FCEUI_DecodeGG(const char *str, int *a, int *v, int *c) +{ + uint16 A; + uint8 V,C; + uint8 t; + int s; + + A=0x8000; + V=0; + C=0; + + s=strlen(str); + if(s!=6 && s!=8) return(0); + + t=GGtobin(*str++); + V|=(t&0x07); + V|=(t&0x08)<<4; + + t=GGtobin(*str++); + V|=(t&0x07)<<4; + A|=(t&0x08)<<4; + + t=GGtobin(*str++); + A|=(t&0x07)<<4; + //if(t&0x08) return(0); /* 8-character code?! */ + + t=GGtobin(*str++); + A|=(t&0x07)<<12; + A|=(t&0x08); + + t=GGtobin(*str++); + A|=(t&0x07); + A|=(t&0x08)<<8; + + if(s==6) + { + t=GGtobin(*str++); + A|=(t&0x07)<<8; + V|=(t&0x08); + + *a=A; + *v=V; + *c=-1; + return(1); + } + else + { + t=GGtobin(*str++); + A|=(t&0x07)<<8; + C|=(t&0x08); + + t=GGtobin(*str++); + C|=(t&0x07); + C|=(t&0x08)<<4; + + t=GGtobin(*str++); + C|=(t&0x07)<<4; + V|=(t&0x08); + *a=A; + *v=V; + *c=C; + return(1); + } + return(0); +} + +int FCEUI_DecodePAR(const char *str, int *a, int *v, int *c, int *type) +{ + int boo[4]; + if(strlen(str)!=8) return(0); + + sscanf(str,"%02x%02x%02x%02x",boo,boo+1,boo+2,boo+3); + + *c=-1; + + if(1) + { + *a=(boo[3]<<8)|(boo[2]+0x7F); + *v=0; + } + else + { + *v=boo[3]; + *a=boo[2]|(boo[1]<<8); + } + /* Zero-page addressing modes don't go through the normal read/write handlers in FCEU, so + we must do the old hacky method of RAM cheats. + */ + if(*a<0x0100) + *type=0; + else + *type=1; + return(1); +} + +/* name can be NULL if the name isn't going to be changed. */ +/* same goes for a, v, and s(except the values of each one must be <0) */ + +int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int c, int s, int type) +{ + struct CHEATF *next = cheats; + uint32 x = 0; + + while(next) + { + if(x == which) + { + if(name) + { + char *t; + if((t = (char *)realloc(next->name, strlen(name) + 1))) + strcpy(next->name = t, name); + else + return 0; + } + if(a >= 0) + next->addr = a; + if(v >= 0) + next->val = v; + if(s >= 0) + next->status = s; + if(c >= -1) + next->compare = c; + next->type = type; + + savecheats = 1; + RebuildSubCheats(); + + return 1; + } + next = next->next; + x++; + } + return 0; +} + +/* Convenience function. */ +int FCEUI_ToggleCheat(uint32 which) +{ + struct CHEATF *next=cheats; + uint32 x=0; + + while(next) + { + if(x==which) + { + next->status=!next->status; + savecheats=1; + RebuildSubCheats(); + return(next->status); + } + next=next->next; + x++; + } + + return(-1); +} + +int FCEUI_GlobalToggleCheat(int global_enabled) +{ + uint32 _numsubcheats = numsubcheats; + globalCheatDisabled = !global_enabled; + RebuildSubCheats(); + return _numsubcheats != numsubcheats; +} + +static int InitCheatComp(void) +{ + uint32 x; + + CheatComp=(uint16*)FCEU_dmalloc(65536*sizeof(uint16)); + if(!CheatComp) + { + CheatMemErr(); + return(0); + } + for(x=0;x<65536;x++) + CheatComp[x]=CHEATC_NONE; + + return(1); +} + +void FCEUI_CheatSearchSetCurrentAsOriginal(void) +{ + uint32 x; + + if(!CheatComp) + { + if(InitCheatComp()) + { + CheatMemErr(); + return; + } + } + for(x=0x000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatRPtrs[x>>10]) + CheatComp[x]=CheatRPtrs[x>>10][x]; + else + CheatComp[x]|=CHEATC_NONE; + } +} + +void FCEUI_CheatSearchShowExcluded(void) +{ + uint32 x; + + for(x=0x000;x<0x10000;x++) + CheatComp[x]&=~CHEATC_EXCLUDED; +} + + +int32 FCEUI_CheatSearchGetCount(void) +{ + uint32 x,c=0; + + if(CheatComp) + { + for(x=0x0000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10]) + c++; + } + + return c; +} +/* This function will give the initial value of the search and the current value at a location. */ + +void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current, void *data),void *data) +{ + uint32 x; + + if(!CheatComp) + { + if(!InitCheatComp()) + CheatMemErr(); + return; + } + + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW) && CheatRPtrs[x>>10]) + if(!callb(x,CheatComp[x],CheatRPtrs[x>>10][x],data)) + break; +} + +void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, uint8 last, uint8 current)) +{ + uint32 x; + uint32 in = 0; + + if(!CheatComp) + { + if(!InitCheatComp()) + CheatMemErr(); + return; + } + + for(x = 0; x < 0x10000; x++) + if(!(CheatComp[x] & CHEATC_NOSHOW) && CheatRPtrs[x >> 10]) + { + if(in >= first) + if(!callb(x, CheatComp[x], CheatRPtrs[x >> 10][x])) + break; + in++; + if(in > last) + return; + } +} + +void FCEUI_CheatSearchBegin(void) +{ + uint32 x; + + if(!CheatComp) + { + if(!InitCheatComp()) + { + CheatMemErr(); + return; + } + } + for(x=0;x<0x10000;x++) + { + if(CheatRPtrs[x>>10]) + CheatComp[x]=CheatRPtrs[x>>10][x]; + else + CheatComp[x]=CHEATC_NONE; + } +} + + +static int INLINE CAbs(int x) +{ + if(x<0) + return(0-x); + return x; +} + +void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2) +{ + uint32 x; + + if(!CheatComp) + { + if(!InitCheatComp()) + { + CheatMemErr(); + return; + } + } + + switch (type) + { + default: + case FCEU_SEARCH_SPECIFIC_CHANGE: // Change to a specific value + for (x = 0; x < 0x10000; ++x) + if (!(CheatComp[x] & CHEATC_NOSHOW) && (CheatComp[x] != v1 || CheatRPtrs[x >> 10][x] != v2)) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_RELATIVE_CHANGE: // Search for relative change (between values). + for (x = 0; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && (CheatComp[x] != v1 || CAbs(CheatComp[x] - CheatRPtrs[x >> 10][x]) != v2)) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_PUERLY_RELATIVE_CHANGE: // Purely relative change. + for (x = 0x000; x<0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && CAbs(CheatComp[x] - CheatRPtrs[x >> 10][x]) != v2) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_ANY_CHANGE: // Any change. + for (x = 0x000; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && CheatComp[x] == CheatRPtrs[x >> 10][x]) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_NEWVAL_KNOWN: // new value = known + for (x = 0x000; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && CheatRPtrs[x >> 10][x] != v1) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_NEWVAL_GT: // new value greater than + for (x = 0x000; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && CheatComp[x] >= CheatRPtrs[x >> 10][x]) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_NEWVAL_LT: // new value less than + for (x = 0x000; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && CheatComp[x] <= CheatRPtrs[x >> 10][x]) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_NEWVAL_GT_KNOWN: // new value greater than by known value + for (x = 0x000; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && CheatRPtrs[x >> 10][x] - CheatComp[x] != v2) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + case FCEU_SEARCH_NEWVAL_LT_KNOWN: // new value less than by known value + for (x = 0x000; x < 0x10000; x++) + if (!(CheatComp[x] & CHEATC_NOSHOW) && (CheatComp[x] - CheatRPtrs[x >> 10][x]) != v2) + CheatComp[x] |= CHEATC_EXCLUDED; + break; + } + +} + +int FCEU_CheatGetByte(uint32 A) +{ + if(A < 0x10000) { + uint32 ret; + fceuindbg=1; + ret = ARead[A](A); + fceuindbg=0; + return ret; + } else + return 0; +} + +void FCEU_CheatSetByte(uint32 A, uint8 V) +{ + if(CheatRPtrs[A>>10]) + CheatRPtrs[A>>10][A]=V; + else if(A < 0x10000) + BWrite[A](A, V); +} + +void UpdateFrozenList(void) +{ + //The purpose of this function is to keep an up to date list of addresses that are currently frozen + //and make these accessible to other dialogs that deal with memory addresses such as + //memwatch, hex editor, ramfilter, etc. + + uint32 x; + FrozenAddresses.clear(); //Clear vector and repopulate + for(x = 0; x < numsubcheats; x++) + { + FrozenAddresses.push_back(SubCheats[x].addr); + //FCEU_printf("Address %d: %d \n",x,FrozenAddresses[x]); //Debug + } + //FCEUI_DispMessage("FrozenCount: %d",0,FrozenAddressCount);//Debug +} + +// disable all cheats +int FCEU_DisableAllCheats(){ + int count = 0; + struct CHEATF *next = cheats; + while(next) + { + if(next->status){ + count++; + } + next->status = 0; + next = next->next; + } + savecheats = 1; + RebuildSubCheats(); + return count; +} diff --git a/apps/fceux/src/cheat.h b/apps/fceux/src/cheat.h new file mode 100644 index 00000000..0dea8cc1 --- /dev/null +++ b/apps/fceux/src/cheat.h @@ -0,0 +1,64 @@ +#ifndef CHEAT_H +#define CHEAT_H +void FCEU_CheatResetRAM(void); +void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p); + +void FCEU_LoadGameCheats(FILE *override, int override_existing = 1); +void FCEU_FlushGameCheats(FILE *override, int nosave); +void FCEU_SaveGameCheats(FILE *fp, int release = 0); +int FCEUI_GlobalToggleCheat(int global_enabled); +void FCEU_ApplyPeriodicCheats(void); +void FCEU_PowerCheats(void); +int FCEU_CalcCheatAffectedBytes(uint32 address, uint32 size); + + +int FCEU_CheatGetByte(uint32 A); +void FCEU_CheatSetByte(uint32 A, uint8 V); + +extern int savecheats; +extern int globalCheatDisabled; +extern int disableAutoLSCheats; + +int FCEU_DisableAllCheats(); + +typedef struct { + uint16 addr; + uint8 val; + int compare; + readfunc PrevRead; +} CHEATF_SUBFAST; + +struct CHEATF { + struct CHEATF *next; + char *name = const_cast(""); + uint16 addr; + uint8 val; + int compare; /* -1 for no compare. */ + int type; /* 0 for replace, 1 for substitute(GG). */ + int status; +}; + +struct SEARCHPOSSIBLE { + uint16 addr; + uint8 previous; + uint8 current; + bool update; +}; + +#define FCEU_SEARCH_SPECIFIC_CHANGE 0 +#define FCEU_SEARCH_RELATIVE_CHANGE 1 +#define FCEU_SEARCH_PUERLY_RELATIVE_CHANGE 2 +#define FCEU_SEARCH_ANY_CHANGE 3 +#define FCEU_SEARCH_NEWVAL_KNOWN 4 +#define FCEU_SEARCH_NEWVAL_GT 5 +#define FCEU_SEARCH_NEWVAL_LT 6 +#define FCEU_SEARCH_NEWVAL_GT_KNOWN 7 +#define FCEU_SEARCH_NEWVAL_LT_KNOWN 8 + + +#define CalcAddressRangeCheatCount(count, address, size) \ + count = 0; \ + for (int i = 0; i < numsubcheats && count < size; ++i) \ + if (SubCheats[i].addr >= address && SubCheats[i].addr < address + size) \ + ++count +#endif diff --git a/apps/fceux/src/conddebug.cpp b/apps/fceux/src/conddebug.cpp new file mode 100644 index 00000000..eb8f773c --- /dev/null +++ b/apps/fceux/src/conddebug.cpp @@ -0,0 +1,523 @@ +/* FCEUXD SP - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 Sebastian Porst + * + * 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 + */ + +/* +* The parser was heavily inspired by the following two websites: + +* http://www.cs.utsa.edu/~wagner/CS5363/rdparse.html +* http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm +* +* Grammar of the parser: +* +* P -> Connect +* Connect -> Compare {('||' | '&&') Compare} +* Compare -> Sum {('==' | '!=' | '<=' | '>=' | '<' | '>') Sum} +* Sum -> Product {('+' | '-') Product} +* Product -> Primitive {('*' | '/') Primitive} +* Primitive -> Number | Address | Register | Flag | PC Bank | '(' Connect ')' +* Number -> '#' [1-9A-F]* +* Address -> '$' [1-9A-F]* | '$' '[' Connect ']' +* Register -> 'A' | 'X' | 'Y' | 'P' | 'S' +* Flag -> 'N' | 'C' | 'Z' | 'I' | 'B' | 'V' | 'U' | 'D' +* PC Bank -> 'K' +* Data Bank -> 'T' +* Value -> 'R' | 'W' +*/ + +#include "types.h" +#include "conddebug.h" +#include "utils/memory.h" + +#include +#include +#include +#include +#include + +uint16 debugLastAddress = 0; // used by 'T' and 'R' conditions +uint8 debugLastOpcode; // used to evaluate 'W' condition + +// Next non-whitespace character in string +char next; + +int ishex(char c) +{ + return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + +void scan(const char** str) +{ + do + { + next = **str; + (*str)++; + } while (isspace(next)); +} + +// Frees a condition and all of it's sub conditions +void freeTree(Condition* c) +{ + if (c->lhs) freeTree(c->lhs); + if (c->rhs) freeTree(c->rhs); + + free(c); +} + +// Generic function to handle all infix operators but the last one in the precedence hierarchy. : '(' E ')' +Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**)) +{ + Condition* t = nextPart(str); + Condition* t1; + Condition* mid; + int op; + + while ((op = operators(str))) + { + scan(str); + + t1 = nextPart(str); + + if (t1 == 0) + { + if(t) + freeTree(t); + return 0; + } + + mid = (Condition*)FCEU_dmalloc(sizeof(Condition)); + if (!mid) + return NULL; + memset(mid, 0, sizeof(Condition)); + + mid->lhs = t; + mid->rhs = t1; + mid->op = op; + + t = mid; + } + + return t; +} + +// Generic handler for two-character operators +int TwoCharOperator(const char** str, char c1, char c2, int op) +{ + if (next == c1 && **str == c2) + { + scan(str); + return op; + } + else + { + return 0; + } +} + +// Determines if a character is a flag +int isFlag(char c) +{ + return c == 'N' || c == 'I' || c == 'C' || c == 'V' || c == 'Z' || c == 'B' || c == 'U' || c == 'D'; +} + +// Determines if a character is a register +int isRegister(char c) +{ + return c == 'A' || c == 'X' || c == 'Y' || c == 'P' || c == 'S'; +} + +// Determines if a character is for PC bank +int isPCBank(char c) +{ + return c == 'K'; +} + +// Determines if a character is for Data bank +int isDataBank(char c) +{ + return c == 'T'; +} + +// Determines if a character is for value read +int isValueRead(char c) +{ + return c == 'R'; +} + +// Determines if a character is for value write +int isValueWrite(char c) +{ + return c == 'W'; +} + +// Reads a hexadecimal number from str +int getNumber(unsigned int* number, const char** str) +{ +// char buffer[5]; + + if (sscanf(*str, "%X", number) == EOF || *number > 0xFFFF) + { + return 0; + } + +// Older, inferior version which doesn't work with leading zeros +// sprintf(buffer, "%X", *number); +// *str += strlen(buffer); + while (ishex(**str)) (*str)++; + scan(str); + + return 1; +} + +Condition* Connect(const char** str); + +// Handles the following part of the grammar: '(' E ')' +Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar) +{ + if (next == openPar) + { + scan(str); + + c->lhs = Connect(str); + + if (!c) return 0; + + if (next == closePar) + { + scan(str); + return c; + } + else + { + return 0; + } + } + + return 0; +} + +/* +* Check for primitives +* Flags, Registers, Numbers, Addresses and parentheses +*/ +Condition* Primitive(const char** str, Condition* c) +{ + if (isFlag(next)) /* Flags */ + { + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_FLAG; + c->value1 = next; + } + else + { + c->type2 = TYPE_FLAG; + c->value2 = next; + } + + scan(str); + + return c; + } + else if (isRegister(next)) /* Registers */ + { + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_REG; + c->value1 = next; + } + else + { + c->type2 = TYPE_REG; + c->value2 = next; + } + + scan(str); + + return c; + } + else if (isPCBank(next)) /* PC Bank */ + { + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_PC_BANK; + c->value1 = next; + } + else + { + c->type2 = TYPE_PC_BANK; + c->value2 = next; + } + + scan(str); + + return c; + } + else if (isDataBank(next)) /* Data Bank */ + { + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_DATA_BANK; + c->value1 = next; + } + else + { + c->type2 = TYPE_DATA_BANK; + c->value2 = next; + } + + scan(str); + + return c; + } + else if (isValueRead(next)) + { + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_VALUE_READ; + c->value1 = next; + } + else + { + c->type2 = TYPE_VALUE_READ; + c->value2 = next; + } + + scan(str); + + return c; + } + else if (isValueWrite(next)) + { + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_VALUE_WRITE; + c->value1 = next; + } + else + { + c->type2 = TYPE_VALUE_WRITE; + c->value2 = next; + } + + scan(str); + + return c; + } + else if (next == '#') /* Numbers */ + { + unsigned int number = 0; + if (!getNumber(&number, str)) + { + return 0; + } + + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_NUM; + c->value1 = number; + } + else + { + c->type2 = TYPE_NUM; + c->value2 = number; + } + + return c; + } + else if (next == '$') /* Addresses */ + { + if ((**str >= '0' && **str <= '9') || (**str >= 'A' && **str <= 'F')) /* Constant addresses */ + { + unsigned int number = 0; + if (!getNumber(&number, str)) + { + return 0; + } + + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_ADDR; + c->value1 = number; + } + else + { + c->type2 = TYPE_ADDR; + c->value2 = number; + } + + return c; + } + else if (**str == '[') /* Dynamic addresses */ + { + scan(str); + Parentheses(str, c, '[', ']'); + + if (c->type1 == TYPE_NO) + { + c->type1 = TYPE_ADDR; + } + else + { + c->type2 = TYPE_ADDR; + } + + return c; + } + else + { + return 0; + } + } + else if (next == '(') + { + return Parentheses(str, c, '(', ')'); + } + + return 0; +} + +/* Handle * and / operators */ +Condition* Term(const char** str) +{ + Condition* t; + Condition* t1; + Condition* mid; + + t = (Condition*)FCEU_dmalloc(sizeof(Condition)); + if (!t) + return NULL; + + memset(t, 0, sizeof(Condition)); + + if (!Primitive(str, t)) + { + freeTree(t); + return 0; + } + + while (next == '*' || next == '/') + { + int op = next == '*' ? OP_MULT : OP_DIV; + + scan(str); + + if (!(t1 = (Condition*)FCEU_dmalloc(sizeof(Condition)))) + return NULL; + + memset(t1, 0, sizeof(Condition)); + + if (!Primitive(str, t1)) + { + freeTree(t); + freeTree(t1); + return 0; + } + + if (!(mid = (Condition*)FCEU_dmalloc(sizeof(Condition)))) + return NULL; + + memset(mid, 0, sizeof(Condition)); + + mid->lhs = t; + mid->rhs = t1; + mid->op = op; + + t = mid; + } + + return t; +} + +/* Check for + and - operators */ +int SumOperators(const char** str) +{ + switch (next) + { + case '+': return OP_PLUS; + case '-': return OP_MINUS; + default: return OP_NO; + } +} + +/* Handle + and - operators */ +Condition* Sum(const char** str) +{ + return InfixOperator(str, Term, SumOperators); +} + +/* Check for <=, =>, ==, !=, > and < operators */ +int CompareOperators(const char** str) +{ + int val = TwoCharOperator(str, '=', '=', OP_EQ); + if (val) return val; + + val = TwoCharOperator(str, '!', '=', OP_NE); + if (val) return val; + + val = TwoCharOperator(str, '>', '=', OP_GE); + if (val) return val; + + val = TwoCharOperator(str, '<', '=', OP_LE); + if (val) return val; + + val = next == '>' ? OP_G : 0; + if (val) return val; + + val = next == '<' ? OP_L : 0; + if (val) return val; + + return OP_NO; +} + +/* Handle <=, =>, ==, !=, > and < operators */ +Condition* Compare(const char** str) +{ + return InfixOperator(str, Sum, CompareOperators); +} + +/* Check for || or && operators */ +int ConnectOperators(const char** str) +{ + int val = TwoCharOperator(str, '|', '|', OP_OR); + if(val) return val; + + val = TwoCharOperator(str, '&', '&', OP_AND); + if(val) return val; + + return OP_NO; +} + +/* Handle || and && operators */ +Condition* Connect(const char** str) +{ + return InfixOperator(str, Compare, ConnectOperators); +} + +/* Root of the parser generator */ +Condition* generateCondition(const char* str) +{ + Condition* c; + + scan(&str); + c = Connect(&str); + + if (!c || next != 0) return 0; + else return c; +} diff --git a/apps/fceux/src/conddebug.h b/apps/fceux/src/conddebug.h new file mode 100644 index 00000000..24af528f --- /dev/null +++ b/apps/fceux/src/conddebug.h @@ -0,0 +1,69 @@ +/* FCEUXD SP - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 Sebastian Porst + * + * 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 + */ + +#ifndef CONDDEBUG_H +#define CONDDEBUG_H + +#define TYPE_NO 0 +#define TYPE_REG 1 +#define TYPE_FLAG 2 +#define TYPE_NUM 3 +#define TYPE_ADDR 4 +#define TYPE_PC_BANK 5 +#define TYPE_DATA_BANK 6 +#define TYPE_VALUE_READ 7 +#define TYPE_VALUE_WRITE 8 + +#define OP_NO 0 +#define OP_EQ 1 +#define OP_NE 2 +#define OP_GE 3 +#define OP_LE 4 +#define OP_G 5 +#define OP_L 6 +#define OP_PLUS 7 +#define OP_MINUS 8 +#define OP_MULT 9 +#define OP_DIV 10 +#define OP_OR 11 +#define OP_AND 12 + +extern uint16 debugLastAddress; +extern uint8 debugLastOpcode; + +//mbg merge 7/18/06 turned into sane c++ +struct Condition +{ + Condition* lhs; + Condition* rhs; + + unsigned int type1; + unsigned int value1; + + unsigned int op; + + unsigned int type2; + unsigned int value2; +}; + +void freeTree(Condition* c); +Condition* generateCondition(const char* str); + +#endif diff --git a/apps/fceux/src/config.cpp b/apps/fceux/src/config.cpp new file mode 100644 index 00000000..f998a749 --- /dev/null +++ b/apps/fceux/src/config.cpp @@ -0,0 +1,59 @@ +/// \file +/// \brief Contains methods related to the build configuration + +#include "types.h" +#include "version.h" +#include "fceu.h" +#include "driver.h" +#include "utils/memory.h" + +#include +#include +#include + +static char *aboutString = 0; + +// returns a string suitable for use in an aboutbox +char *FCEUI_GetAboutString() { + const char *aboutTemplate = + FCEU_NAME_AND_VERSION "\n\n" + "Administrators:\n" + "zeromus, punkrockguy318 (Lukas Sabota), feos\n" + "\n" + "Current Contributors:\n" + "CaH4e3, rainwarrior\n" + "\n" + "Past Contributors:\n" + "xhainingx, gocha, AnS\n" + "\n" + "FCEUX 2.0:\n" + "mz, nitsujrehtona, SP, Ugly Joe,\n" + "Plombo, qeed, QFox, Shinydoofy\n" + "ugetab, Acmlm, DWEdit\n" + "\n" + "Previous versions:\n" + "FCE - Bero\n" + "FCEU - Xodnizel\n" + "FCEU XD - Bbitmaster & Parasyte\n" + "FCEU XD SP - Sebastian Porst\n" + "FCEU MM - CaH4e3\n" + "FCEU TAS - blip & nitsuja\n" + "FCEU TAS+ - Luke Gustafson\n" + "\n" + "FCEUX is dedicated to the fallen heroes\n" + "of NES emulation. In Memoriam --\n" + "ugetab\n" + "\n" + __TIME__ " " __DATE__ "\n"; + + if(aboutString) return aboutString; + + const char *compilerString = FCEUD_GetCompilerString(); + + //allocate the string and concatenate the template with the compiler string + if (!(aboutString = (char*)FCEU_dmalloc(strlen(aboutTemplate) + strlen(compilerString) + 1))) + return NULL; + + sprintf(aboutString,"%s%s",aboutTemplate,compilerString); + return aboutString; +} diff --git a/apps/fceux/src/debug.cpp b/apps/fceux/src/debug.cpp new file mode 100644 index 00000000..fbb3c36c --- /dev/null +++ b/apps/fceux/src/debug.cpp @@ -0,0 +1,873 @@ +/// \file +/// \brief Implements core debugging facilities +#include "types.h" +#include "x6502.h" +#include "fceu.h" +#include "cart.h" +#include "ines.h" +#include "debug.h" +#include "driver.h" +#include "ppu.h" + +#include "x6502abbrev.h" + +#include +#include + +unsigned int debuggerPageSize = 14; +int vblankScanLines = 0; //Used to calculate scanlines 240-261 (vblank) +int vblankPixel = 0; //Used to calculate the pixels in vblank + +int offsetStringToInt(unsigned int type, const char* offsetBuffer) +{ + int offset = -1; + + if (sscanf(offsetBuffer,"%4X",&offset) == EOF) + { + return -1; + } + + if (type & BT_P) + { + return offset & 0x3FFF; + } + else if (type & BT_S) + { + return offset & 0x00FF; + } + else // BT_C + { + if (GameInfo->type == GIT_NSF) { //NSF Breakpoint keywords + if (strcmp(offsetBuffer,"LOAD") == 0) return (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8)); + if (strcmp(offsetBuffer,"INIT") == 0) return (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8)); + if (strcmp(offsetBuffer,"PLAY") == 0) return (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8)); + } + else if (GameInfo->type == GIT_FDS) { //FDS Breakpoint keywords + if (strcmp(offsetBuffer,"NMI1") == 0) return (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8)); + if (strcmp(offsetBuffer,"NMI2") == 0) return (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8)); + if (strcmp(offsetBuffer,"NMI3") == 0) return (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8)); + if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8)); + if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8)); + } + else { //NES Breakpoint keywords + if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) return (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8)); + if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8)); + if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8)); + } + } + + return offset; +} + +// Returns the value of a given type or register + +int getValue(int type) +{ + switch (type) + { + case 'A': return _A; + case 'X': return _X; + case 'Y': return _Y; + case 'N': return _P & N_FLAG ? 1 : 0; + case 'V': return _P & V_FLAG ? 1 : 0; + case 'U': return _P & U_FLAG ? 1 : 0; + case 'B': return _P & B_FLAG ? 1 : 0; + case 'D': return _P & D_FLAG ? 1 : 0; + case 'I': return _P & I_FLAG ? 1 : 0; + case 'Z': return _P & Z_FLAG ? 1 : 0; + case 'C': return _P & C_FLAG ? 1 : 0; + case 'P': return _PC; + case 'S': return _S; + } + + return 0; +} + + +/** +* Checks whether a breakpoint condition is syntactically valid +* and creates a breakpoint condition object if everything's OK. +* +* @param condition Condition to parse +* @param num Number of the breakpoint in the BP list the condition belongs to +* @return 0 in case of an error; 2 if everything went fine +**/ +int checkCondition(const char* condition, int num) +{ + const char* b = condition; + + // Check if the condition isn't just all spaces. + + int onlySpaces = 1; + + while (*b) + { + if (*b != ' ') + { + onlySpaces = 0; + break; + } + + ++b; + } + + + // If there's an actual condition create the BP condition object now + + if (*condition && !onlySpaces) + { + Condition* c = generateCondition(condition); + + // Remove the old breakpoint condition before adding a new condition. + if (watchpoint[num].cond) + { + freeTree(watchpoint[num].cond); + free(watchpoint[num].condText); + watchpoint[num].cond = 0; + watchpoint[num].condText = 0; + } + + // If the creation of the BP condition object was succesful + // the condition is apparently valid. It can be added to the + // breakpoint now. + + if (c) + { + watchpoint[num].cond = c; + watchpoint[num].condText = (char*)malloc(strlen(condition) + 1); + if (!watchpoint[num].condText) + return 0; + strcpy(watchpoint[num].condText, condition); + } + else + { + watchpoint[num].cond = 0; + } + + return watchpoint[num].cond == 0 ? 2 : 0; + } + else + { + // Remove the old breakpoint condition + if (watchpoint[num].cond) + { + freeTree(watchpoint[num].cond); + free(watchpoint[num].condText); + watchpoint[num].cond = 0; + watchpoint[num].condText = 0; + } + return 0; + } +} + +/** +* Adds a new breakpoint. +* +* @param hwndDlg Handle of the debugger window +* @param num Number of the breakpoint +* @param +**/ +unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable) +{ + // Finally add breakpoint to the list + watchpoint[num].address = start; + watchpoint[num].endaddress = 0; + + // Optional end address found + if (end != -1) + { + watchpoint[num].endaddress = end; + } + + // Get the breakpoint flags + watchpoint[num].flags = 0; + if (enable) watchpoint[num].flags|=WP_E; + if (type & WP_R) watchpoint[num].flags|=WP_R; + if (type & WP_F) watchpoint[num].flags|=WP_F; + if (type & WP_W) watchpoint[num].flags|=WP_W; + if (type & WP_X) watchpoint[num].flags|=WP_X; + if (type & BT_P) { + watchpoint[num].flags|=BT_P; + watchpoint[num].flags&=~WP_X; //disable execute flag! + } + if (type & BT_S) { + watchpoint[num].flags|=BT_S; + watchpoint[num].flags&=~WP_X; //disable execute flag! + } + + if (watchpoint[num].desc) + free(watchpoint[num].desc); + + watchpoint[num].desc = (char*)malloc(strlen(name) + 1); + strcpy(watchpoint[num].desc, name); + + return checkCondition(condition, num); +} + +int GetPRGAddress(int A){ + int result; + if(A > 0xFFFF) + return -1; + result = &Page[A>>11][A]-PRGptr[0]; + if((result > (int)PRGsize[0]) || (result < 0)) + return -1; + else + return result; +} + +/** +* Returns the bank for a given offset. +* Technically speaking this function does not calculate the actual bank +* where the offset resides but the 0x4000 bytes large chunk of the ROM of the offset. +* +* @param offs The offset +* @return The bank of that offset or -1 if the offset is not part of the ROM. +**/ +int getBank(int offs) +{ + //NSF data is easy to overflow the return on. + //Anything over FFFFF will kill it. + + //GetNesFileAddress doesn't work well with Unif files + int addr = GetNesFileAddress(offs)-16; + + if (GameInfo && GameInfo->type==GIT_NSF) + return addr != -1 ? addr / 0x1000 : -1; + return addr != -1 ? addr / (1< 0xFFFF))return -1; + result = &Page[A>>11][A]-PRGptr[0]; + if((result > (int)(PRGsize[0])) || (result < 0))return -1; + else return result+16; //16 bytes for the header remember +} + +int GetRomAddress(int A){ + int i; + uint8 *p = GetNesPRGPointer(A-=16); + for(i = 16;i < 32;i++){ + if((&Page[i][i<<11] <= p) && (&Page[i][(i+1)<<11] > p))break; + } + if(i == 32)return -1; //not found + + return (i<<11) + (p-&Page[i][i<<11]); +} + +uint8 *GetNesPRGPointer(int A){ + return PRGptr[0]+A; +} + +uint8 *GetNesCHRPointer(int A){ + return CHRptr[0]+A; +} + +uint8 GetMem(uint16 A) { + if ((A >= 0x2000) && (A < 0x4000)) // PPU regs and their mirrors + switch (A&7) { + case 0: return PPU[0]; + case 1: return PPU[1]; + case 2: return PPU[2]|(PPUGenLatch&0x1F); + case 3: return PPU[3]; + case 4: return SPRAM[PPU[3]]; + case 5: return XOffset; + case 6: return FCEUPPU_PeekAddress() & 0xFF; + case 7: return VRAMBuffer; + } + // feos: added more registers + else if ((A >= 0x4000) && (A < 0x4010)) + return PSG[A&15]; + else if ((A >= 0x4010) && (A < 0x4018)) + switch(A&7) { + case 0: return DMCFormat; + case 1: return RawDALatch; + case 2: return DMCAddressLatch; + case 3: return DMCSizeLatch; + case 4: return SpriteDMA; + case 5: return EnabledChannels; + case 6: return RawReg4016; + case 7: return IRQFrameMode; + } + else if ((A >= 0x4018) && (A < 0x5000)) // AnS: changed the range, so MMC5 ExRAM can be watched in the Hexeditor + return 0xFF; + if (GameInfo) { //adelikat: 11/17/09: Prevent crash if this is called with no game loaded. + uint32 ret; + fceuindbg=1; + ret = ARead[A](A); + fceuindbg=0; + return ret; + } else return 0; +} + +uint8 GetPPUMem(uint8 A) { + uint16 tmp = FCEUPPU_PeekAddress() & 0x3FFF; + + if (tmp<0x2000) return VPage[tmp>>10][tmp]; + if (tmp>=0x3F00) return PALRAM[tmp&0x1F]; + return vnapage[(tmp>>10)&0x3][tmp&0x3FF]; +} + +//--------------------- + +uint8 evaluateWrite(uint8 opcode, uint16 address) +{ + // predicts value written by this opcode + switch (opwrite[opcode]) + { + default: + case 0: return 0; // no write + case 1: return _A; // STA, PHA + case 2: return _X; // STX + case 3: return _Y; // STY + case 4: return _P; // PHP + case 5: return GetMem(address) << 1; // ASL (SLO) + case 6: return GetMem(address) >> 1; // LSR (SRE) + case 7: return (GetMem(address) << 1) | (_P & 1); // ROL (RLA) + case 8: return (GetMem(address) >> 1) | ((_P & 1) << 7); // ROL (RRA) + case 9: return GetMem(address) + 1; // INC (ISC) + case 10: return GetMem(address) - 1; // DEC (DCP) + case 11: return _A & _X; // (SAX) + case 12: return _A&_X&(((address-_Y)>>8)+1); // (AHX) + case 13: return _Y&(((address-_X)>>8)+1); // (SHY) + case 14: return _X&(((address-_Y)>>8)+1); // (SHX) + case 15: return _S& (((address-_Y)>>8)+1); // (TAS) + } + return 0; +} + +// Evaluates a condition +int evaluate(Condition* c) +{ + int f = 0; + + int value1, value2; + + if (c->lhs) + { + value1 = evaluate(c->lhs); + } + else + { + switch(c->type1) + { + case TYPE_ADDR: // This is intended to not break, and use the TYPE_NUM code + case TYPE_NUM: value1 = c->value1; break; + default: value1 = getValue(c->value1); break; + } + } + + switch(c->type1) + { + case TYPE_ADDR: value1 = GetMem(value1); break; + case TYPE_PC_BANK: value1 = getBank(_PC); break; + case TYPE_DATA_BANK: value1 = getBank(debugLastAddress); break; + case TYPE_VALUE_READ: value1 = GetMem(debugLastAddress); break; + case TYPE_VALUE_WRITE: value1 = evaluateWrite(debugLastOpcode, debugLastAddress); break; + } + + f = value1; + + if (c->op) + { + if (c->rhs) + { + value2 = evaluate(c->rhs); + } + else + { + switch(c->type2) + { + case TYPE_ADDR: // This is intended to not break, and use the TYPE_NUM code + case TYPE_NUM: value2 = c->value2; break; + default: value2 = getValue(c->type2); break; + } + } + + switch(c->type2) + { + case TYPE_ADDR: value2 = GetMem(value2); break; + case TYPE_PC_BANK: value2 = getBank(_PC); break; + case TYPE_DATA_BANK: value2 = getBank(debugLastAddress); break; + case TYPE_VALUE_READ: value2 = GetMem(debugLastAddress); break; + case TYPE_VALUE_WRITE: value2 = evaluateWrite(debugLastOpcode, debugLastAddress); break; + } + + switch (c->op) + { + case OP_EQ: f = value1 == value2; break; + case OP_NE: f = value1 != value2; break; + case OP_GE: f = value1 >= value2; break; + case OP_LE: f = value1 <= value2; break; + case OP_G: f = value1 > value2; break; + case OP_L: f = value1 < value2; break; + case OP_MULT: f = value1 * value2; break; + case OP_DIV: f = (value2==0) ? 0 : (value1 / value2); break; + case OP_PLUS: f = value1 + value2; break; + case OP_MINUS: f = value1 - value2; break; + case OP_OR: f = value1 || value2; break; + case OP_AND: f = value1 && value2; break; + } + } + + return f; +} + +int condition(watchpointinfo* wp) +{ + return wp->cond == 0 || evaluate(wp->cond); +} + + +//--------------------- + +volatile int codecount, datacount, undefinedcount; +unsigned char *cdloggerdata; +unsigned int cdloggerdataSize = 0; +static int indirectnext; + +int debug_loggingCD; + +//called by the cpu to perform logging if CDLogging is enabled +void LogCDVectors(int which){ + int j; + j = GetPRGAddress(which); + if(j == -1) return; + + if(!(cdloggerdata[j] & 2)){ + cdloggerdata[j] |= 0x0E; // we're in the last bank and recording it as data so 0x1110 or 0xE should be what we need + datacount++; + if(!(cdloggerdata[j] & 1))undefinedcount--; + } + j++; + + if(!(cdloggerdata[j] & 2)){ + cdloggerdata[j] |= 0x0E; + datacount++; + if(!(cdloggerdata[j] & 1))undefinedcount--; + } +} + +void LogCDData(uint8 *opcode, uint16 A, int size) { + int i, j; + uint8 memop = 0; + + if((j = GetPRGAddress(_PC)) != -1) + for (i = 0; i < size; i++) { + if(cdloggerdata[j+i] & 1)continue; //this has been logged so skip + cdloggerdata[j+i] |= 1; + cdloggerdata[j+i] |= ((_PC + i) >> 11) & 0x0c; + cdloggerdata[j+i] |= ((_PC & 0x8000) >> 8) ^ 0x80; // 19/07/14 used last reserved bit, if bit 7 is 1, then code is running from lowe area (6000) + if(indirectnext)cdloggerdata[j+i] |= 0x10; + codecount++; + if(!(cdloggerdata[j+i] & 2))undefinedcount--; + } + + //log instruction jumped to in an indirect jump + if(opcode[0] == 0x6c) + indirectnext = 1; + else + indirectnext = 0; + + switch (optype[opcode[0]]) { + case 1: + case 4: memop = 0x20; break; + } + + if((j = GetPRGAddress(A)) != -1) { + if(!(cdloggerdata[j] & 2)) { + cdloggerdata[j] |= 2; + cdloggerdata[j] |=(A>>11)&0x0c; + cdloggerdata[j] |= memop; + datacount++; + if(!(cdloggerdata[j] & 1))undefinedcount--; + } + } +} + +//-----------debugger stuff + +watchpointinfo watchpoint[65]; //64 watchpoints, + 1 reserved for step over +int iaPC; +uint32 iapoffset; //mbg merge 7/18/06 changed from int +int u; //deleteme +int skipdebug; //deleteme +int numWPs; + +bool break_asap = false; +// for CPU cycles and Instructions counters +uint64 total_cycles_base = 0; +uint64 delta_cycles_base = 0; +bool break_on_cycles = false; +uint64 break_cycles_limit = 0; +uint64 total_instructions = 0; +uint64 delta_instructions = 0; +bool break_on_instructions = false; +uint64 break_instructions_limit = 0; + +static DebuggerState dbgstate; + +DebuggerState &FCEUI_Debugger() { return dbgstate; } + +void ResetDebugStatisticsCounters() +{ + ResetCyclesCounter(); + ResetInstructionsCounter(); +} +void ResetCyclesCounter() +{ + total_cycles_base = delta_cycles_base = timestampbase + (uint64)timestamp; +} +void ResetInstructionsCounter() +{ + total_instructions = delta_instructions = 0; +} +void ResetDebugStatisticsDeltaCounters() +{ + delta_cycles_base = timestampbase + (uint64)timestamp; + delta_instructions = 0; +} +void IncrementInstructionsCounters() +{ + total_instructions++; + delta_instructions++; +} + +void BreakHit(int bp_num, bool force) +{ + if(!force) + { + if (bp_num >= 0 && !condition(&watchpoint[bp_num])) + { + return; // condition rejected + } + + //check to see whether we fall in any forbid zone + for (int i = 0; i < numWPs; i++) + { + watchpointinfo& wp = watchpoint[i]; + if(!(wp.flags & WP_F) || !(wp.flags & WP_E)) + continue; + + if (condition(&wp)) + { + if (wp.endaddress) { + if( (wp.address <= _PC) && (wp.endaddress >= _PC) ) + return; //forbid + } else { + if(wp.address == _PC) + return; //forbid + } + } + } + } + + FCEUI_SetEmulationPaused(EMULATIONPAUSED_PAUSED); //mbg merge 7/19/06 changed to use EmulationPaused() + +#ifdef WIN32 + FCEUD_DebugBreakpoint(bp_num); +#endif +} + +int StackAddrBackup; +uint16 StackNextIgnorePC = 0xFFFF; + +///fires a breakpoint +static void breakpoint(uint8 *opcode, uint16 A, int size) { + int i, j; + uint8 brk_type; + uint8 stackop=0; + uint8 stackopstartaddr = 0,stackopendaddr = 0; + + debugLastAddress = A; + debugLastOpcode = opcode[0]; + + if (break_asap) + { + break_asap = false; + BreakHit(BREAK_TYPE_LUA, true); + } + + if (break_on_cycles && ((timestampbase + (uint64)timestamp - total_cycles_base) > break_cycles_limit)) + BreakHit(BREAK_TYPE_CYCLES_EXCEED, true); + if (break_on_instructions && (total_instructions > break_instructions_limit)) + BreakHit(BREAK_TYPE_INSTRUCTIONS_EXCEED, true); + + //if the current instruction is bad, and we are breaking on bad opcodes, then hit the breakpoint + if(dbgstate.badopbreak && (size == 0)) + BreakHit(BREAK_TYPE_BADOP, true); + + //if we're stepping out, track the nest level + if (dbgstate.stepout) { + if (opcode[0] == 0x20) dbgstate.jsrcount++; + else if (opcode[0] == 0x60) { + if (dbgstate.jsrcount) + dbgstate.jsrcount--; + else { + dbgstate.stepout = false; + dbgstate.step = true; + return; + } + } + } + + //if we're stepping, then we'll always want to break + if (dbgstate.step) { + dbgstate.step = false; + BreakHit(BREAK_TYPE_STEP, true); + return; + } + + //if we're running for a scanline, we want to check if we've hit the cycle limit + if (dbgstate.runline) { + uint64 ts = timestampbase; + ts+=timestamp; + int diff = dbgstate.runline_end_time-ts; + if (diff<=0) + { + dbgstate.runline=false; + BreakHit(BREAK_TYPE_STEP, true); + return; + } + } + + //check the step over address and break if we've hit it + if ((watchpoint[64].address == _PC) && (watchpoint[64].flags)) { + watchpoint[64].address = 0; + watchpoint[64].flags = 0; + BreakHit(BREAK_TYPE_STEP, true); + return; + } + + brk_type = opbrktype[opcode[0]] | WP_X; + + switch (opcode[0]) { + //Push Ops + case 0x08: //Fall to next + case 0x48: debugLastAddress=stackopstartaddr=stackopendaddr=X.S-1; stackop=WP_W; StackAddrBackup = X.S; StackNextIgnorePC=_PC+1; break; + //Pull Ops + case 0x28: //Fall to next + case 0x68: debugLastAddress=stackopstartaddr=stackopendaddr=X.S+1; stackop=WP_R; StackAddrBackup = X.S; StackNextIgnorePC=_PC+1; break; + //JSR (Includes return address - 1) + case 0x20: stackopstartaddr=stackopendaddr=X.S-1; stackop=WP_W; StackAddrBackup = X.S; StackNextIgnorePC=(opcode[1]|opcode[2]<<8); break; + //RTI (Includes processor status, and exact return address) + case 0x40: stackopstartaddr=X.S+1; stackopendaddr=X.S+3; stackop=WP_R; StackAddrBackup = X.S; StackNextIgnorePC=(GetMem((X.S+2)|0x0100)|GetMem((X.S+3)|0x0100)<<8); break; + //RTS (Includes return address - 1) + case 0x60: stackopstartaddr=X.S+1; stackopendaddr=X.S+2; stackop=WP_R; StackAddrBackup = X.S; StackNextIgnorePC=(GetMem(stackopstartaddr|0x0100)|GetMem(stackopendaddr|0x0100)<<8)+1; break; + default: break; + } + + #define BREAKHIT(x) { breakHit = (x); goto STOPCHECKING; } + int breakHit = -1; + for (i = 0; i < numWPs; i++) + { + if ((watchpoint[i].flags & WP_E)) + { + if (watchpoint[i].flags & BT_P) + { + // PPU Mem breaks + if ((watchpoint[i].flags & brk_type) && ((A >= 0x2000) && (A < 0x4000)) && ((A&7) == 7)) + { + const uint32 PPUAddr = FCEUPPU_PeekAddress(); + if (watchpoint[i].endaddress) + { + if ((watchpoint[i].address <= PPUAddr) && (watchpoint[i].endaddress >= PPUAddr)) + BREAKHIT(i); + } else + { + if (watchpoint[i].address == PPUAddr) + BREAKHIT(i); + } + } + } else if (watchpoint[i].flags & BT_S) + { + // Sprite Mem breaks + if ((watchpoint[i].flags & brk_type) && ((A >= 0x2000) && (A < 0x4000)) && ((A&7) == 4)) + { + if (watchpoint[i].endaddress) + { + if ((watchpoint[i].address <= PPU[3]) && (watchpoint[i].endaddress >= PPU[3])) + BREAKHIT(i); + } else + { + if (watchpoint[i].address == PPU[3]) + BREAKHIT(i); + } + } else if ((watchpoint[i].flags & WP_W) && (A == 0x4014)) + { + // Sprite DMA! :P + BREAKHIT(i); + } + } else + { + // CPU mem breaks + if ((watchpoint[i].flags & brk_type)) + { + if (watchpoint[i].endaddress) + { + if (((watchpoint[i].flags & (WP_R | WP_W)) && (watchpoint[i].address <= A) && (watchpoint[i].endaddress >= A)) || + ((watchpoint[i].flags & WP_X) && (watchpoint[i].address <= _PC) && (watchpoint[i].endaddress >= _PC))) + BREAKHIT(i); + } else + { + if (((watchpoint[i].flags & (WP_R | WP_W)) && (watchpoint[i].address == A)) || + ((watchpoint[i].flags & WP_X) && (watchpoint[i].address == _PC))) + BREAKHIT(i); + } + } else + { + // brk_type independant coding + if (stackop > 0) + { + // Announced stack mem breaks + // PHA, PLA, PHP, and PLP affect the stack data. + // TXS and TSX only deal with the pointer. + if (watchpoint[i].flags & stackop) + { + for (j = (stackopstartaddr|0x0100); j <= (stackopendaddr|0x0100); j++) + { + if (watchpoint[i].endaddress) + { + if ((watchpoint[i].address <= j) && (watchpoint[i].endaddress >= j)) + BREAKHIT(i); + } else + { + if (watchpoint[i].address == j) + BREAKHIT(i); + } + } + } + } + if (StackNextIgnorePC == _PC) + { + // Used to make it ignore the unannounced stack code one time + StackNextIgnorePC = 0xFFFF; + } else + { + if (StackAddrBackup != -1 && (X.S < StackAddrBackup) && (stackop==0)) + { + // Unannounced stack mem breaks + // Pushes to stack + if (watchpoint[i].flags & WP_W) + { + for (j = (X.S|0x0100); j < (StackAddrBackup|0x0100); j++) + { + if (watchpoint[i].endaddress) + { + if ((watchpoint[i].address <= j) && (watchpoint[i].endaddress >= j)) + BREAKHIT(i); + } else + { + if (watchpoint[i].address == j) + BREAKHIT(i); + } + } + } + } else if (StackAddrBackup != -1 && (StackAddrBackup < X.S) && (stackop==0)) + { + // Pulls from stack + if (watchpoint[i].flags & WP_R) + { + for (j = (StackAddrBackup|0x0100); j < (X.S|0x0100); j++) + { + if (watchpoint[i].endaddress) + { + if ((watchpoint[i].address <= j) && (watchpoint[i].endaddress >= j)) + BREAKHIT(i); + } else + { + if (watchpoint[i].address == j) + BREAKHIT(i); + } + } + } + } + } + + } + } + } + } //loop across all breakpoints + +STOPCHECKING: + + //Update the stack address with the current one, now that changes have registered. + //ZEROMUS THINKS IT MAKES MORE SENSE HERE + StackAddrBackup = X.S; + + if(breakHit != -1) + BreakHit(i); + + ////Update the stack address with the current one, now that changes have registered. + //StackAddrBackup = X.S; +} +//bbit edited: this is the end of the inserted code + +void DebugCycle() +{ + uint8 opcode[3] = {0}; + uint16 A = 0, tmp; + int size; + + if (scanline == 240) + { + vblankScanLines = (PAL?int((double)timestamp / ((double)341 / (double)3.2)):timestamp / 114); //114 approximates the number of timestamps per scanline during vblank. Approx 2508. NTSC: (341 / 3.0) PAL: (341 / 3.2). Uses (3.? * cpu_cycles) / 341.0, and assumes 1 cpu cycle. + if (vblankScanLines) vblankPixel = 341 / vblankScanLines; //341 pixels per scanline + //FCEUI_printf("vbPixel = %d",vblankPixel); //Debug + //FCEUI_printf("ts: %d line: %d\n", timestamp, vblankScanLines); //Debug + } + else + vblankScanLines = 0; + + if (GameInfo->type==GIT_NSF) + { + if ((_PC >= 0x3801) && (_PC <= 0x3824)) return; + } + + opcode[0] = GetMem(_PC); + size = opsize[opcode[0]]; + switch (size) + { + default: + case 1: break; + case 2: + opcode[1] = GetMem(_PC + 1); + break; + case 0: // illegal instructions may have operands + case 3: + opcode[1] = GetMem(_PC + 1); + opcode[2] = GetMem(_PC + 2); + break; + } + + switch (optype[opcode[0]]) + { + case 0: break; + case 1: + tmp = (opcode[1] + _X) & 0xFF; + A = GetMem(tmp); + tmp = (opcode[1] + _X + 1) & 0xFF; + A |= (GetMem(tmp) << 8); + break; + case 2: A = opcode[1]; break; + case 3: A = opcode[1] | (opcode[2] << 8); break; + case 4: A = (GetMem(opcode[1]) | (GetMem((opcode[1] + 1) & 0xFF) << 8)) + _Y; break; + case 5: A = opcode[1] + _X; break; + case 6: A = (opcode[1] | (opcode[2] << 8)) + _Y; break; + case 7: A = (opcode[1] | (opcode[2] << 8)) + _X; break; + case 8: A = opcode[1] + _Y; break; + } + + if (numWPs || dbgstate.step || dbgstate.runline || dbgstate.stepout || watchpoint[64].flags || dbgstate.badopbreak || break_on_cycles || break_on_instructions || break_asap) + breakpoint(opcode, A, size); + + if(debug_loggingCD) + LogCDData(opcode, A, size); + +#ifdef WIN32 + //This needs to be windows only or else the linux build system will fail since logging is declared in a + //windows source file + FCEUD_TraceInstruction(opcode, size); +#endif + +} diff --git a/apps/fceux/src/debug.h b/apps/fceux/src/debug.h new file mode 100644 index 00000000..9c25b376 --- /dev/null +++ b/apps/fceux/src/debug.h @@ -0,0 +1,171 @@ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include "conddebug.h" +#include "git.h" +#include "nsf.h" + +//watchpoint stuffs +#define WP_E 0x01 //watchpoint, enable +#define WP_W 0x02 //watchpoint, write +#define WP_R 0x04 //watchpoint, read +#define WP_X 0x08 //watchpoint, execute +#define WP_F 0x10 //watchpoint, forbid + +#define BT_C 0x00 //break type, cpu mem +#define BT_P 0x20 //break type, ppu mem +#define BT_S 0x40 //break type, sprite mem + +#define BREAK_TYPE_STEP -1 +#define BREAK_TYPE_BADOP -2 +#define BREAK_TYPE_CYCLES_EXCEED -3 +#define BREAK_TYPE_INSTRUCTIONS_EXCEED -4 +#define BREAK_TYPE_LUA -5 + +//opbrktype is used to grab the breakpoint type that each instruction will cause. +//WP_X is not used because ALL opcodes will have the execute bit set. +static const uint8 opbrktype[256] = { + /*0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F*/ +/*0x00*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, 0, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0x10*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0x20*/ 0, WP_R, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, 0, 0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, +/*0x30*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0x40*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, 0, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0x50*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0x60*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, 0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, +/*0x70*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0x80*/ 0, WP_W, 0, 0, WP_W, WP_W, WP_W, 0, 0, 0, 0, 0, WP_W, WP_W, WP_W, 0, +/*0x90*/ 0, WP_W, 0, 0, WP_W, WP_W, WP_W, 0, 0, WP_W, 0, 0, 0, WP_W, 0, 0, +/*0xA0*/ 0, WP_R, 0, 0, WP_R, WP_R, WP_R, 0, 0, 0, 0, 0, WP_R, WP_R, WP_R, 0, +/*0xB0*/ 0, WP_R, 0, 0, WP_R, WP_R, WP_R, 0, 0, WP_R, 0, 0, WP_R, WP_R, WP_R, 0, +/*0xC0*/ 0, WP_R, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, 0, 0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, +/*0xD0*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, +/*0xE0*/ 0, WP_R, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, 0, 0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, +/*0xF0*/ 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0, 0, WP_R, WP_R|WP_W, 0 +}; + + +typedef struct { + uint16 address; + uint16 endaddress; + uint8 flags; + + Condition* cond; + char* condText; + char* desc; + +} watchpointinfo; + +//mbg merge 7/18/06 had to make this extern +extern watchpointinfo watchpoint[65]; //64 watchpoints, + 1 reserved for step over + +int getBank(int offs); +int GetNesFileAddress(int A); +int GetPRGAddress(int A); +int GetRomAddress(int A); +//int GetEditHex(HWND hwndDlg, int id); +uint8 *GetNesPRGPointer(int A); +uint8 *GetNesCHRPointer(int A); +void KillDebugger(); +uint8 GetMem(uint16 A); +uint8 GetPPUMem(uint8 A); + +//---------CDLogger +void LogCDVectors(int which); +void LogCDData(uint8 *opcode, uint16 A, int size); +extern volatile int codecount, datacount, undefinedcount; +extern unsigned char *cdloggerdata; +extern unsigned int cdloggerdataSize; + +extern int debug_loggingCD; +static INLINE void FCEUI_SetLoggingCD(int val) { debug_loggingCD = val; } +static INLINE int FCEUI_GetLoggingCD() { return debug_loggingCD; } +//------- + +//-------tracing +//we're letting the win32 driver handle this ittself for now +//extern int debug_tracing; +//static INLINE void FCEUI_SetTracing(int val) { debug_tracing = val; } +//static INLINE int FCEUI_GetTracing() { return debug_tracing; } +//--------- + +//--------debugger +extern int iaPC; +extern uint32 iapoffset; //mbg merge 7/18/06 changed from int +void DebugCycle(); +void BreakHit(int bp_num, bool force = false); + +extern bool break_asap; +extern uint64 total_cycles_base; +extern uint64 delta_cycles_base; +extern bool break_on_cycles; +extern uint64 break_cycles_limit; +extern uint64 total_instructions; +extern uint64 delta_instructions; +extern bool break_on_instructions; +extern uint64 break_instructions_limit; +extern void ResetDebugStatisticsCounters(); +extern void ResetCyclesCounter(); +extern void ResetInstructionsCounter(); +extern void ResetDebugStatisticsDeltaCounters(); +extern void IncrementInstructionsCounters(); +//------------- + +//internal variables that debuggers will want access to +extern uint8 *vnapage[4],*VPage[8]; +extern uint8 PPU[4],PALRAM[0x20],UPALRAM[3],SPRAM[0x100],VRAMBuffer,PPUGenLatch,XOffset; +extern uint32 FCEUPPU_PeekAddress(); +extern uint8 READPAL_MOTHEROFALL(uint32 A); +extern int numWPs; + +///encapsulates the operational state of the debugger core +class DebuggerState { +public: + ///indicates whether the debugger is stepping through a single instruction + bool step; + ///indicates whether the debugger is stepping out of a function call + bool stepout; + ///indicates whether the debugger is running one line + bool runline; + ///target timestamp for runline to stop at + uint64 runline_end_time; + ///indicates whether the debugger should break on bad opcodes + bool badopbreak; + ///counts the nest level of the call stack while stepping out + int jsrcount; + + ///resets the debugger state to an empty, non-debugging state + void reset() { + numWPs = 0; + step = false; + stepout = false; + jsrcount = 0; + } +}; + +extern NSF_HEADER NSFHeader; + +extern uint8 PSG[0x10]; +extern uint8 DMCFormat; +extern uint8 RawDALatch; +extern uint8 DMCAddressLatch; +extern uint8 DMCSizeLatch; +extern uint8 EnabledChannels; +extern uint8 SpriteDMA; +extern uint8 RawReg4016; +extern uint8 IRQFrameMode; + +///retrieves the core's DebuggerState +DebuggerState &FCEUI_Debugger(); + +//#define CPU_BREAKPOINT 1 +//#define PPU_BREAKPOINT 2 +//#define SPRITE_BREAKPOINT 4 +//#define READ_BREAKPOINT 8 +//#define WRITE_BREAKPOINT 16 +//#define EXECUTE_BREAKPOINT 32 + +int offsetStringToInt(unsigned int type, const char* offsetBuffer); +unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable); + +#endif diff --git a/apps/fceux/src/drawing.cpp b/apps/fceux/src/drawing.cpp new file mode 100644 index 00000000..7597c068 --- /dev/null +++ b/apps/fceux/src/drawing.cpp @@ -0,0 +1,525 @@ +#include "types.h" +#include "fceu.h" +#include "drawing.h" +#include "video.h" +#include "movie.h" +#include "driver.h" + +static uint8 Font6x7[792] = +{ + 6, 0, 0, 0, 0, 0, 0, 0, // 0x20 - Spacebar + 3, 64, 64, 64, 64, 64, 0, 64, + 5, 80, 80, 80, 0, 0, 0, 0, + 6, 80, 80,248, 80,248, 80, 80, + 6, 32,120,160,112, 40,240, 32, + 6, 64,168, 80, 32, 80,168, 16, + 6, 96,144,160, 64,168,144,104, + 3, 64, 64, 0, 0, 0, 0, 0, + 4, 32, 64, 64, 64, 64, 64, 32, + 4, 64, 32, 32, 32, 32, 32, 64, + 6, 0, 80, 32,248, 32, 80, 0, + 6, 0, 32, 32,248, 32, 32, 0, + 3, 0, 0, 0, 0, 0, 64,128, + 5, 0, 0, 0,240, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 64, + 5, 16, 16, 32, 32, 32, 64, 64, + 6,112,136,136,136,136,136,112, // 0x30 - 0 + 6, 32, 96, 32, 32, 32, 32, 32, + 6,112,136, 8, 48, 64,128,248, + 6,112,136, 8, 48, 8,136,112, + 6, 16, 48, 80,144,248, 16, 16, + 6,248,128,128,240, 8, 8,240, + 6, 48, 64,128,240,136,136,112, + 6,248, 8, 16, 16, 32, 32, 32, + 6,112,136,136,112,136,136,112, + 6,112,136,136,120, 8, 16, 96, + 3, 0, 0, 64, 0, 0, 64, 0, + 3, 0, 0, 64, 0, 0, 64,128, + 4, 0, 32, 64,128, 64, 32, 0, + 5, 0, 0,240, 0,240, 0, 0, + 4, 0,128, 64, 32, 64,128, 0, + 6,112,136, 8, 16, 32, 0, 32, // 0x3F - ? + 6,112,136,136,184,176,128,112, // 0x40 - @ + 6,112,136,136,248,136,136,136, // 0x41 - A + 6,240,136,136,240,136,136,240, + 6,112,136,128,128,128,136,112, + 6,224,144,136,136,136,144,224, + 6,248,128,128,240,128,128,248, + 6,248,128,128,240,128,128,128, + 6,112,136,128,184,136,136,120, + 6,136,136,136,248,136,136,136, + 4,224, 64, 64, 64, 64, 64,224, + 6, 8, 8, 8, 8, 8,136,112, + 6,136,144,160,192,160,144,136, + 6,128,128,128,128,128,128,248, + 6,136,216,168,168,136,136,136, + 6,136,136,200,168,152,136,136, + 7, 48, 72,132,132,132, 72, 48, + 6,240,136,136,240,128,128,128, + 6,112,136,136,136,168,144,104, + 6,240,136,136,240,144,136,136, + 6,112,136,128,112, 8,136,112, + 6,248, 32, 32, 32, 32, 32, 32, + 6,136,136,136,136,136,136,112, + 6,136,136,136, 80, 80, 32, 32, + 6,136,136,136,136,168,168, 80, + 6,136,136, 80, 32, 80,136,136, + 6,136,136, 80, 32, 32, 32, 32, + 6,248, 8, 16, 32, 64,128,248, + 3,192,128,128,128,128,128,192, + 5, 64, 64, 32, 32, 32, 16, 16, + 3,192, 64, 64, 64, 64, 64,192, + 4, 64,160, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 0, 0, 0,248, + 3,128, 64, 0, 0, 0, 0, 0, + 5, 0, 0, 96, 16,112,144,112, // 0x61 - a + 5,128,128,224,144,144,144,224, + 5, 0, 0,112,128,128,128,112, + 5, 16, 16,112,144,144,144,112, + 5, 0, 0, 96,144,240,128,112, + 5, 48, 64,224, 64, 64, 64, 64, + 5, 0,112,144,144,112, 16,224, + 5,128,128,224,144,144,144,144, + 2,128, 0,128,128,128,128,128, + 4, 32, 0, 32, 32, 32, 32,192, + 5,128,128,144,160,192,160,144, + 2,128,128,128,128,128,128,128, + 6, 0, 0,208,168,168,168,168, + 5, 0, 0,224,144,144,144,144, + 5, 0, 0, 96,144,144,144, 96, + 5, 0, 0,224,144,144,224,128, + 5, 0, 0,112,144,144,112, 16, + 5, 0, 0,176,192,128,128,128, + 5, 0, 0,112,128, 96, 16,224, + 4, 64, 64,224, 64, 64, 64, 32, + 5, 0, 0,144,144,144,144,112, + 5, 0, 0,144,144,144,160,192, + 6, 0, 0,136,136,168,168, 80, + 5, 0, 0,144,144, 96,144,144, + 5, 0,144,144,144,112, 16, 96, + 5, 0, 0,240, 32, 64,128,240, + 4, 32, 64, 64,128, 64, 64, 32, + 3, 64, 64, 64, 64, 64, 64, 64, + 4,128, 64, 64, 32, 64, 64,128, + 6, 0,104,176, 0, 0, 0, 0 +}; + +void DrawTextLineBG(uint8 *dest) +{ + int x,y; + static int otable[7]={81,49,30,17,8,3,0}; + //100,40,15,10,7,5,2}; + for(y=0;y<14;y++) + { + int offs; + + if(y>=7) offs=otable[13-y]; + else offs=otable[y]; + + for(x=offs;x<(256-offs);x++) + { + // Choose the dimmest set of colours and then dim that + dest[y*256+x]=(dest[y*256+x]&0x0F)|0xC0; + } + } +} + + +void DrawMessage(bool beforeMovie) +{ + if(guiMessage.howlong) + { + //don't display movie messages if we're not before the movie + if(beforeMovie && !guiMessage.isMovieMessage) + return; + + uint8 *t; + guiMessage.howlong--; + + if (guiMessage.linesFromBottom > 0) + t=XBuf+FCEU_TextScanlineOffsetFromBottom(guiMessage.linesFromBottom)+1; + else + t=XBuf+FCEU_TextScanlineOffsetFromBottom(20)+1; + + /* + FCEU palette: + $00: [8] unvpalette found in palettes/palettes.h + black, white, black, greyish, redish, bright green, bluish + $80: + nes palette + $C0: + dim version of nes palette + + */ + + if(t>=XBuf) + { + int color = 0x20; + if(guiMessage.howlong <= 40) color = 0x3C; + if(guiMessage.howlong <= 32) color = 0x31; + if(guiMessage.howlong <= 24) color = 0x21; + if(guiMessage.howlong <= 16) color = 0x51; + if(guiMessage.howlong <= 8) color = 0x41; + DrawTextTrans(ClipSidesOffset+t, 256, (uint8 *)guiMessage.errmsg, color+0x80); + } + } + + if(subtitleMessage.howlong) + { + //don't display movie messages if we're not before the movie + if(beforeMovie && !subtitleMessage.isMovieMessage) + return; + + uint8 *tt; + subtitleMessage.howlong--; + tt=XBuf+FCEU_TextScanlineOffsetFromBottom(216); + + if(tt>=XBuf) + { + int color = 0x20; + if(subtitleMessage.howlong == 39) color = 0x38; + if(subtitleMessage.howlong <= 30) color = 0x2C; + if(subtitleMessage.howlong <= 20) color = 0x1C; + if(subtitleMessage.howlong <= 10) color = 0x11; + if(subtitleMessage.howlong <= 5) color = 0x1; + DrawTextTrans(ClipSidesOffset+tt, 256, (uint8 *)subtitleMessage.errmsg, color+0x80); + } + } +} + + + + +static uint8 sstat[2541] = +{ + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, + 0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x83, + 0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x80,0x83, + 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81, + 0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x81,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83, + 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83, + 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, + 0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83, + 0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x83,0x80,0x80,0x81,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x80,0x83,0x83,0x81,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x81,0x81,0x81,0x83,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 +}; + + + + + +static uint8 play_slines[]= +{ + 0, 0, 1, + 1, 0, 2, + 2, 0, 3, + 3, 0, 4, + 4, 0, 5, + 5, 0, 6, + 6, 0, 7, + 7, 0, 8, + 8, 0, 7, + 9, 0, 6, + 10, 0, 5, + 11, 0, 4, + 12, 0, 3, + 13, 0, 2, + 14, 0, 1, + 99, +}; + +static uint8 record_slines[]= +{ + 0, 5, 9, + 1, 3, 11, + 2, 2, 12, + 3, 1, 13, + 4, 1, 13, + 5, 0, 14, + 6, 0, 14, + 7, 0, 14, + 8, 0, 14, + 9, 0, 14, + 10, 1, 13, + 11, 1, 13, + 12, 2, 12, + 13, 3, 11, + 14, 5, 9, + 99, +}; + +static uint8 pause_slines[]= +{ + 0, 2, 6, + 1, 2, 6, + 2, 2, 6, + 3, 2, 6, + 4, 2, 6, + 5, 2, 6, + 6, 2, 6, + 7, 2, 6, + 8, 2, 6, + 9, 2, 6, + 10, 2, 6, + 11, 2, 6, + 12, 2, 6, + 13, 2, 6, + 14, 2, 6, + + 0, 9, 13, + 1, 9, 13, + 2, 9, 13, + 3, 9, 13, + 4, 9, 13, + 5, 9, 13, + 6, 9, 13, + 7, 9, 13, + 8, 9, 13, + 9, 9, 13, + 10, 9, 13, + 11, 9, 13, + 12, 9, 13, + 13, 9, 13, + 14, 9, 13, + 99, +}; + +static uint8 no_slines[]= +{ + 99 +}; + +static uint8* sline_icons[4]= +{ + no_slines, + play_slines, + record_slines, + pause_slines +}; + +static void drawstatus(uint8* XBuf, int n, int y, int xofs) +{ + uint8* slines=sline_icons[n]; + int i; + + + XBuf += FCEU_TextScanlineOffsetFromBottom(y) + 240 + 255 + xofs; + for(i=0; slines[i]!=99; i+=3) + { + int y=slines[i]; + uint8* dest=XBuf+(y*256); + int x; + for(x=slines[i+1]; x!=slines[i+2]; ++x) + dest[x]=0; + } + + XBuf -= 255; + for(i=0; slines[i]!=99; i+=3) + { + int y=slines[i]; + uint8* dest=XBuf+(y*256); + int x; + for(x=slines[i+1]; x!=slines[i+2]; ++x) + dest[x]=4; + } +} + +/// this draws the recording icon (play/pause/record) +void FCEU_DrawRecordingStatus(uint8* XBuf) +{ + if(FCEUD_ShowStatusIcon()) + { + bool hasPlayRecIcon = false; + if(FCEUMOV_Mode(MOVIEMODE_RECORD)) + { + drawstatus(XBuf-ClipSidesOffset,2,28,0); + hasPlayRecIcon = true; + } + else if(FCEUMOV_Mode(MOVIEMODE_PLAY|MOVIEMODE_FINISHED)) + { + drawstatus(XBuf-ClipSidesOffset,1,28,0); + hasPlayRecIcon = true; + } + + if(FCEUI_EmulationPaused()) + drawstatus(XBuf-ClipSidesOffset,3,28,hasPlayRecIcon?-16:0); + } +} + + +void FCEU_DrawNumberRow(uint8 *XBuf, int *nstatus, int cur) +{ + uint8 *XBaf; + int z,x,y; + + XBaf=XBuf - 4 + (FSettings.LastSLine-34)*256; + if(XBaf>=XBuf) + for(z=1;z<11;z++) + { + if(nstatus[z%10]) + { + for(y=0;y<13;y++) + for(x=0;x<21;x++) + XBaf[y*256+x+z*21+z]=sstat[y*21+x+(z-1)*21*12]^0x80; + } else { + for(y=0;y<13;y++) + for(x=0;x<21;x++) + if(sstat[y*21+x+(z-1)*21*12]!=0x83) + XBaf[y*256+x+z*21+z]=sstat[y*21+x+(z-1)*21*12]^0x80; + + else + XBaf[y*256+x+z*21+z]=(XBaf[y*256+x+z*21+z]&0xF)|0xC0; + } + if(cur==z%10) + { + for(x=0;x<21;x++) + XBaf[x+z*21+z*1]=4; + for(x=1;x<12;x++) + { + XBaf[256*x+z*21+z*1]= + XBaf[256*x+z*21+z*1+20]=4; + } + for(x=0;x<21;x++) + XBaf[12*256+x+z*21+z*1]=4; + } + } +} + +static int FixJoedChar(uint8 ch) +{ + int c = ch - 32; + return (c < 0 || c > 98) ? 0 : c; +} +//static int JoedCharWidth(uint8 ch) +//{ +// return Font6x7[FixJoedChar(ch)*8]; +//} + +char target[64][256]; + +void DrawTextTransWH(uint8 *dest, int width, uint8 *textmsg, uint8 fgcolor, int max_w, int max_h, int border) +{ + int beginx=2, x=beginx; + int y=2; + + memset(target, 0, 64 * 256); + + assert(width==256); + if (max_w > 256) max_w = 256; + if (max_h > 64) max_h = 64; + + int ch = 0, wid = 0, nx = 0, ny = 0, max_x = x, offs = 0; + int pixel_color; + for(; *textmsg; ++textmsg) + { + if(*textmsg == '\n') + { + // new line + x = beginx; + y += 8; + continue; + } + ch = FixJoedChar(*textmsg); + wid = Font6x7[ch * 8]; + + if ((x + wid) >= (int)width) + { + // wrap to new line + x = beginx; + y += 8; + } + + for(ny = 0; ny < 7; ++ny) + { + uint8 d = Font6x7[ch * 8 + 1 + ny]; + for(nx = 0; nx < wid; ++nx) + { + pixel_color = (d >> (7 - nx)) & 1; + if (pixel_color) + { + if (y + ny >= 62) + { + // Max border is 2, so the max safe y is 62 (since 64 is the max for the target array + goto textoverflow; + } + target[y + ny][x + nx] = 2; + } else + { + target[y + ny][x + nx] = 1; + } + } + } + // proceed to next char + x += wid; + if (max_x < x) + max_x = x; + + } +textoverflow: + + max_x += 2; + if (max_x > width) + max_x = width; + int max_y = y + ny + 2; + if (max_y > 62) + max_y = 62; + + // draw target buffer to screen buffer + for (y = 0; y < max_y; ++y) + { + for (x = 0; x < max_x; ++x) + { + offs = y * width + x; + pixel_color = target[y][x] * 100; + + if(border>=1) + { + x>=( 1) && (pixel_color += target[y][x-1]); + x<(width-1) && (pixel_color += target[y][x+1]); + y>=( 1) && (pixel_color += target[y-1][x]); + y<(16 -1) && (pixel_color += target[y+1][x]); + } + if(border>=2) + { + x>=( 1) && (pixel_color += target[y][x-1]*10); + x<(width-1) && (pixel_color += target[y][x+1]*10); + y>=( 1) && (pixel_color += target[y-1][x]*10); + y<(16 -1) && (pixel_color += target[y+1][x]*10); + + x>=( 1) && y>=( 1) && (pixel_color += target[y-1][x-1]); + x<(width-1) && y>=( 1) && (pixel_color += target[y-1][x+1]); + x>=( 1) && y<(16-1) && (pixel_color += target[y+1][x-1]); + x<(width-1) && y<(16-1) && (pixel_color += target[y+1][x+1]); + + x>=( 2) && (pixel_color += target[y][x-2]); + x<(width-2) && (pixel_color += target[y][x+2]); + y>=( 2) && (pixel_color += target[y-2][x]); + y<(16 -2) && (pixel_color += target[y+2][x]); + } + + if(pixel_color >= 200) + dest[offs] = fgcolor; + else if(pixel_color >= 10) + { + if(dest[offs] < 0xA0) + dest[offs] = 0xC1; + else + dest[offs] = 0xD1; + } + else if(pixel_color > 0) + { + dest[offs] = 0xCF; + } + } + } +} + +void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor) +{ + DrawTextTransWH(dest, width, textmsg, fgcolor, 256, 16, 2); +} diff --git a/apps/fceux/src/drawing.h b/apps/fceux/src/drawing.h new file mode 100644 index 00000000..9ec8e50e --- /dev/null +++ b/apps/fceux/src/drawing.h @@ -0,0 +1,6 @@ +void DrawTextLineBG(uint8 *dest); +void DrawMessage(bool beforeMovie); +void FCEU_DrawRecordingStatus(uint8* XBuf); +void FCEU_DrawNumberRow(uint8 *XBuf, int *nstatus, int cur); +void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor); +void DrawTextTransWH(uint8 *dest, int width, uint8 *textmsg, uint8 fgcolor, int max_w, int max_h, int border); diff --git a/apps/fceux/src/driver.h b/apps/fceux/src/driver.h new file mode 100644 index 00000000..d3f1f020 --- /dev/null +++ b/apps/fceux/src/driver.h @@ -0,0 +1,355 @@ +#ifndef __DRIVER_H_ +#define __DRIVER_H_ + +#include "types.h" +#include "git.h" +#include "file.h" + +#include +#include +#include + +FILE *FCEUD_UTF8fopen(const char *fn, const char *mode); +inline FILE *FCEUD_UTF8fopen(const std::string &n, const char *mode) { return FCEUD_UTF8fopen(n.c_str(),mode); } +EMUFILE_FILE* FCEUD_UTF8_fstream(const char *n, const char *m); +inline EMUFILE_FILE* FCEUD_UTF8_fstream(const std::string &n, const char *m) { return FCEUD_UTF8_fstream(n.c_str(),m); } +FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string& fname, int innerIndex); +FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string& fname, int innerIndex, int* userCancel); +FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename); +FCEUFILE* FCEUD_OpenArchive(ArchiveScanRecord& asr, std::string& fname, std::string* innerFilename, int* userCancel); +ArchiveScanRecord FCEUD_ScanArchive(std::string fname); + +//mbg 7/23/06 +const char *FCEUD_GetCompilerString(); + +//This makes me feel dirty for some reason. +void FCEU_printf(const char *format, ...); +#define FCEUI_printf FCEU_printf + +//Video interface +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b); +void FCEUD_GetPalette(uint8 i,uint8 *r, uint8 *g, uint8 *b); + +//Displays an error. Can block or not. +void FCEUD_PrintError(const char *s); +void FCEUD_Message(const char *s); + +//Network interface + +//Call only when a game is loaded. +int FCEUI_NetplayStart(int nlocal, int divisor); + +// Call when network play needs to stop. +void FCEUI_NetplayStop(void); + +//Note: YOU MUST NOT CALL ANY FCEUI_* FUNCTIONS WHILE IN FCEUD_SendData() or FCEUD_RecvData(). + +//Return 0 on failure, 1 on success. +int FCEUD_SendData(void *data, uint32 len); +int FCEUD_RecvData(void *data, uint32 len); + +//Display text received over the network. +void FCEUD_NetplayText(uint8 *text); + +//Encode and send text over the network. +void FCEUI_NetplayText(uint8 *text); + +//Called when a fatal error occurred and network play can't continue. This function +//should call FCEUI_NetplayStop() after it has deinitialized the network on the driver +//side. +void FCEUD_NetworkClose(void); + +bool FCEUI_BeginWaveRecord(const char *fn); +int FCEUI_EndWaveRecord(void); + +void FCEUI_ResetNES(void); +void FCEUI_PowerNES(void); + +void FCEUI_NTSCSELHUE(void); +void FCEUI_NTSCSELTINT(void); +void FCEUI_NTSCDEC(void); +void FCEUI_NTSCINC(void); +void FCEUI_GetNTSCTH(int *tint, int *hue); +void FCEUI_SetNTSCTH(bool en, int tint, int hue); + +void FCEUI_SetInput(int port, ESI type, void *ptr, int attrib); +void FCEUI_SetInputFC(ESIFC type, void *ptr, int attrib); + +//tells the emulator whether a fourscore is attached +void FCEUI_SetInputFourscore(bool attachFourscore); +//tells whether a fourscore is attached +bool FCEUI_GetInputFourscore(); +//tells whether the microphone is used +bool FCEUI_GetInputMicrophone(); + +void FCEUI_UseInputPreset(int preset); + + +//New interface functions + +//0 to order screen snapshots numerically(0.png), 1 to order them file base-numerically(smb3-0.png). +//this variable isn't used at all, snap is always name-based +//void FCEUI_SetSnapName(bool a); + +//0 to keep 8-sprites limitation, 1 to remove it +void FCEUI_DisableSpriteLimitation(int a); + +void FCEUI_SetRenderPlanes(bool sprites, bool bg); +void FCEUI_GetRenderPlanes(bool& sprites, bool& bg); + +//name=path and file to load. returns null if it failed +FCEUGI *FCEUI_LoadGame(const char *name, int OverwriteVidMode, bool silent = false); + +//same as FCEUI_LoadGame, except that it can load from a tempfile. +//name is the logical path to open; archiveFilename is the archive which contains name +FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silent = false); + +//general purpose emulator initialization. returns true if successful +bool FCEUI_Initialize(); + +//Emulates a frame. +void FCEUI_Emulate(uint8 **, int32 **, int32 *, int); + +//Closes currently loaded game +void FCEUI_CloseGame(void); + +//Deallocates all allocated memory. Call after FCEUI_Emulate() returns. +void FCEUI_Kill(void); + +//Enable/Disable game genie. a=true->enabled +void FCEUI_SetGameGenie(bool a); + +//Set video system a=0 NTSC, a=1 PAL +void FCEUI_SetVidSystem(int a); + +//Set variables for NTSC(0) / PAL(1) / Dendy(2) +//Dendy has PAL framerate and resolution, but ~NTSC timings, and has 50 dummy scanlines to force 50 fps +void FCEUI_SetRegion(int region, int notify = 1); + +//Convenience function; returns currently emulated video system(0=NTSC, 1=PAL). +int FCEUI_GetCurrentVidSystem(int *slstart, int *slend); + +#ifdef FRAMESKIP +/* Should be called from FCEUD_BlitScreen(). Specifies how many frames + to skip until FCEUD_BlitScreen() is called. FCEUD_BlitScreenDummy() + will be called instead of FCEUD_BlitScreen() when when a frame is skipped. +*/ +void FCEUI_FrameSkip(int x); +#endif + +//First and last scanlines to render, for ntsc and pal emulation. +void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall); + +//Sets the base directory(save states, snapshots, etc. are saved in directories below this directory. +void FCEUI_SetBaseDirectory(std::string const & dir); + +void FCEUI_SetUserPalette(uint8 *pal, int nEntries); + +//Sets up sound code to render sound at the specified rate, in samples +//per second. Only sample rates of 44100, 48000, and 96000 are currently supported. +//If "Rate" equals 0, sound is disabled. +void FCEUI_Sound(int Rate); +void FCEUI_SetSoundVolume(uint32 volume); +void FCEUI_SetTriangleVolume(uint32 volume); +void FCEUI_SetSquare1Volume(uint32 volume); +void FCEUI_SetSquare2Volume(uint32 volume); +void FCEUI_SetNoiseVolume(uint32 volume); +void FCEUI_SetPCMVolume(uint32 volume); + +void FCEUI_SetSoundQuality(int quality); + +void FCEUD_SoundToggle(void); +void FCEUD_SoundVolumeAdjust(int); + +int FCEUI_SelectState(int, int); +extern void FCEUI_SelectStateNext(int); + +//"fname" overrides the default save state filename code if non-NULL. +void FCEUI_SaveState(const char *fname, bool display_message=true); +void FCEUI_LoadState(const char *fname, bool display_message=true); + +void FCEUD_SaveStateAs(void); +void FCEUD_LoadStateFrom(void); + +//at the minimum, you should call FCEUI_SetInput, FCEUI_SetInputFC, and FCEUI_SetInputFourscore +//you may also need to maintain your own internal state +void FCEUD_SetInput(bool fourscore, bool microphone, ESI port0, ESI port1, ESIFC fcexp); + + +void FCEUD_MovieRecordTo(void); +void FCEUD_MovieReplayFrom(void); +void FCEUD_LuaRunFrom(void); + +int32 FCEUI_GetDesiredFPS(void); +void FCEUI_SaveSnapshot(void); +void FCEUI_SaveSnapshotAs(void); +void FCEU_DispMessage(const char *format, int disppos, ...); +#define FCEUI_DispMessage FCEU_DispMessage + +int FCEUI_DecodePAR(const char *code, int *a, int *v, int *c, int *type); +int FCEUI_DecodeGG(const char *str, int *a, int *v, int *c); +int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type); +int FCEUI_DelCheat(uint32 which); +int FCEUI_ToggleCheat(uint32 which); +int FCEUI_GlobalToggleCheat(int global_enable); + +int32 FCEUI_CheatSearchGetCount(void); +void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, uint8 last, uint8 current)); +void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current, void *data), void *data); +void FCEUI_CheatSearchBegin(void); +void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2); +void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data); + +int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *compare, int *s, int *type); +int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int compare,int s, int type); + +void FCEUI_CheatSearchShowExcluded(void); +void FCEUI_CheatSearchSetCurrentAsOriginal(void); + +//.rom +#define FCEUIOD_ROMS 0 //Roms +#define FCEUIOD_NV 1 //NV = nonvolatile. save data. +#define FCEUIOD_STATES 2 //savestates +#define FCEUIOD_FDSROM 3 //disksys.rom +#define FCEUIOD_SNAPS 4 //screenshots +#define FCEUIOD_CHEATS 5 //cheats +#define FCEUIOD_MOVIES 6 //.fm2 files +#define FCEUIOD_MEMW 7 //memory watch fiels +#define FCEUIOD_BBOT 8 //basicbot, obsolete +#define FCEUIOD_MACRO 9 //macro files - old TASEdit v0.1 paradigm, not implemented, probably obsolete +#define FCEUIOD_INPUT 10 //input presets +#define FCEUIOD_LUA 11 //lua scripts +#define FCEUIOD_AVI 12 //default file for avi output +#define FCEUIOD__COUNT 13 //base directory override? + +void FCEUI_SetDirOverride(int which, char *n); + +void FCEUI_MemDump(uint16 a, int32 len, void (*callb)(uint16 a, uint8 v)); +uint8 FCEUI_MemSafePeek(uint16 A); +void FCEUI_MemPoke(uint16 a, uint8 v, int hl); +void FCEUI_NMI(void); +void FCEUI_IRQ(void); +uint16 FCEUI_Disassemble(void *XA, uint16 a, char *stringo); +void FCEUI_GetIVectors(uint16 *reset, uint16 *irq, uint16 *nmi); + +uint32 FCEUI_CRC32(uint32 crc, uint8 *buf, uint32 len); + +void FCEUI_SetLowPass(int q); + +void FCEUI_NSFSetVis(int mode); +int FCEUI_NSFChange(int amount); +int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen); + +void FCEUI_VSUniToggleDIPView(void); +void FCEUI_VSUniToggleDIP(int w); +uint8 FCEUI_VSUniGetDIPs(void); +void FCEUI_VSUniSetDIP(int w, int state); +void FCEUI_VSUniCoin(void); + +void FCEUI_FDSInsert(void); //mbg merge 7/17/06 changed to void fn(void) to make it an EMUCMDFN +//int FCEUI_FDSEject(void); +void FCEUI_FDSSelect(void); + +int FCEUI_DatachSet(const uint8 *rcode); + +///returns a flag indicating whether emulation is paused +int FCEUI_EmulationPaused(); +///returns a flag indicating whether a one frame step has been requested +int FCEUI_EmulationFrameStepped(); +///clears the framestepped flag. use it after youve stepped your one frame +void FCEUI_ClearEmulationFrameStepped(); +///sets the EmulationPaused flags +void FCEUI_SetEmulationPaused(int val); +///toggles the paused bit (bit0) for EmulationPaused. caused FCEUD_DebugUpdate() to fire if the emulation pauses +void FCEUI_ToggleEmulationPause(); + +//indicates whether input aids should be drawn (such as crosshairs, etc; usually in fullscreen mode) +bool FCEUD_ShouldDrawInputAids(); + +///called when the emulator closes a game +void FCEUD_OnCloseGame(void); + +void FCEUI_FrameAdvance(void); +void FCEUI_FrameAdvanceEnd(void); + +//AVI Output +int FCEUI_AviBegin(const char* fname); +void FCEUI_AviEnd(void); +void FCEUI_AviVideoUpdate(const unsigned char* buffer); +void FCEUI_AviSoundUpdate(void* soundData, int soundLen); +bool FCEUI_AviIsRecording(); +bool FCEUI_AviEnableHUDrecording(); +void FCEUI_SetAviEnableHUDrecording(bool enable); +bool FCEUI_AviDisableMovieMessages(); +void FCEUI_SetAviDisableMovieMessages(bool disable); + +void FCEUD_AviRecordTo(void); +void FCEUD_AviStop(void); + +///A callback that the emu core uses to poll the state of a given emulator command key +typedef int TestCommandState(int cmd); +///Signals the emu core to poll for emulator commands and take actions +void FCEUI_HandleEmuCommands(TestCommandState* testfn); + + +//Emulation speed +enum EMUSPEED_SET +{ + EMUSPEED_SLOWEST=0, + EMUSPEED_SLOWER, + EMUSPEED_NORMAL, + EMUSPEED_FASTER, + EMUSPEED_FASTEST +}; +void FCEUD_SetEmulationSpeed(int cmd); +void FCEUD_TurboOn(void); +void FCEUD_TurboOff(void); +void FCEUD_TurboToggle(void); + +int FCEUD_ShowStatusIcon(void); +void FCEUD_ToggleStatusIcon(void); +void FCEUD_HideMenuToggle(void); + +///signals the driver to perform a file open GUI operation +void FCEUD_CmdOpen(void); + +//new merge-era driver routines here: + +///signals that the cpu core hit a breakpoint. this function should not return until the core is ready for the next cycle +void FCEUD_DebugBreakpoint(int bp_num); + +///the driver should log the current instruction, if it wants (we should move the code in the win driver that does this to the shared area) +void FCEUD_TraceInstruction(uint8 *opcode, int size); + +///the driver might should update its NTView (only used if debugging support is compiled in) +void FCEUD_UpdateNTView(int scanline, bool drawall); + +///the driver might should update its PPUView (only used if debugging support is compiled in) +void FCEUD_UpdatePPUView(int scanline, int drawall); + +///I am dissatisfied with this method of getting an option from the driver to the core. but that is what we're using for now +bool FCEUD_PauseAfterPlayback(); + +///called when fceu changes something in the video system you might be interested in +void FCEUD_VideoChanged(); + +enum EFCEUI +{ + FCEUI_STOPAVI, FCEUI_QUICKSAVE, FCEUI_QUICKLOAD, FCEUI_SAVESTATE, FCEUI_LOADSTATE, + FCEUI_NEXTSAVESTATE,FCEUI_PREVIOUSSAVESTATE,FCEUI_VIEWSLOTS, + FCEUI_STOPMOVIE, FCEUI_RECORDMOVIE, FCEUI_PLAYMOVIE, + FCEUI_OPENGAME, FCEUI_CLOSEGAME, + FCEUI_TASEDITOR, + FCEUI_RESET, FCEUI_POWER, FCEUI_PLAYFROMBEGINNING, FCEUI_EJECT_DISK, FCEUI_SWITCH_DISK, FCEUI_INSERT_COIN, + FCEUI_TOGGLERECORDINGMOVIE, FCEUI_TRUNCATEMOVIE, FCEUI_INSERT1FRAME, FCEUI_DELETE1FRAME +}; + +//checks whether an EFCEUI is valid right now +bool FCEU_IsValidUI(EFCEUI ui); + +#ifdef __cplusplus +extern "C" +#endif +FILE *FCEUI_UTF8fopen_C(const char *n, const char *m); + +#endif //__DRIVER_H_ diff --git a/apps/fceux/src/drivers/common/args.cpp b/apps/fceux/src/drivers/common/args.cpp new file mode 100644 index 00000000..d7d29c94 --- /dev/null +++ b/apps/fceux/src/drivers/common/args.cpp @@ -0,0 +1,108 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains code for parsing command-line */ +/* options. */ +/* */ +/****************************************************************/ + +#include +#include +#include + +#include "../../types.h" +#include "args.h" + +int ParseEA(int x, int argc, char *argv[], ARGPSTRUCT *argsps) +{ + int y=0; + int ret=1; + + do + { + if(!argsps[y].name) + { + ParseEA(x,argc,argv,(ARGPSTRUCT*)argsps[y].var); + y++; + continue; + } + if(!strcmp(argv[x],argsps[y].name)) // A match. + { + //ret++; + if(argsps[y].subs) + { + if((x+1)>=argc) + break; + ret++; + if(argsps[y].substype&0x2000) + { + ((void (*)(char *))argsps[y].subs)(argv[x+1]); + } + else if(argsps[y].substype&0x8000) + { + *(int *)argsps[y].subs&=~(argsps[y].substype&(~0x8000)); + *(int *)argsps[y].subs|=atoi(argv[x+1])?(argsps[y].substype&(~0x8000)):0; + } + else + switch(argsps[y].substype&(~0x4000)) + { + case 0: // Integer + *(int *)argsps[y].subs=atoi(argv[x+1]); + break; + case 2: // Double float + *(double *)argsps[y].subs=atof(argv[x+1]); + break; + case 1: // String + if(argsps[y].substype&0x4000) + { + if(*(char **)argsps[y].subs) + free(*(char **)argsps[y].subs); + if(!( *(char **)argsps[y].subs=(char*)malloc(strlen(argv[x+1])+1) )) + break; + } + strcpy(*(char **)argsps[y].subs,argv[x+1]); + break; + } + } + if(argsps[y].var) + *argsps[y].var=1; + } + y++; + } while(argsps[y].var || argsps[y].subs); + return ret; +} + +int ParseArguments(int argc, char *argv[], ARGPSTRUCT *argsps) +{ + int x; + + for(x=0;x +#include +#include "../../driver.h" +#include "../../fceu.h" + +static void GetString(char *s, int max) +{ + int x; + fgets(s,max,stdin); + + for(x=0;x "); + fgets(buf,ARRAY_SIZE(buf),stdin); + if(buf[0]=='s' || buf[0]=='S') return(-1); + if(buf[0]=='\n') return(0); + if(!sscanf(buf,"%d",&num)) + return(0); + if(num<1) goto tryagain; + return(num); + } + else + { + int num=0; + + tryagain2: + printf(" <'Enter' to make no selection or enter a number.> "); + fgets(buf,ARRAY_SIZE(buf),stdin); + if(buf[0]=='\n') return(0); + if(!sscanf(buf,"%d",&num)) + return(0); + if(num<1) goto tryagain2; + return(num); + } +} + +int EndListShow(void) +{ + if(mordoe) + { + int r=ListChoice(1); + if(r>0 && r<=listcount) + listsel=listids[r-1]; + } + return(listsel); +} + +/* Returns 0 to stop listing, 1 to continue. */ +int AddToList(char *text, uint32 id) +{ + if(listcount==16) + { + int t=ListChoice(0); + mordoe=0; + if(t==-1) return(0); // Stop listing. + else if(t>0 && t<17) + { + listsel=listids[t-1]; + return(0); + } + listcount=0; + } + mordoe=1; + listids[listcount]=id; + printf("%2d) %s\n",listcount+1,text); + listcount++; + return(1); +} + +/* +** +** End list code. +**/ + +typedef struct MENU { + const char *text; + void *action; + int type; // 0 for menu, 1 for function. +} MENU; + +static void SetOC(void) +{ + FCEUI_CheatSearchSetCurrentAsOriginal(); +} + +static void UnhideEx(void) +{ + FCEUI_CheatSearchShowExcluded(); +} + +static void ToggleCheat(int num) +{ + printf("Cheat %d %sabled.\n",1+num, + FCEUI_ToggleCheat(num)?"en":"dis"); +} + +static void ModifyCheat(int num) +{ + char *name; + char buf[256]; + uint32 A; + uint8 V; + int compare; + int type; + + int s; + int t; + + FCEUI_GetCheat(num, &name, &A, &V, &compare, &s, &type); + + printf("Name [%s]: ",name); + GetString(buf,256); + + /* This obviously doesn't allow for cheats with no names. Bah. Who wants + nameless cheats anyway... + */ + + if(buf[0]) + name=buf; // Change name when FCEUI_SetCheat() is called. + else + name=0; // Don't change name when FCEUI_SetCheat() is called. + + printf("Address [$%04x]: ",(unsigned int)A); + A=GetH16(A); + + printf("Value [%03d]: ",(unsigned int)V); + V=Get8(V); + + printf("Compare [%3d]: ",compare); + compare=GetI(compare); + + printf("Type(0=Old Style, 1=Read Substitute) [%1d]: ",type); + type=GetI(type)?1:0; + + printf("Enable [%s]: ",s?"Y":"N"); + t=getchar(); + if(t=='Y' || t=='y') s=1; + else if(t=='N' || t=='n') s=0; + + FCEUI_SetCheat(num,name,A,V,compare,s,type); +} + + +static void AddCheatGGPAR(int which) +{ + int A, V, C; + int type; + char name[256],code[256]; + + printf("Name: "); + GetString(name,256); + + printf("Code: "); + GetString(code,256); + + printf("Add cheat \"%s\" for code \"%s\"?",name,code); + if(GetYN(0)) + { + if(which) + { + if(!FCEUI_DecodePAR(code,&A,&V,&C,&type)) + { + puts("Invalid Game Genie code."); + return; + } + } + else + { + if(!FCEUI_DecodeGG(code,&A,&V,&C)) + { + puts("Invalid Game Genie code."); + return; + } + type=1; + } + + if(FCEUI_AddCheat(name,A,V,C,type)) + puts("Cheat added."); + else + puts("Error adding cheat."); + } +} + +static void AddCheatGG(void) +{ + AddCheatGGPAR(0); +} + +static void AddCheatPAR(void) +{ + AddCheatGGPAR(1); +} + +static void AddCheatParam(uint32 A, uint8 V) +{ + char name[256]; + + printf("Name: "); + GetString(name,256); + printf("Address [$%04x]: ",(unsigned int)A); + A=GetH16(A); + printf("Value [%03d]: ",(unsigned int)V); + V=Get8(V); + printf("Add cheat \"%s\" for address $%04x with value %03d?",name,(unsigned int)A,(unsigned int)V); + if(GetYN(0)) + { + if(FCEUI_AddCheat(name,A,V,-1,0)) + puts("Cheat added."); + else + puts("Error adding cheat."); + } +} + +static void AddCheat(void) +{ + AddCheatParam(0,0); +} + +static int lid; +static int clistcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data) +{ + char tmp[512]; + int ret; + + if(compare>=0) + sprintf(tmp,"%s $%04x:%03d:%03d - %s",s?"*":" ",(unsigned int)a,(unsigned int)v,compare,name); + else + sprintf(tmp,"%s $%04x:%03d - %s",s?"*":" ",(unsigned int)a,(unsigned int)v,name); + if(type==1) + tmp[2]='S'; + ret=AddToList(tmp,lid); + lid++; + return(ret); +} + +static void ListCheats(void) +{ + int which; + lid=0; + + BeginListShow(); + FCEUI_ListCheats(clistcallb,0); + which=EndListShow(); + if(which>=0) + { + char tmp[32]; + printf(" <(T)oggle status, (M)odify, or (D)elete this cheat.> "); + fgets(tmp,ARRAY_SIZE(tmp),stdin); + switch(tolower(tmp[0])) + { + case 't':ToggleCheat(which); + break; + case 'd':if(!FCEUI_DelCheat(which)) + puts("Error deleting cheat!"); + else + puts("Cheat has been deleted."); + break; + case 'm':ModifyCheat(which); + break; + } + } +} + +static void ResetSearch(void) +{ + FCEUI_CheatSearchBegin(); + puts("Done."); +} + +static int srescallb(uint32 a, uint8 last, uint8 current, void *data) +{ + char tmp[14]; + sprintf(tmp, "$%04x:%03d:%03d",(unsigned int)a,(unsigned int)last,(unsigned int)current); + return(AddToList(tmp,a)); +} + +static void ShowRes(void) +{ + int n=FCEUI_CheatSearchGetCount(); + printf(" %d results:\n",n); + if(n) + { + int which; + BeginListShow(); + FCEUI_CheatSearchGet(srescallb,0); + which=EndListShow(); + if(which>=0) + AddCheatParam(which,0); + } +} + +static int ShowShortList(const char *moe[], int n, int def) +{ + int x,c; + int baa; //mbg merge 7/17/06 made to normal int + char tmp[256]; + + red: + for(x=0;x ",def+1); + fgets(tmp,ARRAY_SIZE(tmp),stdin); + if(tmp[0]=='\n') + return def; + c=tolower(tmp[0]); + baa=c-'1'; + + if(baaC)", + "Value increased by V2 (|C-O|==V2)", + "Value decreased by V2 (|O-C|==V2)"}; + int av[9]={ASK_V1|ASK_V2, + ASK_V1|ASK_V2, + ASK_V2, + ASK_NONE, + ASK_V1, + ASK_NONE, + ASK_NONE, + ASK_V2, + ASK_V2}; + + printf("\nSearch Filter:\n"); + + method=ShowShortList(m,9,method); + if(av[method]&ASK_V1) + { + printf("V1 [%03d]: ",v1); + v1=Get8(v1); + } + if(av[method]&ASK_V2) + { + printf("V2 [%03d]: ",v2); + v2=Get8(v2); + } + FCEUI_CheatSearchEnd(method,v1,v2); + puts("Search completed.\n"); +} + + +static MENU NewCheatsMenu[]={ + {"Add Cheat",(void *)AddCheat,1}, + {"Reset Search",(void *)ResetSearch,1}, + {"Do Search",(void *)DoSearch,1}, + {"Set Original to Current",(void *)SetOC,1}, + {"Unhide Excluded",(void *)UnhideEx,1}, + {"Show Results",(void *)ShowRes,1}, + {"Add Game Genie Cheat",(void *)AddCheatGG,1}, + {"Add PAR Cheat",(void *)AddCheatPAR,1}, + {0} +}; + +static MENU MainMenu[]={ + {"List Cheats",(void *)ListCheats,1}, + {"New Cheats...",(void *)NewCheatsMenu,0}, + {0} +}; + +static void DoMenu(MENU *men) +{ + int x=0; + + redisplay: + x=0; + puts(""); + while(men[x].text) + { + printf("%d) %s\n",x+1,men[x].text); + x++; + } + puts("D) Display Menu\nX) Return to Previous\n"); + { + char buf[32]; + int c; + + recommand: + printf("Command> "); + fgets(buf,ARRAY_SIZE(buf),stdin); + c=tolower(buf[0]); + if(c=='\n') + goto recommand; + else if(c=='d') + goto redisplay; + else if(c=='x') + { + return; + } + else if(sscanf(buf,"%d",&c)) + { + if(c>x) goto invalid; + if(men[c-1].type) + { + void (*func)(void)=(void(*)())men[c-1].action; + func(); + } + else + DoMenu((MENU*)men[c-1].action); /* Mmm...recursivey goodness. */ + goto redisplay; + } + else + { + invalid: + puts("Invalid command.\n"); + goto recommand; + } + + } +} + +void DoConsoleCheatConfig(void) +{ + MENU *curmenu=MainMenu; + + DoMenu(curmenu); +} diff --git a/apps/fceux/src/drivers/common/cheat.h b/apps/fceux/src/drivers/common/cheat.h new file mode 100644 index 00000000..d8dc8f60 --- /dev/null +++ b/apps/fceux/src/drivers/common/cheat.h @@ -0,0 +1,23 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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 + */ +#ifndef COMMON_CHEAT_H +#define COMMON_CHEAT_H +void DoConsoleCheatConfig(void); +#endif \ No newline at end of file diff --git a/apps/fceux/src/drivers/common/config.cpp b/apps/fceux/src/drivers/common/config.cpp new file mode 100644 index 00000000..7f1157e1 --- /dev/null +++ b/apps/fceux/src/drivers/common/config.cpp @@ -0,0 +1,374 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Xodnizel + * + * 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. + *fs + * 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 + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains routines for reading/writing the */ +/* configuration file. */ +/* */ +/****************************************************************/ + +#include +#include +#include + +#include "../../types.h" +#include "../../driver.h" +#include "../../utils/xstring.h" +#include "config.h" + +static int FReadString(FILE *fp, char *str, int n) +{ + int x=0,z; + for(;;) + { + z=fgetc(fp); + str[x]=z; + x++; + if(z<=0) break; + if(x>=n) return 0; + } + if(z<0) return 0; + return 1; +} + +#include +#include +typedef std::map CFGMAP; +static CFGMAP cfgmap; + +static void cfg_Parse(FILE *fp) +{ + //yes... it is a homebrewed key-value-pair parser + std::string key,value; + enum { + NEWLINE, KEY, SEPARATOR, VALUE, COMMENT + } state = NEWLINE; + bool bail = false; + for(;;) + { + int c = fgetc(fp); + bool iswhitespace, iscommentchar, isnewline; + if(c == -1) + goto bail; + iswhitespace = (c==' '||c=='\t'); + iscommentchar = (c=='#'); + isnewline = (c==10||c==13); + switch(state) + { + case NEWLINE: + if(iswhitespace) goto done; + if(iscommentchar) goto docomment; + if(isnewline) goto done; + key = ""; + value = ""; + goto dokey; + break; + case COMMENT: + docomment: + state = COMMENT; + if(isnewline) state = NEWLINE; + break; + case KEY: + dokey: //dookie + state = KEY; + if(iswhitespace) goto doseparator; + if(isnewline) goto commit; + key += c; + break; + case SEPARATOR: + doseparator: + state = SEPARATOR; + if(isnewline) goto commit; + if(!iswhitespace) goto dovalue; + break; + case VALUE: + dovalue: + state = VALUE; + if(isnewline) goto commit; + value += c; + } + goto done; + + bail: + bail = true; + if(state == VALUE) goto commit; + commit: + cfgmap[key] = value; + state = NEWLINE; + if(bail) break; + done: ; + } +} + +static void cfg_Save(FILE *fp) +{ + for(CFGMAP::iterator it(cfgmap.begin()); it != cfgmap.end(); it++) + { + if(it->first.size()>30 || it->second.size()>30) + { + //int zzz=9; + } + fprintf(fp,"%s %s\n",it->first.c_str(),it->second.c_str()); + } +} + + +static void GetValueR(FILE *fp, char *str, void *v, int c) +{ + char buf[256]; + int s; + + while(FReadString(fp,buf,256)) + { + fread(&s,1,4,fp); + + if(!strcmp(str, buf)) + { + if(!c) // String, allocate some memory. + { + if(!(*(char **)v=(char*)malloc(s))) + goto gogl; + + fread(*(char **)v,1,s,fp); + continue; + } + else if(s>c || s +#include +#include +#include +#include +#include + +#include "../../types.h" +#include "configSys.h" + +std::string cfgFile = "fceux.cfg"; +/** + * Add a given option. The option is specified as a short command + * line (-f), long command line (--foo), option name (Foo), its type + * (integer or string). + */ +int +Config::_addOption(char shortArg, + const std::string &longArg, + const std::string &name, + int type) +{ + // make sure we have a valid type + if(type != INTEGER && type != STRING && + type != DOUBLE && type != FUNCTION) { + return -1; + } + + // check if the option already exists + if(_shortArgMap.find(shortArg) != _shortArgMap.end() || + _longArgMap.find(longArg) != _longArgMap.end() || + (type == INTEGER && _intOptMap.find(name) != _intOptMap.end()) || + (type == STRING && _strOptMap.find(name) != _strOptMap.end()) || + (type == DOUBLE && _dblOptMap.find(name) != _dblOptMap.end())) { + return -1; + } + + // add the option + switch(type) { + case(STRING): + _strOptMap[name] = ""; + break; + case(INTEGER): + _intOptMap[name] = 0; + break; + case(DOUBLE): + _dblOptMap[name] = 0.0; + break; + case(FUNCTION): + _fnOptMap[name] = NULL; + break; + default: + break; + } + _shortArgMap[shortArg] = name; + _longArgMap[longArg] = name; + + return 0; +} + +int +Config::_addOption(const std::string &longArg, + const std::string &name, + int type) +{ + // make sure we have a valid type + if(type != STRING && type != INTEGER && type != DOUBLE) { + return -1; + } + + // check if the option already exists + if(_longArgMap.find(longArg) != _longArgMap.end() || + (type == STRING && _strOptMap.find(name) != _strOptMap.end()) || + (type == INTEGER && _intOptMap.find(name) != _intOptMap.end()) || + (type == DOUBLE && _dblOptMap.find(name) != _dblOptMap.end())) { + return -1; + } + + // add the option + switch(type) { + case(STRING): + _strOptMap[name] = ""; + break; + case(INTEGER): + _intOptMap[name] = 0; + break; + case(DOUBLE): + _dblOptMap[name] = 0.0; + break; + default: + break; + } + _longArgMap[longArg] = name; + + return 0; +} + + +/** + * Add a given option and sets its default value. The option is + * specified as a short command line (-f), long command line (--foo), + * option name (Foo), its type (integer or string), and its default + * value. + */ +int +Config::addOption(char shortArg, + const std::string &longArg, + const std::string &name, + int defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(shortArg, longArg, name, INTEGER); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + return 0; +} + +/** + * Add a given option and sets its default value. The option is + * specified as a short command line (-f), long command line (--foo), + * option name (Foo), its type (integer or string), and its default + * value. + */ +int +Config::addOption(char shortArg, + const std::string &longArg, + const std::string &name, + double defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(shortArg, longArg, name, DOUBLE); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + return 0; +} + + +/** + * Add a given option and sets its default value. The option is + * specified as a short command line (-f), long command line (--foo), + * option name (Foo), its type (integer or string), and its default + * value. + */ +int +Config::addOption(char shortArg, + const std::string &longArg, + const std::string &name, + const std::string &defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(shortArg, longArg, name, STRING); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + return 0; +} + +int +Config::addOption(char shortArg, + const std::string &longArg, + const std::string &name, + void (*defaultFn)(const std::string &)) +{ + int error; + + // add the option to the config system + error = _addOption(shortArg, longArg, name, FUNCTION); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultFn); + if(error) { + return error; + } + + return 0; +} + +int +Config::addOption(const std::string &longArg, + const std::string &name, + const std::string &defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(longArg, name, STRING); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + return 0; +} + +int +Config::addOption(const std::string &longArg, + const std::string &name, + int defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(longArg, name, INTEGER); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + return 0; +} + +int +Config::addOption(const std::string &longArg, + const std::string &name, + double defaultValue) +{ + int error; + + // add the option to the config system + error = _addOption(longArg, name, DOUBLE); + if(error) { + return error; + } + + // set the option to the default value + error = setOption(name, defaultValue); + if(error) { + return error; + } + + return 0; +} + +int +Config::addOption(const std::string &name, + const std::string &defaultValue) +{ + if(_strOptMap.find(name) != _strOptMap.end()) { + return -1; + } + + // add the option + _strOptMap[name] = defaultValue; + return 0; +} + +int +Config::addOption(const std::string &name, + int defaultValue) +{ + if(_intOptMap.find(name) != _intOptMap.end()) { + return -1; + } + + // add the option + _intOptMap[name] = defaultValue; + return 0; +} + +int +Config::addOption(const std::string &name, + double defaultValue) +{ + if(_dblOptMap.find(name) != _dblOptMap.end()) { + return -1; + } + + // add the option + _dblOptMap[name] = defaultValue; + return 0; +} + +/** + * Sets the specified option to the given integer value. + */ +int +Config::setOption(const std::string &name, + int value) +{ + std::map::iterator opt_i; + + // confirm that the option exists + opt_i = _intOptMap.find(name); + if(opt_i == _intOptMap.end()) { + return -1; + } + + // set the option + opt_i->second = value; + return 0; +} + +/** + * Sets the specified option to the given integer value. + */ +int +Config::setOption(const std::string &name, + double value) +{ + std::map::iterator opt_i; + + // confirm that the option exists + opt_i = _dblOptMap.find(name); + if(opt_i == _dblOptMap.end()) { + return -1; + } + + // set the option + opt_i->second = value; + return 0; +} + +/** + * Sets the specified option to the given string value. + */ +int +Config::setOption(const std::string &name, + const std::string &value) +{ + std::map::iterator opt_i; + + // confirm that the option exists + opt_i = _strOptMap.find(name); + if(opt_i == _strOptMap.end()) { + return -1; + } + + // set the option + opt_i->second = value; + return 0; +} + +/** + * Sets the specified option to the given function. + */ +int +Config::setOption(const std::string &name, + void (*value)(const std::string &)) +{ + std::map::iterator opt_i; + + // confirm that the option exists + opt_i = _fnOptMap.find(name); + if(opt_i == _fnOptMap.end()) { + return -1; + } + + // set the option + opt_i->second = value; + return 0; +} + + +int +Config::getOption(const std::string &name, + std::string *value) const +{ + std::map::const_iterator opt_i; + + // confirm that the option exists + opt_i = _strOptMap.find(name); + if(opt_i == _strOptMap.end()) { + return -1; + } + + // get the option + (*value) = opt_i->second; + return 0; +} + +int +Config::getOption(const std::string &name, + const char **value) const +{ + std::map::const_iterator opt_i; + + // confirm that the option exists + opt_i = _strOptMap.find(name); + if(opt_i == _strOptMap.end()) { + return -1; + } + + // get the option + (*value) = opt_i->second.c_str(); + return 0; +} + +int +Config::getOption(const std::string &name, + int *value) const +{ + std::map::const_iterator opt_i; + + // confirm that the option exists + opt_i = _intOptMap.find(name); + if(opt_i == _intOptMap.end()) { + return -1; + } + + // get the option + (*value) = opt_i->second; + return 0; +} + +int +Config::getOption(const std::string &name, + double *value) const +{ + std::map::const_iterator opt_i; + + // confirm that the option exists + opt_i = _dblOptMap.find(name); + if(opt_i == _dblOptMap.end()) { + return -1; + } + + // get the option + (*value) = opt_i->second; + return 0; +} + +/** + * Parses the command line arguments. Short args are of the form -f + * , long args are of the form --foo . Returns < 0 on error, + * or the index of the rom file in argv. + */ +int +Config::_parseArgs(int argc, + char **argv) +{ + int retval = 0; + std::map::iterator long_i, str_i; + std::map::iterator short_i; + std::map::iterator int_i; + std::map::iterator dbl_i; + std::map::iterator fn_i; + std::string arg, opt, value; + + for(int i = 1; i < argc; i++) { + arg = argv[i]; + if(arg[0] != '-') { + // must be a rom name? + retval = i; + continue; + } + + if(arg.size() < 2) { + // XXX invalid argument + return -1; + } + + // parse the argument and get the option name + if(arg[1] == '-') { + // long arg + long_i = _longArgMap.find(arg.substr(2)); + if(long_i == _longArgMap.end()) { + // XXX invalid argument + return -1; + } + + opt = long_i->second; + } else { + // short arg + short_i = _shortArgMap.find(arg[1]); + if(short_i == _shortArgMap.end()) { + // XXX invalid argument + return -1; + } + + opt = short_i->second; + } + + // make sure we've got a value + if(i + 1 >= argc) { + // XXX missing value + return -1; + } + i++; + + // now, find the appropriate option entry, and update it + str_i = _strOptMap.find(opt); + int_i = _intOptMap.find(opt); + dbl_i = _dblOptMap.find(opt); + fn_i = _fnOptMap.find(opt); + if(str_i != _strOptMap.end()) { + str_i->second = argv[i]; + } else if(int_i != _intOptMap.end()) { + int_i->second = atol(argv[i]); + } else if(dbl_i != _dblOptMap.end()) { + dbl_i->second = atof(argv[i]); + } else if(fn_i != _fnOptMap.end()) { + (*(fn_i->second))(argv[i]); + } else { + // XXX invalid option? shouldn't happen + return -1; + } + } + + // if we didn't get a rom-name, return error + return (retval) ? retval : -1; +} + + +/** + * Parses first the configuration file, and then overrides with any + * command-line options that were specified. + */ +int +Config::parse(int argc, + char **argv) +{ + int error; + + // read the config file + error = _load(); + if(error) { + return error; + } + + // try to read cfg.d/* + std::string cfgd_dir_name = _dir + "/" + "cfg.d/"; + DIR *d; + struct dirent *dir; + d = opendir(cfgd_dir_name.c_str()); + if (d) + { + while ((dir = readdir(d)) != NULL) + { + // dont load "." or ".." + if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) + { + continue; + } + + // TODO 0 = good -1 = bad + std::string fname = cfgd_dir_name + dir->d_name; + printf("Loading auxilary configuration file at %s...\n", fname.c_str()); + if (_loadFile(fname.c_str()) != 0) + { + printf("Failed to parse configuration at %s\n", fname.c_str()); + } + } + + closedir(d); + } + + // parse the arguments + return _parseArgs(argc, argv); +} + + +/** + * Read each line of the config file and put the variables into the + * config maps. Valid configuration lines are of the form: + * + *

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%0AMHlNlha41N7qNjj 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ð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