From c62d28102c77e19c291c78bf6bf7f0a81abd54b9 Mon Sep 17 00:00:00 2001 From: notaz Date: Sat, 7 Apr 2007 13:46:21 +0000 Subject: [PATCH] initial fce ultra 0.81 import git-svn-id: file:///home/notaz/opt/svn/fceu@90 be3aeb3a-fb24-0410-a615-afba39da0efa --- Documentation/AUTHORS | 25 + Documentation/COPYING | 341 +++ Documentation/ChangeLog | 823 ++++++++ Documentation/FAQ | 45 + Documentation/README | 31 + Documentation/RELEASE-NOTES | 36 + Documentation/TODO | 60 + Documentation/cheat.txt | 249 +++ Documentation/fcs.txt | 143 ++ Documentation/porting.txt | 229 ++ Documentation/rel/1.0 | 178 ++ Documentation/rel/1.1.dos | 22 + Documentation/rel/1.1.linux | 22 + Documentation/rel/1.1.win | 26 + Documentation/rel/2.0.dos | 93 + Documentation/rel/2.0.linux | 109 + Documentation/rel/2.0.win | 8 + Documentation/rel/2.1.dos | 59 + Documentation/rel/2.1.linux | 62 + Documentation/rel/2.1.win | 125 ++ Documentation/rel/3.0.dos | 19 + Documentation/rel/3.0.linux | 37 + Documentation/rel/3.0.win | 11 + Documentation/rel/3.1 | 20 + Documentation/rel/3.2 | 28 + Documentation/rel/3.3 | 18 + Documentation/rel/3.4 | 23 + Documentation/rel/3.5 | 35 + Documentation/rel/3.6 | 19 + Documentation/rel/3.7 | 15 + Documentation/rel/4.0 | 12 + Documentation/rel/4.1 | 36 + Documentation/rel/d | 5 + Documentation/rel/da.sh | 3 + Documentation/rel/new | 56 + Documentation/rel/new.dos | 5 + Documentation/rel/new.linux | 6 + Documentation/rel/new.win | 0 Documentation/rel/readme-dos.txt | 646 ++++++ Documentation/rel/readme-linux.txt | 707 +++++++ Documentation/rel/readme-win.txt | 641 ++++++ Documentation/rel/toc | 23 + Documentation/rel/top.dos | 8 + Documentation/rel/top.linux | 8 + Documentation/rel/top.win | 8 + Documentation/tech/README.now | 6 + Documentation/tech/README.sound | 2 + Documentation/tech/UNIF_current.txt | 302 +++ Documentation/tech/cpu/4017.txt | 97 + Documentation/tech/cpu/6502_cpu.txt | 1537 ++++++++++++++ Documentation/tech/cpu/NESSOUND.txt | 697 +++++++ Documentation/tech/cpu/dmc.txt | 235 +++ Documentation/tech/exp/mmc5-e.txt | 250 +++ Documentation/tech/exp/mmc5_bank_switch.txt | 128 ++ Documentation/tech/nsfspec.txt | 336 +++ Documentation/tech/ppu/loopy1.txt | 63 + Documentation/tech/ppu/loopy2.txt | 33 + Documentation/tech/ppu/timing.txt | 266 +++ Makefile.base | 42 + Makefile.beos | 23 + Makefile.common | 6 + Makefile.dos | 23 + Makefile.linuxvga | 22 + Makefile.unixsdl | 25 + Makefile.win | 22 + banksw.h | 101 + boards/Makefile | 9 + boards/h2288.c | 99 + boards/malee.c | 45 + boards/mapinc.h | 11 + boards/novel.c | 53 + boards/sachen.c | 289 +++ boards/simple.c | 162 ++ boards/super24.c | 236 +++ boards/supervision.c | 76 + cart.c | 590 ++++++ cart.h | 70 + cheat.c | 565 +++++ cheat.h | 6 + crc32.c | 100 + crc32.h | 6 + debug.c | 35 + debug.h | 2 + drawing.h | 154 ++ driver.h | 174 ++ drivers/cli/dface.h | 32 + drivers/cli/dos-joystick.c | 190 ++ drivers/cli/dos-joystick.h | 27 + drivers/cli/dos-keyboard.c | 131 ++ drivers/cli/dos-mouse.c | 77 + drivers/cli/dos-sound.c | 567 +++++ drivers/cli/dos-sound.h | 26 + drivers/cli/dos-video.c | 246 +++ drivers/cli/dos-video.h | 22 + drivers/cli/dos.c | 128 ++ drivers/cli/dos.h | 28 + drivers/cli/input.c | 340 +++ drivers/cli/keyscan.h | 166 ++ drivers/cli/lnx-joystick.c | 201 ++ drivers/cli/lnx-joystick.h | 3 + drivers/cli/main.c | 376 ++++ drivers/cli/main.h | 30 + drivers/cli/sdl-joystick.c | 200 ++ drivers/cli/sdl-netplay.c | 163 ++ drivers/cli/sdl-netplay.h | 5 + drivers/cli/sdl-sound.c | 148 ++ drivers/cli/sdl-video.c | 228 ++ drivers/cli/sdl-video.h | 1 + drivers/cli/sdl.c | 215 ++ drivers/cli/sdl.h | 47 + drivers/cli/svga-video.c | 497 +++++ drivers/cli/svga-video.h | 32 + drivers/cli/svgalib.c | 178 ++ drivers/cli/svgalib.h | 2 + drivers/cli/throttle.c | 41 + drivers/cli/throttle.h | 2 + drivers/cli/unix-basedir.h | 15 + drivers/cli/unix-netplay.c | 158 ++ drivers/cli/unix-netplay.h | 5 + drivers/cli/usage.h | 56 + drivers/cli/vgatweak.c | 168 ++ drivers/common/args.c | 91 + drivers/common/args.h | 30 + drivers/common/cheat.c | 448 ++++ drivers/common/cheat.h | 21 + drivers/common/config.c | 151 ++ drivers/common/config.h | 57 + drivers/common/unixdsp.c | 123 ++ drivers/common/unixdsp.h | 23 + drivers/common/vidblit.c | 229 ++ drivers/common/vidblit.h | 25 + drivers/win/cheat.c | 471 +++++ drivers/win/cheat.h | 1 + drivers/win/common.h | 20 + drivers/win/config.c | 132 ++ drivers/win/input.c | 288 +++ drivers/win/input.h | 25 + drivers/win/joystick.c | 407 ++++ drivers/win/joystick.h | 16 + drivers/win/keyboard.c | 458 ++++ drivers/win/keyboard.h | 22 + drivers/win/keyscan.h | 125 ++ drivers/win/main.c | 338 +++ drivers/win/netplay.c | 415 ++++ drivers/win/res.res | Bin 0 -> 27108 bytes drivers/win/sound.c | 457 ++++ drivers/win/throttle.c | 73 + drivers/win/video.c | 1174 +++++++++++ drivers/win/wave.c | 125 ++ drivers/win/window.c | 855 ++++++++ endian.c | 71 + endian.h | 3 + fce.c | 1565 ++++++++++++++ fce.h | 81 + fceline.h | 112 + fds.c | 709 +++++++ fds.h | 8 + file.c | 407 ++++ file.h | 12 + general.c | 169 ++ general.h | 14 + git.h | 22 + ines.c | 860 ++++++++ ines.h | 366 ++++ input.c | 330 +++ input.h | 23 + input/Makefile | 8 + input/arkanoid.c | 117 ++ input/cursor.c | 45 + input/fkb.c | 102 + input/fkb.h | 72 + input/powerpad.c | 60 + input/shadow.c | 127 ++ input/share.h | 7 + input/zapper.c | 143 ++ mappers/105.c | 131 ++ mappers/112.c | 52 + mappers/113.c | 47 + mappers/117.c | 67 + mappers/15.c | 87 + mappers/151.c | 41 + mappers/16.c | 129 ++ mappers/17.c | 74 + mappers/18.c | 80 + mappers/180.c | 14 + mappers/182.c | 48 + mappers/184.c | 15 + mappers/189.c | 37 + mappers/19.c | 286 +++ mappers/21.c | 102 + mappers/22.c | 63 + mappers/225.c | 87 + mappers/226.c | 105 + mappers/227.c | 79 + mappers/228.c | 52 + mappers/229.c | 48 + mappers/23.c | 102 + mappers/232.c | 50 + mappers/234.c | 107 + mappers/240.c | 39 + mappers/242.c | 41 + mappers/245.c | 51 + mappers/246.c | 44 + mappers/248.c | 90 + mappers/249.c | 46 + mappers/24and26.c | 351 ++++ mappers/25.c | 95 + mappers/32.c | 54 + mappers/33.c | 63 + mappers/40.c | 58 + mappers/41.c | 51 + mappers/42.c | 63 + mappers/43.c | 63 + mappers/46.c | 48 + mappers/51.c | 66 + mappers/6.c | 76 + mappers/64.c | 150 ++ mappers/65.c | 68 + mappers/67.c | 67 + mappers/68.c | 104 + mappers/69.c | 268 +++ mappers/71.c | 41 + mappers/72.c | 37 + mappers/73.c | 57 + mappers/75.c | 47 + mappers/76.c | 55 + mappers/77.c | 54 + mappers/79.c | 40 + mappers/8.c | 38 + mappers/80.c | 49 + mappers/82.c | 62 + mappers/83.c | 113 + mappers/85.c | 140 ++ mappers/86.c | 32 + mappers/88.c | 56 + mappers/89.c | 34 + mappers/90.c | 154 ++ mappers/92.c | 45 + mappers/95.c | 76 + mappers/97.c | 42 + mappers/99.c | 35 + mappers/Makefile | 74 + mappers/fmopl.c | 871 ++++++++ mappers/fmopl.h | 149 ++ mappers/mapinc.h | 12 + mappers/mapshare.h | 5 + mappers/mmc2and4.c | 121 ++ mappers/simple.c | 299 +++ mappers/vrc7snd.c | 190 ++ mbshare/Makefile | 5 + mbshare/mapinc.h | 14 + mbshare/mmc1.c | 353 ++++ mbshare/mmc3.c | 649 ++++++ mbshare/mmc5.c | 758 +++++++ memory.c | 67 + memory.h | 29 + netplay.c | 98 + netplay.h | 7 + nsf.c | 410 ++++ nsf.h | 53 + nsfbgnew.h | 2062 +++++++++++++++++++ ops.h | 479 +++++ palette.h | 120 ++ palettes/nsfnew.h | 39 + palettes/rp2c04001.h | 64 + palettes/vscv.h | 64 + palettes/vseb.h | 64 + palettes/vsgoonies.h | 65 + palettes/vsgrad.h | 65 + palettes/vsmar.h | 64 + palettes/vsplatoon.h | 64 + palettes/vsslalom.h | 64 + palettes/vssmb.h | 65 + sound.c | 1029 +++++++++ sound.h | 73 + state.c | 607 ++++++ state.h | 39 + svga.c | 567 +++++ svga.h | 83 + types.h | 66 + unif.c | 469 +++++ unif.h | 94 + version.h | 29 + video.c | 261 +++ video.h | 3 + x6502.c | 484 +++++ x6502.h | 66 + zlib/ChangeLog | 481 +++++ zlib/Makefile | 25 + zlib/adler32.c | 48 + zlib/algorithm.txt | 213 ++ zlib/compress.c | 68 + zlib/crc32.c | 162 ++ zlib/deflate.c | 1350 ++++++++++++ zlib/deflate.h | 318 +++ zlib/descrip.mms | 48 + zlib/example.c | 556 +++++ zlib/faq | 72 + zlib/gzio.c | 875 ++++++++ zlib/infblock.c | 403 ++++ zlib/infblock.h | 39 + zlib/infcodes.c | 251 +++ zlib/infcodes.h | 27 + zlib/inffast.c | 183 ++ zlib/inffast.h | 17 + zlib/inffixed.h | 151 ++ zlib/inflate.c | 366 ++++ zlib/inftrees.c | 454 ++++ zlib/inftrees.h | 58 + zlib/infutil.c | 87 + zlib/infutil.h | 98 + zlib/maketree.c | 85 + zlib/readme | 148 ++ zlib/trees.c | 1214 +++++++++++ zlib/trees.h | 128 ++ zlib/uncompr.c | 58 + zlib/unzip.c | 1301 ++++++++++++ zlib/unzip.h | 275 +++ zlib/zconf.h | 279 +++ zlib/zlib.h | 893 ++++++++ zlib/zutil.c | 225 ++ zlib/zutil.h | 220 ++ 322 files changed, 56312 insertions(+) create mode 100644 Documentation/AUTHORS create mode 100644 Documentation/COPYING create mode 100644 Documentation/ChangeLog create mode 100644 Documentation/FAQ create mode 100644 Documentation/README create mode 100644 Documentation/RELEASE-NOTES create mode 100644 Documentation/TODO create mode 100644 Documentation/cheat.txt create mode 100644 Documentation/fcs.txt create mode 100644 Documentation/porting.txt create mode 100644 Documentation/rel/1.0 create mode 100644 Documentation/rel/1.1.dos create mode 100644 Documentation/rel/1.1.linux create mode 100644 Documentation/rel/1.1.win create mode 100644 Documentation/rel/2.0.dos create mode 100644 Documentation/rel/2.0.linux create mode 100644 Documentation/rel/2.0.win create mode 100644 Documentation/rel/2.1.dos create mode 100644 Documentation/rel/2.1.linux create mode 100644 Documentation/rel/2.1.win create mode 100644 Documentation/rel/3.0.dos create mode 100644 Documentation/rel/3.0.linux create mode 100644 Documentation/rel/3.0.win create mode 100644 Documentation/rel/3.1 create mode 100644 Documentation/rel/3.2 create mode 100644 Documentation/rel/3.3 create mode 100644 Documentation/rel/3.4 create mode 100644 Documentation/rel/3.5 create mode 100644 Documentation/rel/3.6 create mode 100644 Documentation/rel/3.7 create mode 100644 Documentation/rel/4.0 create mode 100644 Documentation/rel/4.1 create mode 100644 Documentation/rel/d create mode 100644 Documentation/rel/da.sh create mode 100644 Documentation/rel/new create mode 100644 Documentation/rel/new.dos create mode 100644 Documentation/rel/new.linux create mode 100644 Documentation/rel/new.win create mode 100644 Documentation/rel/readme-dos.txt create mode 100644 Documentation/rel/readme-linux.txt create mode 100644 Documentation/rel/readme-win.txt create mode 100644 Documentation/rel/toc create mode 100644 Documentation/rel/top.dos create mode 100644 Documentation/rel/top.linux create mode 100644 Documentation/rel/top.win create mode 100644 Documentation/tech/README.now create mode 100644 Documentation/tech/README.sound create mode 100644 Documentation/tech/UNIF_current.txt create mode 100644 Documentation/tech/cpu/4017.txt create mode 100644 Documentation/tech/cpu/6502_cpu.txt create mode 100644 Documentation/tech/cpu/NESSOUND.txt create mode 100644 Documentation/tech/cpu/dmc.txt create mode 100644 Documentation/tech/exp/mmc5-e.txt create mode 100644 Documentation/tech/exp/mmc5_bank_switch.txt create mode 100644 Documentation/tech/nsfspec.txt create mode 100644 Documentation/tech/ppu/loopy1.txt create mode 100644 Documentation/tech/ppu/loopy2.txt create mode 100644 Documentation/tech/ppu/timing.txt create mode 100644 Makefile.base create mode 100644 Makefile.beos create mode 100644 Makefile.common create mode 100644 Makefile.dos create mode 100644 Makefile.linuxvga create mode 100644 Makefile.unixsdl create mode 100644 Makefile.win create mode 100644 banksw.h create mode 100644 boards/Makefile create mode 100644 boards/h2288.c create mode 100644 boards/malee.c create mode 100644 boards/mapinc.h create mode 100644 boards/novel.c create mode 100644 boards/sachen.c create mode 100644 boards/simple.c create mode 100644 boards/super24.c create mode 100644 boards/supervision.c create mode 100644 cart.c create mode 100644 cart.h create mode 100644 cheat.c create mode 100644 cheat.h create mode 100644 crc32.c create mode 100644 crc32.h create mode 100644 debug.c create mode 100644 debug.h create mode 100644 drawing.h create mode 100644 driver.h create mode 100644 drivers/cli/dface.h create mode 100644 drivers/cli/dos-joystick.c create mode 100644 drivers/cli/dos-joystick.h create mode 100644 drivers/cli/dos-keyboard.c create mode 100644 drivers/cli/dos-mouse.c create mode 100644 drivers/cli/dos-sound.c create mode 100644 drivers/cli/dos-sound.h create mode 100644 drivers/cli/dos-video.c create mode 100644 drivers/cli/dos-video.h create mode 100644 drivers/cli/dos.c create mode 100644 drivers/cli/dos.h create mode 100644 drivers/cli/input.c create mode 100644 drivers/cli/keyscan.h create mode 100644 drivers/cli/lnx-joystick.c create mode 100644 drivers/cli/lnx-joystick.h create mode 100644 drivers/cli/main.c create mode 100644 drivers/cli/main.h create mode 100644 drivers/cli/sdl-joystick.c create mode 100644 drivers/cli/sdl-netplay.c create mode 100644 drivers/cli/sdl-netplay.h create mode 100644 drivers/cli/sdl-sound.c create mode 100644 drivers/cli/sdl-video.c create mode 100644 drivers/cli/sdl-video.h create mode 100644 drivers/cli/sdl.c create mode 100644 drivers/cli/sdl.h create mode 100644 drivers/cli/svga-video.c create mode 100644 drivers/cli/svga-video.h create mode 100644 drivers/cli/svgalib.c create mode 100644 drivers/cli/svgalib.h create mode 100644 drivers/cli/throttle.c create mode 100644 drivers/cli/throttle.h create mode 100644 drivers/cli/unix-basedir.h create mode 100644 drivers/cli/unix-netplay.c create mode 100644 drivers/cli/unix-netplay.h create mode 100644 drivers/cli/usage.h create mode 100644 drivers/cli/vgatweak.c create mode 100644 drivers/common/args.c create mode 100644 drivers/common/args.h create mode 100644 drivers/common/cheat.c create mode 100644 drivers/common/cheat.h create mode 100644 drivers/common/config.c create mode 100644 drivers/common/config.h create mode 100644 drivers/common/unixdsp.c create mode 100644 drivers/common/unixdsp.h create mode 100644 drivers/common/vidblit.c create mode 100644 drivers/common/vidblit.h create mode 100644 drivers/win/cheat.c create mode 100644 drivers/win/cheat.h create mode 100644 drivers/win/common.h create mode 100644 drivers/win/config.c create mode 100644 drivers/win/input.c create mode 100644 drivers/win/input.h create mode 100644 drivers/win/joystick.c create mode 100644 drivers/win/joystick.h create mode 100644 drivers/win/keyboard.c create mode 100644 drivers/win/keyboard.h create mode 100644 drivers/win/keyscan.h create mode 100644 drivers/win/main.c create mode 100644 drivers/win/netplay.c create mode 100644 drivers/win/res.res create mode 100644 drivers/win/sound.c create mode 100644 drivers/win/throttle.c create mode 100644 drivers/win/video.c create mode 100644 drivers/win/wave.c create mode 100644 drivers/win/window.c create mode 100644 endian.c create mode 100644 endian.h create mode 100644 fce.c create mode 100644 fce.h create mode 100644 fceline.h create mode 100644 fds.c create mode 100644 fds.h create mode 100644 file.c create mode 100644 file.h create mode 100644 general.c create mode 100644 general.h create mode 100644 git.h create mode 100644 ines.c create mode 100644 ines.h create mode 100644 input.c create mode 100644 input.h create mode 100644 input/Makefile create mode 100644 input/arkanoid.c create mode 100644 input/cursor.c create mode 100644 input/fkb.c create mode 100644 input/fkb.h create mode 100644 input/powerpad.c create mode 100644 input/shadow.c create mode 100644 input/share.h create mode 100644 input/zapper.c create mode 100644 mappers/105.c create mode 100644 mappers/112.c create mode 100644 mappers/113.c create mode 100644 mappers/117.c create mode 100644 mappers/15.c create mode 100644 mappers/151.c create mode 100644 mappers/16.c create mode 100644 mappers/17.c create mode 100644 mappers/18.c create mode 100644 mappers/180.c create mode 100644 mappers/182.c create mode 100644 mappers/184.c create mode 100644 mappers/189.c create mode 100644 mappers/19.c create mode 100644 mappers/21.c create mode 100644 mappers/22.c create mode 100644 mappers/225.c create mode 100644 mappers/226.c create mode 100644 mappers/227.c create mode 100644 mappers/228.c create mode 100644 mappers/229.c create mode 100644 mappers/23.c create mode 100644 mappers/232.c create mode 100644 mappers/234.c create mode 100644 mappers/240.c create mode 100644 mappers/242.c create mode 100644 mappers/245.c create mode 100644 mappers/246.c create mode 100644 mappers/248.c create mode 100644 mappers/249.c create mode 100644 mappers/24and26.c create mode 100644 mappers/25.c create mode 100644 mappers/32.c create mode 100644 mappers/33.c create mode 100644 mappers/40.c create mode 100644 mappers/41.c create mode 100644 mappers/42.c create mode 100644 mappers/43.c create mode 100644 mappers/46.c create mode 100644 mappers/51.c create mode 100644 mappers/6.c create mode 100644 mappers/64.c create mode 100644 mappers/65.c create mode 100644 mappers/67.c create mode 100644 mappers/68.c create mode 100644 mappers/69.c create mode 100644 mappers/71.c create mode 100644 mappers/72.c create mode 100644 mappers/73.c create mode 100644 mappers/75.c create mode 100644 mappers/76.c create mode 100644 mappers/77.c create mode 100644 mappers/79.c create mode 100644 mappers/8.c create mode 100644 mappers/80.c create mode 100644 mappers/82.c create mode 100644 mappers/83.c create mode 100644 mappers/85.c create mode 100644 mappers/86.c create mode 100644 mappers/88.c create mode 100644 mappers/89.c create mode 100644 mappers/90.c create mode 100644 mappers/92.c create mode 100644 mappers/95.c create mode 100644 mappers/97.c create mode 100644 mappers/99.c create mode 100644 mappers/Makefile create mode 100644 mappers/fmopl.c create mode 100644 mappers/fmopl.h create mode 100644 mappers/mapinc.h create mode 100644 mappers/mapshare.h create mode 100644 mappers/mmc2and4.c create mode 100644 mappers/simple.c create mode 100644 mappers/vrc7snd.c create mode 100644 mbshare/Makefile create mode 100644 mbshare/mapinc.h create mode 100644 mbshare/mmc1.c create mode 100644 mbshare/mmc3.c create mode 100644 mbshare/mmc5.c create mode 100644 memory.c create mode 100644 memory.h create mode 100644 netplay.c create mode 100644 netplay.h create mode 100644 nsf.c create mode 100644 nsf.h create mode 100644 nsfbgnew.h create mode 100644 ops.h create mode 100644 palette.h create mode 100644 palettes/nsfnew.h create mode 100644 palettes/rp2c04001.h create mode 100644 palettes/vscv.h create mode 100644 palettes/vseb.h create mode 100644 palettes/vsgoonies.h create mode 100644 palettes/vsgrad.h create mode 100644 palettes/vsmar.h create mode 100644 palettes/vsplatoon.h create mode 100644 palettes/vsslalom.h create mode 100644 palettes/vssmb.h create mode 100644 sound.c create mode 100644 sound.h create mode 100644 state.c create mode 100644 state.h create mode 100644 svga.c create mode 100644 svga.h create mode 100644 types.h create mode 100644 unif.c create mode 100644 unif.h create mode 100644 version.h create mode 100644 video.c create mode 100644 video.h create mode 100644 x6502.c create mode 100644 x6502.h create mode 100644 zlib/ChangeLog create mode 100644 zlib/Makefile create mode 100644 zlib/adler32.c create mode 100644 zlib/algorithm.txt create mode 100644 zlib/compress.c create mode 100644 zlib/crc32.c create mode 100644 zlib/deflate.c create mode 100644 zlib/deflate.h create mode 100644 zlib/descrip.mms create mode 100644 zlib/example.c create mode 100644 zlib/faq create mode 100644 zlib/gzio.c create mode 100644 zlib/infblock.c create mode 100644 zlib/infblock.h create mode 100644 zlib/infcodes.c create mode 100644 zlib/infcodes.h create mode 100644 zlib/inffast.c create mode 100644 zlib/inffast.h create mode 100644 zlib/inffixed.h create mode 100644 zlib/inflate.c create mode 100644 zlib/inftrees.c create mode 100644 zlib/inftrees.h create mode 100644 zlib/infutil.c create mode 100644 zlib/infutil.h create mode 100644 zlib/maketree.c create mode 100644 zlib/readme create mode 100644 zlib/trees.c create mode 100644 zlib/trees.h create mode 100644 zlib/uncompr.c create mode 100644 zlib/unzip.c create mode 100644 zlib/unzip.h create mode 100644 zlib/zconf.h create mode 100644 zlib/zlib.h create mode 100644 zlib/zutil.c create mode 100644 zlib/zutil.h diff --git a/Documentation/AUTHORS b/Documentation/AUTHORS new file mode 100644 index 0000000..c34fcae --- /dev/null +++ b/Documentation/AUTHORS @@ -0,0 +1,25 @@ +A list of people who have contributed code to FCE Ultra follows. +Please note that the "Code Contributions" field may not be all inclusive; +the coder may have done more than what is listed. + +Name/Alias Code Contributions Contact Information +------------------------------------------------------------------------------ +Aaron Oneal Many changes to compile support@pocketgb.com + with MSVC and first frame + skipping code. + +Ben Parnell Most of the FCE Ultra code. xodnizel@users.sourceforge.net + +BERO Base FCE code. bero@geocities.co.jp + +\Firebug\ VGA register setting code. ?? + +LULU SDL network play code. ?? + +Paul Various code for the official kuliniew@purdue.edu + Kuliniewicz SDL port. + +Quietust VRC7 "translation" code. quietust@ircN.org + +Tatsuyuki OPL2 emulator. ?? + Satoh diff --git a/Documentation/COPYING b/Documentation/COPYING new file mode 100644 index 0000000..afd5a94 --- /dev/null +++ b/Documentation/COPYING @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Documentation/ChangeLog b/Documentation/ChangeLog new file mode 100644 index 0000000..9712202 --- /dev/null +++ b/Documentation/ChangeLog @@ -0,0 +1,823 @@ +.81: +---- + + More SDL goodies. + + Renamed "Makefile.olinuxsdl" to "Makefile.unixsdl" + + More SDL fixes and cleanups. + + BeOS port is now compiled with the "-no-fpic" flag, which allows + me to use my inline assembly. The executable work fine as far as + I can tell. + + Added "Makefile.beos". gcc doesn't like my inline assembly + used in RefreshLine(),so this port will likely be slightly slower than + other x86 ports. + + Added a check to make sure SIGBUS is not equal to SIGSEGV in + drivers/cli/main.c. Needed for compiling under BeOS. + + Renamed the "PI" member of the X6502 structure due to conflicts with + a defined symbol with some math header files. + + Merged fcelineasm.h into fceline.h + + Fixed(possibly) a possible problem in fcelineasm.h with input register + clobbering. + + More SDL changes. + + Added speed throttling code to the CLI code, and added a command + line switch "-nothrottle". + + Lots of restructuring/rewriting/merging of the MMC3 code. + + Updated DOS code to use the generic CLI wrapper. + + Reads from $4090 and $4092 now return the current envelope setting + in FDS sound emulation. I'm not sure if this is correct... Affects + "Ai Senshi Nicole" and "Bio Miracle Bokutte Upa". + + Added native SDL sound support to the SDL code. the "olinuxsdl" + now uses this code by default instead of the unixdsp sound code. + + Modified MMC3 IRQ counter emulation. I'll need to watch out to see + if it breaks any games. Fixes: MegaMan 3, Gun Nac, Klax(Japanese). + + Changed a few memory reads in x6502.c to use RdRAM instead of RdMem, + resulting in a slight speed increase. + + Cleaned up mapper 250 emulation code. + + Added support for iNES mapper 51(thanks to Kevin Horton for the + information). + + Merged some iNES mappers corresponding to bootleg multicarts + based on MMC3s with mbshare/mmc3.c. + + Added support for iNES mapper 52(thanks to Kevin Horton for the + information). + + Made some hacks to the MMC3 emulation code so that I can add support + for pirate MMC3 multicarts more easily. I should clean it up later. + Moved mapper 44 emulation code to mbshare/mmc3.c. + + Saving screen snapshots will no longer corrupt the frame buffer + for one frame(unless memory couldn't be allocated). + + Fixed screen snapshot saving(it was sort of broken due to the + changes made to the driver<->emulator interface code; status + messages were being saved to the image). FCEUI_SaveSnapshot() + no longer returns a value(the request to save a screen snapshot is + serviced before status information would be written in the next frame). + + nosprites is now set to 0 before RefreshSprite() returns, to prevent + problems if a game turns off the bg and sprites when FetchSpriteData() + for the next scanline is called but then turns on sprites when + the actual scanline is drawn. + + PPU_hook() is now called more often if PPU_hook is non-null. + Made changes to mappers 118, 95, 9, and 10 to compensate. + No games seem to be broken, and I added support for mapper 96 + (though the games aren't very playable because the special controller + isn't emulated). + + Romance of the 3 Kingdoms is now recognized to use 16KB ex-WRAM. + + Added support for mapper 185...sort of. I think this is another + instance of incompatible hardware being lumped onto one mapper number. + Sigh. + + Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + I also made some changes to the mapper 16 IRQ emulation code. + + BRK now sets the I flag. + + Reads from $4015 no longer reset DPCM IRQ. + + Changed emulation of RTI instruction slightly. + + X.IRQlow is now set to 0 in PowerNES(). + + The VS Unisystem bit in the iNES header is no longer looked at( + I was having too many problems with this bit being set when it + shouldn't have been). Now, VS Unisystem emulation is enabled + when a known VS Unisystem game is loaded. I also rewrote the VS + Unisystem detection function. + + iNES mapper 1 now supports pageable CHR RAM if no CHR ROM is present. + Fixes "Family School". + + Mapper 70 no longer has a mirroring control emulated, and I extended + the number of 8KB CHR pages supported to 16. + + Cleaned up iNES MMC5 save RAM loading/saving code and added + support for MMC1 games with 16KB of RAM(the second 8KB are saved), + via CRC32s(currently only Genghis Khan(USA) and Nobunaga's Ambition( + USA and Japan) are recognized). + + Added support for the MMC5 Koei game "Ishin no Arashi", in the iNES + format(I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs). + + Better iNES mapper 33/48 IRQ counter emulation. + + Added the game "Uchuusen - Cosmo Carrier" to this list. I'm + beginning to hate the iNES format more and more...or maybe + just Fanwen. :) + + Added the mapper 32 game "Major League" to the list of games + that usually need iNES header correction...but in this case, the + iNES header cannot specify that this game needs to have one-screen + mirroring. + + iNES header information is now printed before any header corrections + are made based on a database. + + Fixed a bug in mapper 32 emulation. "Ai Sensei no Oshiete" + works now. + + Tried to add support for iNES mappers 245 and 249. + + Fixed the MMC5 read handler to return the data last on the data + bus instead of 0xFF when a read was made to an unmapped address. + This seems to fix the lockup problems in "Bandit Kings of Ancient + China". + + Reversed "Modified the time at which the "y scroll" register is updated + during hblank." The changes broke Klax. + + Added an unsigned 64-bit base timestamp variable "timestampbase". + Adding this to the 32-bit variable "timestamp" will return + the number of cycles executed since emulation started(in the future + I'll probably change it to since reset or power toggle). + This allowed me to replace "lastn" hack in the MMC1 code with + something better. + + Changed my mind and undid the removal of support for old save states. + + Removed support for old save states and in general I won't + try to support save states made with previous versions. + + MMC1: Writes to $8000-$FFFF with D7 set will now cause + the first MMC1 register to be OR'ed with 0x0C. I'm not sure + if this is correct, but it doesn't seem to break anything + and it fixes Robocop 3. I'll see if anyone reports games + not working in .81 that worked in .80. + + Worked on a generic driver interface wrapper very similar + to the driver interface FCE Ultra used to use(I'm getting + tired of all of the duplicated driver code). Eventually, + the DOS, SVGAlib, and SDL ports will use this wrapper. + + Similar change to the argument parsing code. + + Changed configuration file saving-loading routines and the + configuration structure to allow for linking config structures + to each other). + + Small fix to the emulation of the MMC5 split screen mode. + + Made Linux SDL code compilable again. + + Changes to MMC5 EXRAM emulation(read/write). + + Fixes to the emulation of the MMC5's split screen mode, based on + observations while using CastleVania 3 and a Game Genie(on a real + NES). + + Fixed a bug in ines.c that caused any calls to AddExState() from + a mapper initialization function to be effectively "erased"(ResetExState() + was called after the mapper was initialized). Fixes the VRC7 sound + state saving/loading stuff. + + Finished adding support for the MMC5's split screen mode(this does + not mean that the support is complete, but at least the intro in + "Uchuu Keibitai SDF" works correctly now). + + Worked on adding support for the MMC5's split screen mode. Not + completed. + + Reverted to .80's FDS sound code. + + Modified the time at which the "y scroll" register is updated + during hblank. + + NSF playing code will now disable FDS sound output on song init + (fixes some problems with the Zelda no Densetsu NSF rip). + + Increased the emulated clock speed of the FDS sound emulation code + to give better quality output. + + Modified NMI to occur a few cycles later. Fixes BattleToads...but + it may have broken other games. Also modified the way NMI's are + handled in x6502.c. + + Modified ines.c to memset() GameMemBlock to 0 on virtual power toggle. + Also, trainers are now loaded into their own buffer instead of + directly into emulated WRAM and copied into emulated WRAM on + power toggle; I've been meaning to do this for quite some time. + + Changes to the way the zapper cursor is drawn on the screen. + + FCEUD_WriteSoundData(), FCEUD_BlitScreen(), and FCEUD_UpdateInput() + have been combined into one function: FCEUD_Update(). + + More fixes to the network play code, and a fix to the Windows network + play driver code that fixes(hopefully) a rather evil bug that caused + lockups when the remote stopped network play. + + Added code to set the battery-backed bit in RAM if a game needs it, + based on CRC32. + + Added more games to the list of games that commonly have bad iNES + headers, in ines.c + + Updated docs and usage.h for DOS and Linux regarding the new video + mode and the new refresh rates. + + Linux: Fixed a bug with video mode 6(a few upper scanlines were being + cut off). Increased the refresh rate of video mode 3 to 120hz. + + Increased the refresh rate of video mode 2 to 65 hz in the Linux port. + + Screen snapshots can now be taken while playing an NSF. + + Added a new sexy tweaked vga mode that I created to the Linux svgalib + port. It's 256x224 at a refresh rate of 103hz. Hopefully it won't + blow up anyone's monitor. ;) + DOS port will follow eventually. + + Modified Makefile.base to produce an executable named "fceu" instead + of "fce". + + The plans(cycle-accurate ppu emulation) for .90 were a bit ambitious, + and I still need to make other fixes before then. + + Fixed some minor(usually) bugs with setting 256x240 tweaked VGA mode + in DOS and Linux ports. + +.80: +---- + + Cleaned/fixed a few things in the mapper 19 emulation code. + Family Circuit '91 still doesn't work quite right... I wonder if + it's a bad dump. + + Added input override code to Windows port. + + Added code to fix iNES header information in RAM and suggest + changes to the user. + + Added support for iNES mapper 152(to be used with games set to + mapper 70, that use one-screen mirroring instead of h/v mirroring). + + Blits using the DirectX blitting function(method?) to the primary + surface are now done with the asynchronous flag set(if that + fails, a "normal" blit is tried). + + The DirectX blit buffer(secondary surface that FCE Ultra writes to + directly and then uses the DirectDraw blit function on to blit + to the primary buffer) is now created without specifying it + should be in system memory or video memory, except in the case + when no hardware blitting is available, and then DDraw is explicitly + told to create the surface in system memory. + + Added Family Keyboard support to the DOS port. + + Cleaned up the VRC7 sound emulation code. I need to find a way + to save the current sound state in a save state. + + Found out the real name of the "Space Shadow" gun; it's + called the "Hyper Shot". I'm still not sure who made it, though. + Possibly Bandai did. The interesting thing is that Konami + also made a Famicom accessory(dual square boxy things with two buttons + on each) with the same name(though there might not be a space in the + name). + + Only the upper two bits read from $4016/$4017 are undefined. + Bit5 is always 0, though. Fixed the bug in "input.c". + Silly kevtris' old documents. New kevtris' brain is always good. + + Family Keyboard support for the Windows port. + + Added support for the Family Basic Keyboard to the Linux port, other + ports todo. + Might want to add support for the tape recorder at some time. + Also mapped the "Scroll Lock" key to disable/enable command keys + so that the FBK is more useable. It doesn't disable CTRL C, + though... + + Changed a lot of inlined functions in x6502.c to macros so that + I could test out some optimization ideas. + + DOS code updates for game input override support. + + Small optimzation to opcode $4c, and relative jumps. + + Added some code to ines.c to set controller information in + FCEUGameInfo(returned by FCEUI_LoadGame()) based on crc32 values. + + Updated user documentation and usage.h for DOS and SVGAlib input + command-line changes. + + Added an option to disable the four-score(to Windows and Linux ports + so far). + + Updated Windows interface to support the new Famicom expansion + devices. + + (Re)Added support for the Famicom 4-player device. + + Improved Zapper emulation...sort of. It still needs a lot of work. :/ + + Added *partial* support for the "Space Shadow" gun. + + Added support for the Arkanoid controller(both NES and Famicom style). + + Added code to support the extension Famicom input devices. + + Added PAL scanline start/end drawing settings to Windows port. + + Added pause emulation key(F2) to Windows port. + + In the process of rewriting/fixing up input code stuff. + + Minor bug fix to Power Pad emulation code. + + VS Hogan's Alley and VS Duck Hunt automatically select the zapper + now(though it only works on the SVGAlib port). + + Undid some FDS sound code changes introduced in 0.76 that totally + screwed up sound. Oops. + + Added code to allow different settings for first/last scanline + drawn while in PAL emulation mode, to the Linux and DOS ports. + + Added convenience(it's not necessary, but it reduces redundant and + confusing code in the driver code) function + FCEUI_GetCurrentVidSystem(int *slstart, int *slend). + + Updated file "TODO". + + Changed #include to #include "zlib/unzip.h" + in file.c. + + NSF 6502 player now initialized the stack pointer on reset. + + Worked on de-emphasis emulation code quite a bit. + The deemphasized palette calculated at the end of the frame is now + based on what deemphasis bits were set for the longest during + the screen(sampling interval is a scanline) update loop. + Added a "static" deemphasized palette at $40-$7F in the palette table. + This corresponds to the colors when all of the deemphasis bits are set. + I did this to fix the PAL game "Noah's Ark", without breaking + anything else. The only downside is a slight speed loss(~2% on + my system when sound is disabled), but this is acceptable to me, + at least. + Maybe it's time to write hi/true-color ppu drawing code... + + + Fixed an out of bounds array access in svga.c in SetNESDeemph(). + The variable "lastd" in svga.c was being initialized to the wrong value. + Thanks to "Jarod CANAL" for pointing this out. + + Removed FCEUI_SetFirstRenderedLine or whatever it was called and + the function to set the last line. Replaced with: + void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall); + + Changed SetVidSys(int w) to ResetVidSys() in fce.c. Reenabled + PAL/NTSC emulation overrides based on game loaded(really only useful + for NSFs and UNIFs now). + + UNIF loading code now recognizes the chunk "CTRL" and tries + to use it. Only the svgalib code supports overriding of input + settings based on game loaded, now, though... + The user is still going to have to configure powerpad settings + on his/her own. + + Fixed return values of FCEU_fseek() and fixed a problem in unif.c + related to it. + + Changed mechanism for how FCE Ultra tells the driver + code what type of system is being emulated. A structure of + type "FCEUGI" is returned from FCEUI_LoadGame(). + + Fixed a major mapper 64 emulation bug introduced in 0.76. + + Modified BlitVidHi() in drivers/win/video.c to speed it up. + + Added support for loading the iNES-format Game Genie ROM image. + + Removed ggrom.h and added code to load the Game Genie ROM + image from a file. + + Added Windows netplay.c. (new: the user exiting the emulator + while stuck in a blocking recv() or send() loop now works.) + + Fixed a vram address register bug in fce.c that I created when I got + rid of the pair/dpair data types. + + Added new mappers/92.c + + Removed mappers/92.c until I can contact LULU or rewrite it. + + drivers/win changes. Removed netplay.c until I can rewrite it. + + Got rid of pair/dpair data types. + + Got rid of silly "TempArray" thing. + + Began adding GPL headers to files. FCE Ultra is going to be in + a state of legal limbo for the next few days... + + Replaced crc32.c and crc32.h, and added some #ifdef's and #defines + to use the crc32 code in zlib instead if zlib is linked with + FCE Ultra. + + More fixes to sb.c. + + Cleaned up drawing.h and ggrom.h(even though ggrom.h will *probably* + be removed before the next release). + + Redid frameskip code. + + Rewrote necessary pieces of sb.c and sb.h and fixed quite a few + bugs. I still need to test it on various other sound cards, though. + + Rewrote(more like "recreated in my own image") DOS keyboard driver. + Removed unused "keyscan.h" from drivers/svgalib + + Rewrote part of(the parts that Marat wrote - the connection and + closing parts) the Linux TCP/IP network play code. I guess it works, + but I haven't tested it very much. In any case, it's still dangerous + to use network play in FCE Ultra with SVGAlib, since recv() or send() + might block and since the keyboard is in raw mode, you have a problem. + Maybe a future SVGAlib will fix the general problem of lockups if + keyboard_update() isn't called, though it is only partly SVGAlib's + problem... + + Fixed FCEU_fseek() when used with a compressed file in the PKZIP + format that has been loaded. Fixes a UNIF loading problem. + Also added a check to the return value of FCEU_fseek() in unif.c. + + Replaced Marat's 6502 emulation core with my own. + It should be fully functional, but as always, I don't know if + I implemented the undocumented instructions correctly. + Several things are correct in this new core that were not in + Marat's(D flag is no longer cleared by interrupts, for example). + + Altered mapper 16 irq counter emulation slightly. + + Fixed the behavior of the SXA, SYA, and XAS opcodes based on the + documentation I have. I'm not sure what happens when page crossing + occurs with those instructions on a real NES, though. + Also CHANGED(not fixed) emulation of opcode $8B("XAA"). + + Changed some of the M* functions(absolute indexed and maybe some others) + to perform dummy reads. + + Changed some of the macros in m6502.c to inlined functions so + that I can modify and examine them more easily. + +.77: +---- + + Fixed a silly network play bug(in the global network play code) + that caused excessive lag. + + Added a "niceness" setting to the sound configuration dialog. + Removed obsolete information from the dialog. + + Fixed speed throttling code in Windows port when PAL emulation is + disabled/enabled and a new game hasn't been loaded yet. + + Commented out a printf() to debug stuff in fds.c(oops). + + Applied PK's joystick patch to the osdl code. It allows the user + to map axes and it fixes a joystick button mapping configuration saving + bug. + + Added two command line options new to Linux port to DOS port. + Just need to test them... + + Added some stuff to unif.c to allow for boards that can support + CHR RAM instead of CHR ROM(darn Sachen boards...). Fixes UNIF + version of "Q-Boy". + + Added command line option "-snapname" to Linux port. I'll add it + to the Windows port as well, but probably not to the DOS port. + + Added clip option to Linux port. + + Fixed sound logging(in Windows port) so that multiple recording + sessions now work. + + Added an option to clip the leftmost and rightmost 8 columns + of graphics in the Windows port. + + Added a submenu that lists recently opened files, in the Windows + port. + +.76: +---- + + Updated porting.txt. + + Added speed throttling code to Windows port that's used when sound is + disabled(and an option in the "Miscellaneous" + configuration window to disable it). + + Added cheat interface to DOS build. + + A few tweaks to the text cheat interface code in the cheat listing code. + Added a command to quickly toggle the status of the cheat(though a + cheat can still be disabled/enabled by (M)odifying the cheat). + + Support for UNIF UNL-TC-U01-1.5M board added. Same credits as below. + + Rewrote mapper 228 code just for the heck of it(actually, I couldn't + figure out why some(about two) of the games in the Action 52 cart + weren't working in .75(and .76). I traced it back to .71, where an apparent bug in + the undocumented 6502 opcode emulation allowed it to work, which + was later fixed for the .72 release(opcode 0x7C)). I'm thinking + that the dump is bad... + + Added a few crc32 checks to ines.c to check for and report when + a known(by me) bad(hacked or bad dump) game is loaded. + + Added support for the following UNIF boards. Thanks to Quietust + and Kevin Horton for the information. Some problems still exist + with a few games that use these boards, though... + + UNL-Sachen-8259B + UNL-Sachen-8259A + UNL-Sachen-74LS374N + UNL-SA-016-1M + UNL-SA-72007 + UNL-SA-72008 + UNL-SA-0036 + UNL-SA-0037 + + Fixes to some stuff in cart.c(for example, calling setprg32() when + only 16kb of prg data is present now works). + + Added support for iNES mapper 189. + + Tried to add support for the UNIF board "UNL-H2288". Failed. + + Updated "cheat.txt" to fix a few typos and added an example of finding + and adding a cheat using the Linux port's text interface. The actual + section on the Linux cheat interface still needs to be written, however. + + Changed network play code in the Windows port and fixed a bug. + Fixed a similar bug in the Linux netplay code....sort of. + + A few cosmetic changes to the dialogs in the Windows port. + + Fixed sound initialization on the Windows port(it was being initialized + when FCE Ultra started even if it was disabled by the user. Oops.). + + Joystick button configuration code in Windows port changed slightly + to be more useable. + + Changed reference to video mode 5 in the linux port to "1 per 4". + It may not be very meaningful, but it is certainly better + than "TV Emulation". I'll change the Windows port reference later. + + Documented video mode 7(320x240) for the Linux port. Also added + a check for the FBDev driver in order to use this mode instead + of a tweaked vga mode if that driver is being used. + + Added/Fixed cheat interface for Linux port. It's still not perfect, + though. The code is ugly... + + Callback function for FCEUI_ListCheats() now receives + status information(enabled/disabled). + + Callback functions for cheat functions now must return 0 to + stop listing cheats or 1 to continue. + + Fixed a problem(the cheat code was reading in cheats + for address $0000 from cheat files if any blank lines were present). + + SDL port zlib changes(linked dynamically to zlib now). + + More changes to envelope decay + looping on code. No longer + depends on value @ $4017. It now sounds correct based on some + tests I did with SMB3 and a GG, but Goonies 2 doesn't sound right( + based on a sound file sent to me by another person). + + Added support for iNES mapper 140. Thanks to Quietust for the + information. + + I need to figure out how to deal with the problem of so many + bad NSFs that most people consider good... + + Changed envelope decays a bit. Their behavior now depends on bit + 7 of $4017. I don't know if this is right... + + Addition of debug.c for some debugging stuff. + + Updated zlib to 1.1.4. + + Modified code in various files to allow UNIF games to override + current selected video system emulation(NTSC or PAL). Need to + make sure this really works. + + Changed sound.c to prevent desynchronization during network play. + This might slow down sound emulation slightly, especially when + sound emulation is disabled. I really don't care... + + Updating network play code. More info later... + + Moved the sound/video/etc output code in EmLoop() to the top of the + for(;;) loop so that initialization prior to calling EmLoop() will + be the same as initialization done during a call to a FCEUD_* function. + + A few very small changes to sound emulation in fds.c. + + Changed unlink() to remove() in cheat.c and removed the including + of the header file unistd.h. + + Split up the cc=... statement in RefreshLine() to make it easier + to read and not ambiguous(to Borland's C compiler). + + Changed a lot of the function declarations in cheat.c. I'll need + to verify that cheat searching still works ok and that cheats still work + ok. I'll also need to update the Windows(and Linux console) cheat code + to prevent compiler warnings. + + Fixed various minor code problems(not minor if you want to use a + compiler besides gcc). This is an ongoing process... + + Removed bit fields after reading about and thinking about possible + portability problems, though I kept some optimizations in fceline.h + + Minor code simplification in drivers/win/joystick.c(replaced + "case 200 ... 207" and "case 208 ... 215". + + Modified some code to use bit-fields in the graphics rendering code + in fce.c and fceline.h. gcc seems to be able to optimize the new + code to run faster. + + Fixed m6502.c and fce.c to initialize variables on virtual power + toggle. This should fix network play on the Windows port. + I'm also in the process of cleaning up fce.c. + +.75: +---- + Fixed directories configuration stuff in Windows port(corresponds + to .75r2). + + More changes to square wave channel envelope/volume emulation... + Changes described in the large paragraph some lines down have been + abandoned. + + Added a command line switch to set the volume in the dos port. + + Changed Windows sound configuration dialog. Now buffer length is + specified in time, not samples. I also added a volume control. + + Reduced the volume on 8-bit sound output on all ports by 1/2. + + Added a function FCEUI_SetSoundVolume(). Added support for setting + the volume via the command line in the Linux port, other ports coming + soon. + + Changed FCEUD_WriteSoundData() again. No longer has a "Check" + argument. All clipping(ugh) is done internally now. + + Added a directories configuration dialog to the Windows port. I worked + on it too long and I became a zombie, so it might have a few bugs. + That's what guine...err...users are for. ;) Don't expect me to do + anything like this for any other port, though. I don't feel like + doing it on the DOS port, and the Linux and other UNIXy ports shouldn't + really need it. + + Made some changes to the rectangle/square wave channel emulation to + fix the pops in SMB. I have no idea if what I did is correct. To + be honest, I'm not sure I know EXACTLY what I did, but it's something + like this: Writes to $4003/$4007 now reset the duty cycle count + and reload the cycle counter with the current wavelength. Writes to + $4003/$4007 now do not update the amplitude output of the channels; they + will be updated after the cycle counter hits 0(or below). + + More information in iNES informational output. + + Minor changes to mappers/16.c. + + Increased the volume of the VRC6's sawtooth wave channel. + + Added more information to the RAM cheat guide. + + Changed the triangle wave generation code slightly. I decided + to remove support for the higher-frequency triangle waves, as they + are too cpu-costly to create and are probably not very audible on a + real NES anyway. + + Major changes to how sound is mixed. This necessitated a high pass + DC-offset removing filter, and a low-pass filter just to make things + sound better. ^_^ + Note: FCEUD_WriteSoundData() no longer needs to clear the waveform + data referenced by the passed pointer; it's done internally now. + + Fixed JMP ($xxxx) - now handles wrapping like it occurs on a real 6502 + (hopefully; I'm assuming that the same holds true for the NES' cpu). + + Added the ability to load a custom global palette in the DOS port. + + Fixed bug in drivers/common/unixdsp.c(wrong return value if sample + rate was out of range). + + Many sound fixes... - Frequency sweeps, length counter stuff,... + + Changed Windows port to use IDirectInput7 and IDirectInputDevice7 + interfaces. + + Fixed a Game Genie bug in the core emulation code. It only appeared + in the Windows port, though. (Enabling gg emulation, loading a game, + and then disabling gg emulation and loading a new game while in the + gg code entry screen would cause the new loaded game to not work + correctly). + + Modified windows port to use the config saving/loading stuff in + drivers/common/config.c + + Mapper 45 cleanups/fixes. + + Added the ability to load a custom global palette in the Linux port. + Yay. + + Fixed a large number of overcomplicated code and silly bugs in + drivers/common/config.c. This changes the format of the configuration + structure, too. Also added support for saving/loading strings with + automatic memory allocation when strings are loaded. + + Minor change in InitNetplay(). + + Fixed bad type conversions for pointers to functions and fixed + some bad declarations of functions. + + Reenabled zlib support for the sdl build. I need to pay attention + to patches that modify lines than don't fit on my screen. + + Fixed vidblit.c to not emit so many warnings when compiling. + +.74: +---- + + Stop sound in Windows port when user clicks l/m/r mouse buttons + in the non-client area of the window. + + Added "Drag and drop" file open support to Windows port. + + Various code cleanups. + + mappers/33.c optimization. + + Rewrote the function "FCEU_MakeFName()". + + Removed crc32.h from mappers directory. + + Modified some of the window resizing code in the Windows port. + + Added support for waiting for vblank/double buffering to the Windows + port. + + Added/Fixed support for iNES mapper 248. + + After an NSF file is loaded, information about its header is now + displayed. + + Fixed a typo in the Namco 106 extra ram handling code. + + Improved the quality of the Namco 106's extra sound channels. + - Thank Mamiya and Applepie(real name?) for info. + + When an NSF file is being played, FCE Ultra will no longer go through + its scanline rendering loop. This speeds up NSF playback considerably. + + Updated "porting.txt". + + Moved some stuff from DriverInterface() to their own functions. + + Fixed some iNES mapper 18 IRQ counter emulation bugs. "Ninja Jajamaru - + Ginga Dai Sakusen" now works. + + Rewrote large pieces of the mapper 64 code. "Skull and Crossbones" + still doesn't work, though. + + Changed format of iNES header information output, added "ROM CRC32" info. + + Modified the way cycle timing is done slightly. No change + for NTSC emulation, but PAL emulation is a little more accurate. + + Changed the behavior of indirect indexed(I hope I got that right ;)) + instructions to behave more like a real 6502(junk reads are now + performed). + + A few optimizations/cleanups in m6502.c. diff --git a/Documentation/FAQ b/Documentation/FAQ new file mode 100644 index 0000000..0ce51b5 --- /dev/null +++ b/Documentation/FAQ @@ -0,0 +1,45 @@ +FCE Ultra General User's FAQ + preliminary version +------------------ + + +Q: Why doesn't the NSF work(correctly) on FCE Ultra? +A: Some NSF rips are bad. Some read from addresses that are not specified + in the NSF specifications, expecting certain values to be returned. + Others execute undocumented instructions that have no affect on + less-accurate software NSF players, but will cause problems on NSF players + that emulate these instructions. Also, the playback rate specified + in the NSF header is currently ignored, though I haven't encountered + any problems in doing this. + + +Q: Why doesn't the game work(correctly) on FCE Ultra? +A: Many factors can make a game not work on FCE Ultra: + + - If the ROM image is in the iNES format(typically files that have + the extension "nes"), its header may be incorrect. This + incorrectness may also be because of garbage in the + header. Certain utilities used to put text in the reserved + bytes of the iNES header, then those reserved bytes were + later assigned functions. FCE Ultra recognizes and + automatically removes(from the ROM image in RAM, not on the + storage medium) SOME header junk. + + If the game has graphical errors while scrolling, chances are + the mirroring is set incorrectly in the header. + + You can try to edit the header with a utility(in the NES + utilities section at http://zophar.net ) or a hex editor. + + - The on-cart hardware the game uses may not be emulated + correctly. + + - Limitations of the ROM image format may prevent a game from + being emulated correctly without special code to recognize that + game. This occurs quite often with many Koei MMC5(iNES mapper 5) + and MMC1(iNES mapper 1) games in the iNES format. FCE Ultra identifies + and emulates some of these games based on the ROM CRC32 value. + + - The ROM image may be encrypted. The author of SMYNES seems to + have done this intentionally to block other emulators from + playing "SMYNES only" games. diff --git a/Documentation/README b/Documentation/README new file mode 100644 index 0000000..71e5c8b --- /dev/null +++ b/Documentation/README @@ -0,0 +1,31 @@ +FCE Ultra was developed with gcc and GNU make in mind. MSVC will probably +compile the Windows source code with a few modifications, but you'll still +need to make a project file. + +Several pre-made makefiles are provided: + Makefile.beos - BeOS(with SDL and SDL_net) + Makefile.unixsdl - UN*X(FreeBSD/Linux/etc, with SDL) + Makefile.linuxvga - Linux(with svgalib) + Makefile.dos - (MS/PC/DR) DOS + Makefile.win - MS Windows 9x/Me/Xp/2000/etc.(with DirectX). + +If you want to use Makefile.beos or Makefile.unixsdl for a cpu type other +than 80x86, you will need to remove "-DC80x86" from the defines line +and also remove "-DLSB_FIRST" if your target cpu uses MSB first ordering. +You'll also need to remove/change "-mcpu=xxx". + +For the Windows port, I use MINGW32. http://www.mingw.org + +To compile the DOS port, you'll need to download the DJGPP package. +Any version of gcc >=2.95.3 should compile the code without changes, BUT +the DJGPP versions of gcc have some problems: + gcc 2.95.3 sometimes breaks when compiling the code. gcc 3.0.2 seems to + produce bad code for sound.c. So, I recommend using gcc 3.0.4 for + compiling the DOS version. + +Modifying the "-mcpu=i686" string in the makefiles, to optimize more effectively +for your cpu type, is a good idea. + +Always do a "make -f Makefile. clean" before compiling for a +different platform or if you update via cvs and if you have stale object +files lying around. diff --git a/Documentation/RELEASE-NOTES b/Documentation/RELEASE-NOTES new file mode 100644 index 0000000..64d343c --- /dev/null +++ b/Documentation/RELEASE-NOTES @@ -0,0 +1,36 @@ +----June 29, 2001: + -Added the source code for zlib. The makefile in the zlib directory should + be included by the platform makefile(such as Makefile.win) or zlib should + be linked in some other way. If you use the zlib that comes with this + source distribution, you'll need to set up your compiler to recognize + the zlib subdirectory as a global include directory(like "-Izlib" with gcc). + -Changed the expected behavior of FCEUD_BlitScreen(). Added two new functions, + FCEUI_SetFirstRenderedLine() and FCEUI_SetLastRenderedLine(). See + porting.txt for details. + + +----May 25, 2001: + -A few deobfuscations of the code(more will come in the future). + -Defined some macros to declare functions using different(faster) calling + conventions. After redeclaring memory map emulation functions, + there was a somewhat significant speed increase. Using the new calling conventions + for PPU_hook and MapIRQHook type functions resulted in a more significant + speed increase. + -Some of the driver<->emulator interface code has changed. Much more + will change in the future. + -Added some stuff in types.h to (possibly) help people with compiling + FCE Ultra with MSVC++. + +----May 5, 2001: + -Optimized RAM reading/writing emulation in fce.c a little. + -Moved the UNIF board emulation code to the "boards" subdir. UNIF support + isn't even near being done, though. Not a high priority either. + +----April 16, 2001: + -Much of the driver interface code was restructured/rewritten, especially the + input-related code. + -All driver functions called by the emulator are now prefixed with "FCEUD_". + (I may do something similar with the names of the driver interface functions). + -The core emulation code has been updated slightly since the Windows .53 + release, but the changes are so minor that another binary release is not + necessary. diff --git a/Documentation/TODO b/Documentation/TODO new file mode 100644 index 0000000..95b8b77 --- /dev/null +++ b/Documentation/TODO @@ -0,0 +1,60 @@ +*** First, things that are not on the TODO list(Don't bug me about these + things if you're an idiot. I don't like listening to idiots. + If you are not an idiot, and you can make decent arguments for why + these should be on the TODO list, then you can bug me.). + + Remappable command keys: Too much work would be involved, demand isn't + very high, and I don't need this feature. Maybe in the distant future, if + input devices have changed significantly and if FCE Ultra is still around. + + High-level Game Genie support(like the RAM cheats): This would be fairly + simple to do, after doing the RAM cheat stuff, but I think that FCE Ultra + already has too many ways to "cheat" and that this would just be code bloat. + + +*** General Features: + + Windows Port: + Support for command-line options(so that one crazy guy will quit bugging + me). + + SDL Port: + Make the code better. + Add a GTK+ interface using GLADE. + + Figure out a good way to add "turbo" button support and then do it. + + Make default svgalib video mode a non-tweaked VGA mode. + + Finish the software video blitting "library", add support for 2xsai, eagle, + interpolation, etc. effects. + + Rewrite network play code. Add security features(such as only allowing + connections from a specified ip address). + + +*** Emulation: + + Fix DPCM playback and IRQ at end of playback. + + Improve the sound filters. They work, but not very well. + + Fix frame IRQ(if it even exists...) and $4017. + + Fix some 6502 emulation bugs(undocumented opcodes might not be implemented + correctly and I'm not sure if the IRQ flag latency is implemented correctly). + + Fix MMC3 IRQ emulation. Check if this fixes the PAL version of "Star Wars". + + Figure out correct timing for when the PPU refresh address register is + updated by the PPU(for the next scanline). + + Vertical blank period might be too short? Or maybe something is wrong + with my mapper 16 IRQ emulation code. Probably the latter. + See SD Gundam Knight 3 or SD GK 2. + + Sound frame count stuff on PAL games(is it correct?). + + Fix FDS sound emulation. + + Fix Zapper emulation(Chiller still doesn't always work correctly). diff --git a/Documentation/cheat.txt b/Documentation/cheat.txt new file mode 100644 index 0000000..55b1129 --- /dev/null +++ b/Documentation/cheat.txt @@ -0,0 +1,249 @@ +FCE Ultra Cheat Guide + version .4 +--------------------------- + +Table of Contents: + + 1: Introduction + 1.0) Introduction + 1.1) Cheat Files + + 2: The Windows Interface + 2.0) The Main Cheat Window + 2.1) The Add Cheat Window + 2.1.1) The Cheat Search Interface + + 3: The DOS/Linux Interface - UNFINISHED(read "The Windows Interface" for now) + 3.0) The Main Cheat Menu + 3.0.1) The Cheat List + 3.1) The Add Cheat Menu + 3.1.1) The Cheat Search Interface + + 4: Finding Cheats + 4.0) "Mega Man 3" Windows Example + 4.1) "Over Horizon" DOS/Linux Example + 4.2) Hints + +/******** Section 1.0: */ + + FCE Ultra allows cheating by the periodic "patching" of arbitrary addresses + in the 6502's memory space with arbitrary values. Currently, only RAM + patching is allowed(trying to patch an address where ROM is will silently + fail). + + The patches are all applied a short time before the emulated + vertical blanking period. This detail shouldn't concern most people, though. + However, this does mean that cheating with games that use + bank-switched RAM may be problematic. Fortunately, such games are not very + common(in relation to the total number of NES and Famicom games). + +/******** Section 1.1: */ + + Cheats are stored in the "cheats" subdirectory under the base FCE Ultra + directory. The files are in a simple plain-text format. Each line represents + a one-byte memory patch. The format is: + + Address(hex):Value(hex):Description + + Example: + + 040e:05:Infinite super power. + + A colon(:) can be prefixed to the beginning of a line to disable that cheat. + + +/******** Section 2.0 */ + + All addresses listed in the Cheats and Add Cheat windows are in a + 16-bit hexadecimal format and all values in these windows are in an + unsigned 8-bit decimal format(the range for values is 0 through 255). + + + The main Cheats window contains the list of cheats for the currently + loaded game, places to view and edit the attributes of the selected cheat, + a button to delete the selected cheat, a button to open the Add Cheat + window, and a button to close the Cheats window. + +/******** Section 2.1 */ + + To the left in the Add Cheat window are text edit boxes for inputting + attributes of a cheat and a button to add that cheat. To the right is + the cheat searching interface. + + /******* Section 2.1.1 */ + + The cheat search interface consists of several components: a list of + addresses and associated data for a search, several command buttons, + and the search paramters. + + The list of addresses is in the format of: + "Address:Original Value:Current Value". + + The address is the location in the 6502's address space, the original + value is the value that was stored at this address when the search was + reset, and the current value is the value that is currently stored at + that address. Selecting an item in this list will automatically cause + the "Address" field in the "Add Cheat" box to be updated with the + selected address. + + The "Reset Search" button resets the search process; all valid addresses + are displayed in the cheat list and the data values at those addresses noted. + + The "Do Search" buttons performs a search based on the search parameters + and removes any non-matching addresses from the address list. + + The "Set Original to Current" button sets the remembered original values + to the current values. It is like the "Reset Search" button, but it does + not affect which addresses are shown in the address list. This command is + especially useful when used in conjunction with the "O!=C" search filter. + + The "Unhide Excluded" button shows all addresses that are excluded as a + result of any previous searches. It is like the "Reset Search" button + except that it does not affect the remembered original values. + + The numbers assigned the names "V1" and "V2" have different meanings based + on which filter is selected. A list of the names of the filters and detailed + information on what they do follows("original value" corresponds to the value + remembered for a given addres and "current value" is the value currently + at that address. Also, if a value is not explicitly said to be shown + under a certain condition, then it is obviously excluded.): + + "O==V1 && C==V2": + Show the address if the original value is equal to "V1" AND + the current value is equal to "V2". + + "O==V1 && |O-C|==V2": + Show the address if the original value is equal to "V1" AND + the difference between the current value and the original + value is equal to "V2". + + "|O-C|==V2": + Show the address if the difference between the current value + and the original value is equal to "V2". + "O!=C": + Show the address if the original value does not equal the + current value. + + +/******** Section 4.0 */ + + This example will give Mega Man unlimited energy. + Immediately after entering the Top Man stage, make your way to the + "Add Cheat" window. Push "Reset Search". + Go back to playing and move right until the first enemy appears. Allow + yourself to be hit twice. Each hit does "2" damage, so you've lost 4 energy + bars. Go to the "Add Cheat" window again and select the third filter + ("|O-C|==V2") and enter the value 4 next to "V2". Then push "Do Search". + + Several addresses will appear in the address list. You can try to find + the address you want through trial and error, or you can narrow the results + down further. We will do the latter. + + Go back to playing MM3 and get hit one more time and make your way back + to the "Add Cheat" window. Your damage is now "6". You can probably + see which address that contains your life(it is 00A2). If now, change + V2 to 6 and push "Do Search" again. This should leave only 00A2. + + Select that entry in the address list. Shift your attention to the "Add + Cheat" box to the left. Type in a meaningful name and the desired value(156; + it was the value when you had no damage, so it's safe to assume it's the + maximum value you can use). Push the "Add" button and a confirmation box + will come up. The cheat has been added. + + +/******** Section 4.1 */ + + This example will give you infinite lives in the NTSC(Japanese) version + of "Over Horizon". + + Start a new game. Notice that when you press "Start" during gameplay, + the number of lives you have left is indicated. With no cheating, you + start with 3 lives(2 lives left). + + Activate the cheat interface immediately after starting a new game. + Select the "New Cheats" menu and "Reset Search". + + I'll assume that the number of lives left shown in the game is the same number + that's stored in RAM. Now, "Do Search". You're going to use the first search + filter. For V1, enter the value 2. For V2, enter the same value. This, + coupled with the fact that you just reset the search, will allow you to search + for a value "absolutely"(as opposed to changes in the value). + + Now, "Show Results". When I did it, I received 11 results: + + 1) $0000:002:002 + 2) $001c:002:002 + 3) $001e:002:002 + 4) $009d:002:002 + 5) $00b9:002:002 + 6) $00e3:002:002 + 7) $0405:002:002 + 8) $0406:002:002 + 9) $0695:002:002 + 10) $07d5:002:002 + 11) $07f8:002:002 + + You really can't do much yet(unless you want to spend time doing trial + and error cheat additions). Return to the game. + + After losing a life, go back to the cheat interface, to the "New Cheats" + menu, and "Show Results". Here are my results: + + 1) $0000:002:002 + 2) $001c:002:002 + 3) $001e:002:002 + 4) $009d:002:002 + 5) $00b9:002:041 + 6) $00e3:002:002 + 7) $0405:002:001 + 8) $0406:002:002 + 9) $0695:002:002 + 10) $07d5:002:001 + 11) $07f8:002:002 + + Notice that two addresses seem to hold the number of lives($0405 and + $07d5). You can lose another life and go "Show Results" again, and you + should see that $07d5 is the address that holds the number of lives. + + Now that you know the address that holds the number of lives, you can + add a cheat. You can either type in the number from the cheat results list + corresponding to the address you want to add a cheat for, or you can + remember the address and select "Add Cheat" from the "New Cheats" menu. + Do the former. + + Now you will need to enter a name for the cheat. I suggest something short, + but descriptive. "Infinite lives" will work fine. Next, a prompt for + the address will show up. Since you selected an item from the list, you + can press enter to use the associated address($07d5). Next, you will + need to enter a value. It doesn't need to be large(in fact, it probably + shouldn't be; abnormally high numbers can cause some games to misbehave). + I suggest a value of 2. After this, you should get a prompt that looks like + this: + + Add cheat "Infinite lives" for address $07d5 with value 002?(Y/N)[N]: + + Answer "Y". You now have infinite lives. + +/******** Section 4.2 */ + + Games store player information in many different ways. For example, + if you have "3" lives in Super Wacky Dodgeball 1989, the game might store + it in memory as 2, 3, or 4, or perhaps a different number all together. + Also, say that you have 69 life points out of 200 in Mole Mashers. The + game might store how many life points you have, or how much damage you have + taken. Relative value searches are very valuable because you probably + don't know the way that the game stores its player data. + + Some games, especially RPGs, deal with individual numbers greater than + 8-bits in size. Most that I've seen seem to store the multiple-byte data + least significant byte(lower byte of number) first in memory, though + conceivably, it could be stored most significant byte first, or the component + bytes of the number could be non-contiguous, though the latter is very unlikely. + For example, say I have 5304 experience points in Boring Quest for the + Overused Plot Device. To split the number into two eight bit decimal numbers, + take 5304 %(modulus) 256. This will give a number that is the lower 8 bits. + Next, take 5304 / 256. The integral component of your answer will be the + upper 8 bits(or the next 8 bits, if the number is or can be larger than 16 + bits) of 5304. Now you will need to search for these numbers. Fortunately, + most(all?) RPGs seem to store large numbers exactly as they are shown in the + game. diff --git a/Documentation/fcs.txt b/Documentation/fcs.txt new file mode 100644 index 0000000..d269301 --- /dev/null +++ b/Documentation/fcs.txt @@ -0,0 +1,143 @@ +FCE Ultra Save State Format +(preliminary document - AKA *very incomplete*) +--------------------------------------- + +FCE Ultra's save state format is now designed to be as forward and backwards +compatible as possible. This is achieved through the (over)use of chunks. +All multiple-byte variables are stored LSB(least significant byte)-first. +Data types: + + (u)int8 - (un)signed 8 bit variable(also referred to as "byte") + (u)int16 - (un)signed 16 bit variable + (u)int32 - (un)signed 32 bit variable + +-- Main File Header: + +The main file header is 16-bytes in length. The first three bytes contain +the string "FCS". The next byte contains the version of FCE Ultra that saved +this save state. This document only applies to version "53"(.53) and higher. +After the version byte, the size of the entire file in bytes(minus the 16 byte +main file header) is stored. The rest of the header is currently unused +and should be nulled out. Graphical example of relevant parts: + + FCS + +-- Section Chunks: + +Sections chunk headers are 5-bytes in length. The first byte defines what +section it is, the next four bytes define the total size of the section +(including the section chunk header). + + + +Section definitions: + + 1 - "CPU" + 2 - "CPUC" + 3 - "PPU" + 4 - "CTLR" + 5 - "SND" + 16 - "EXTRA" + +-- Subsection Chunks + +Subsection chunks are stored within section chunks. They contain the actual +state data. Each subsection chunk is composed of an 8-byte header and the data. +The header contains a description(a name) and the size of the data contained +in the chunk: + + +The name is a four-byte string. It does not need to be null-terminated. +If the string is less than four bytes in length, the remaining unused bytes +must be null. + +-- Subsection Chunk Description Definitions + +Note that not all subsection chunk description definitions listed below +are guaranteed to be in the section chunk. It's just a list of what CAN +be in a section chunk. This especially applies to the "EXTRA" subsection. + +---- Section "CPU" + + Name: Type: Description: + + PC uint16 Program Counter + A uint8 Accumulator + P uint8 Processor status register + X uint8 X register + Y uint8 Y register + S uint8 Stack pointer + RAM uint8[0x800] 2KB work RAM + +---- Section "CPUC" (emulator specific) + + Name: Type: Description: + + JAMM uint8 Non-zero value if CPU in a "jammed" state + IRQL uint8 Non-zero value if IRQs are to be generated constantly + ICoa int32 Temporary cycle counter + ICou int32 Cycle counter + +---- Section "PPU" + + Name: Type: Description: + + NTAR uint8[0x800] 2 KB of name/attribute table RAM + PRAM uint8[32] 32 bytes of palette index RAM + SPRA uint8[0x100] 256 bytes of sprite RAM + PPU uint8[4] Last values written to $2000 and $2001, the PPU + status register, and the last value written to + $2003. + XOFF uint8 Tile X-offset. + VTOG uint8 Toggle used by $2005 and $2006. + RADD uint16 PPU Address Register(address written to/read from + when $2007 is accessed). + TADD uint16 PPU Address Register + VBUF uint8 VRAM Read Buffer + PGEN uint8 PPU "general" latch. See Ki's document. + +---- Section "CTLR" (somewhat emulator specific) + + Name: Type: Description: + + J1RB uint8 Bit to be returned when first joystick is read. + J2RB uint8 Bit to be returned when second joystick is read. + +---- Section "SND" (somewhat emulator specific) + + + +---- Section "EXTRA" (varying emulator specificness) + + For iNES-format games(incomplete): + + Name: Type: Description: + + WRAM uint8[0x2000] 8KB of WRAM at $6000-$7fff + MEXR uint8[0x8000] (very emulator specific) + CHRR uint8[0x2000] 8KB of CHR RAM at $0000-$1fff(in PPU address space). + EXNR uint8[0x800] Extra 2KB of name/attribute table RAM. + MPBY uint8[32] (very emulator specific) + MIRR uint8 Current mirroring: + 0 = "Horizontal" + 1 = "Vertical" + $10 = Mirror from $2000 + $11 = Mirror from $2400 + IRQC uint32 Generic IRQ counter + IQL1 uint32 Generic IRQ latch + IQL2 uint32 Generic IRQ latch + IRQA uint8 Generic IRQ on/off register. + PBL uint8[4] List of 4 8KB ROM banks paged in at $8000-$FFFF + CBL uint8[8] List of 8 1KB VROM banks page in at $0000-$1FFF(PPU). + + For FDS games(incomplete): + + Name: Type: Description: + + DDT uint8[65500] Disk data for side x(0-3). + FDSR uint8[0x8000] 32 KB of work RAM + CHRR uint8[0x2000] 8 KB of CHR RAM + IRQC uint32 IRQ counter + IQL1 uint32 IRQ latch + IRQA uint8 IRQ on/off. + diff --git a/Documentation/porting.txt b/Documentation/porting.txt new file mode 100644 index 0000000..72fefca --- /dev/null +++ b/Documentation/porting.txt @@ -0,0 +1,229 @@ +*Incomplete* + + +***Driver-supplied functions: + These functions will only be called after the driver code calls + FCEUI_LoadGame() or FCEUI_Emulate(). + +void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count); + Called by FCE Ultra on every emulated frame. This function should + perform the following three things(in any order): + + 1. + Update the data pointed to by the pointers passed to + FCEUI_SetInput() and FCEUI_SetInputFC(). + 2. + Copy contents of XBuf over to video memory(or whatever needs to be + done to make the contents of XBuf visible on screen). + Each line is 256 pixels(and bytes) in width, and there can be 240 + lines. The pitch for each line is 272 bytes. + XBuf will be 0 if the symbol FRAMESKIP is defined and this frame + was skipped. + 3. + Write the contents of "Buffer" to the sound device. "Count" is the + number of samples to write. Only the lower 16-bits of each + sample are used, so each 32-bit sample in "Buffer" can be converted to + signed 16-bit by dropping the upper 16 bits. + When sound was disabled for the frame, "Count" will be 0. + +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b); + Set palette entry "index" to specified RGB values(value: min=0, max=255). + +void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b); + Get palette entry "index" data. + +void FCEUD_PrintError(char *s); + Print/Display an error message string pointed to by "s". + +int FCEUD_NetworkConnect(void); + Initialize a network connection. Return 0 if an error occurs. + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block); + Receive "len" bytes of data to "data". If block is 0 and "len" amount + of data is not available, return -1. If block is 1, wait until the + requested amount of data is available. + Return 0 on failure. + +int FCEUD_NetworkSendData(uint8 *data, uint32 len); + Send "len" bytes of "data". Return 0 on failure. + +void FCEUD_NetworkClose(void); + Close the network connection. + + +***FCE Ultra functions(called by the driver code): + The FCEUI_* functions may only be called before FCEUI_Emulate() is + called or after it returns and before it is called again, or after the + following functions are called and before they return: + FCEUD_Update(); + Calling the FCEUI_* functions at any other time may result in + undefined behavior. + +void FCEUI_SetInput(int port, int type, void *ptr, int attrib); +void FCEUI_SetInputFC(int type, void *ptr, int attrib); +void FCEUI_DisableFourScore(int s); + +void FCEUI_SetSnapName(int a); + +void FCEUI_DisableSpriteLimitation(int a); + Disables the 8-sprite-per-scanline limitation of the NES if "a" + is nonzero. The default behavior is the limitation is enabled. + +void FCEUI_SaveExtraDataUnderBase(int a); + If "a" is nonzero, save extra non-volatile game data(battery-backed + RAM) under FCE Ultra's base directory. Otherwise, the behavior is + to save it under the same directory the game is located in(this is + the default behavior). + +FCEUGI *FCEUI_LoadGame(char *name); + Loads a new file. "name" is the full path of the file to load. + Returns 0 on failure, or a pointer to data type "FCEUGI": + See file "git.h" for more details on this structure. + +int FCEUI_Initialize(void); + Allocates and initializes memory. Should only be called once, before + any calls to other FCEU functions. + +void FCEUI_SetBaseDirectory(void); + Specifies the base FCE Ultra directory. This should be called + immediately after FCEUI_Initialize() and any time afterwards. + +void FCEUI_SetDirOverride(int which, char *n); + + FCEUIOD_CHEATS - Cheats + FCEUIOD_MISC - Miscellaneous stuff(custom game palettes) + FCEUIOD_NV - Non-volatile game data(battery backed RAM) + FCEUIOD_SNAPS - Screen snapshots + FCEUIOD_STATE - Save states + +void FCEUI_Emulate(void); + Enters the emulation loop. This loop will be exited when FCEUI_CloseGame() + is called. This function obviously shouldn't be called if FCEUI_LoadGame() + wasn't called or FCEUI_CloseGame() was called after FCEUI_LoadGame(). + +void FCEUI_CloseGame(void); + Closes the loaded game and frees all memory used to load it. + Also causes FCEUI_Emulate() to return. + +void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall); + Sets the first(minimum is 0) and last(NOT the last scanline plus one; + maximum is 239) scanlines of background data to draw, for both NTSC + emulation mode and PAL emulation mode. + + Defaults are as if this function were called with the variables set + up as follows: + ntscf=8, ntscl=239, palf=0, pall=239 + +void FCEUI_SetNetworkPlay(int type); + Sets status of network play according to "type". If type is 0, + then network play is disabled. If type is 1, then we are server. + If type is 2, then we are a client. + +void FCEUI_SelectState(int w); + Selects the state "slot" to save to and load from. + +void FCEUI_SaveState(void); + Saves the current virtual NES state from the "slot" selected by + FCEUI_SelectState(). + +void FCEUI_LoadState(void); + Loads the current virtual NES state from the "slot" selected by + FCEUI_SelectState(). + +void FCEUI_SaveSnapshot(void); + Saves a screen snapshot. + +void FCEUI_DispMessage(char *msg); + Displays a short, one-line message using FCE Ultra's built-in + functions and ASCII font data. + +int32 FCEUI_GetDesiredFPS(void); + Returns the desired FPS based on whether NTSC or PAL emulation is + enabled, shifted left by 24 bits(this is necessary because the real + FPS value is not a whole integer). This function should only be + necessary if sound emulation is disabled. + +int FCEUI_GetCurrentVidSystem(int *slstart, int *slend); + Convenience function(not strictly necessary, but reduces excessive code + duplication); returns currently emulated video system + (0=NTSC, 1=PAL). It will also set the variables pointed to by slstart + and slend to the first and last scanlines to be rendered, respectively, + if slstart and slend are not 0. + +int FCEUI_AddCheat(char *name, uint16 addr, uint8 val); + Adds a RAM cheat with the specified name to patch the address "addr" + with the value "val". + +int FCEUI_DelCheat(uint32 which); + Deletes the specified(by number) cheat. + +void FCEUI_ListCheats(void (*callb)(char *name, uint16 a, uint8 v)); + Causes FCE Ultra to go through the list of all cheats loaded for + the current game and call callb() for each cheat with the cheat + information. + +int FCEUI_GetCheat(uint32 which, char **name, int32 *a, int32 *v, int *s); + Gets information on the cheat referenced by "which". + +int FCEUI_SetCheat(uint32 which, char *name, int32 a, int32 v, int s); + Sets information for the cheat referenced by "which". + +void FCEUI_CheatSearchBegin(void); + Begins the cheat search process. Current RAM values are copied + to a buffer to later be processed by the other cheat search functions. + +void FCEUI_CheatSearchEnd(int type, int v1, int v2); + Searches the buffer using the search method specified by "type" + and the parameters "v1" and "v2". + +int32 FCEUI_CheatSearchGetCount(void); + Returns the number of matches from the cheat search. + +void FCEUI_CheatSearchGet(void (*callb)(uint16 a, int last, int current)); + +void FCEUI_CheatSearchGetRange(int first, int last, void (*callb)(uint16 a, int last, int current)); + Like FCEUI_CheatSearchGet(), but you can specify the first and last + matches to get. + +void FCEUI_CheatSearchShowExcluded(void); + Undos any exclusions of valid addresses done by FCEUI_CheatSearchEnd(). + +void FCEUI_CheatSearchSetCurrentAsOriginal(void); + Copies the current values in RAM into the cheat search buffer. + +***Recognized defined symbols: + +The following defined symbols affect the way FCE Ultra is compiled: + + C80x86 + - Include 80x86 inline assembly in AT&T syntax. + + FRAMESKIP + - Include frame skipping code. + + NETWORK + - Include network play code. + + FPS + - Compile code that prints out a number when FCE Ultra exits + that represents the average fps. + + ZLIB + - Compile support for compressed PKZIP-style files AND gzip compressed + files. "unzip.c" will need to be compiled and linked in by you if + this is defined(it's in the zlib subdirectory). + + LSB_FIRST + - Compile code to expect that variables that are greater than 8 bits + in size are stored Least Significant Byte First in memory. + + PSS_STYLE x + - Sets the path separator style to the integer 'x'. Valid styles are: + 1: Only "/" - For UNIX platforms. + 2: Both "/" and "\" - For Windows and MSDOS platforms. + 3: Only "\" - For ???. + 4: Only ":" - For Macintoshes and Apple IIs ^_^. + + + + diff --git a/Documentation/rel/1.0 b/Documentation/rel/1.0 new file mode 100644 index 0000000..d791845 --- /dev/null +++ b/Documentation/rel/1.0 @@ -0,0 +1,178 @@ +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + diff --git a/Documentation/rel/1.1.dos b/Documentation/rel/1.1.dos new file mode 100644 index 0000000..003a7d8 --- /dev/null +++ b/Documentation/rel/1.1.dos @@ -0,0 +1,22 @@ +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 9x/Me/2000/XP(long filename support) + VGA adapter + Sound Blaster Pro or newer sound card(or compatible) + + diff --git a/Documentation/rel/1.1.linux b/Documentation/rel/1.1.linux new file mode 100644 index 0000000..b84d391 --- /dev/null +++ b/Documentation/rel/1.1.linux @@ -0,0 +1,22 @@ +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Linux 2.2.x + SVGA adapter with 512 KB of RAM + Sound device capable of handling a sample rate of about 44100 hz. + + diff --git a/Documentation/rel/1.1.win b/Documentation/rel/1.1.win new file mode 100644 index 0000000..af57dc7 --- /dev/null +++ b/Documentation/rel/1.1.win @@ -0,0 +1,26 @@ +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system specifications: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Windows 95 + DirectX 7.0 + Video adapter + + Recommended minimum system specifications: + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 98SE + Video adapter with 2D acceleration abilities + DirectX 8.0 + Joystick + Mouse + Sound device capable of handling a sample rate of 44100 hz. + + diff --git a/Documentation/rel/2.0.dos b/Documentation/rel/2.0.dos new file mode 100644 index 0000000..c837350 --- /dev/null +++ b/Documentation/rel/2.0.dos @@ -0,0 +1,93 @@ +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 6 = 256x224(with scanlines) @ 120 hz + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. This *may* cause sound distortion. + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("C:\nes\test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joy x Joystick mapped to virtual joystick x. + 0 = Disabled, reset configuration. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + diff --git a/Documentation/rel/2.0.linux b/Documentation/rel/2.0.linux new file mode 100644 index 0000000..bace00f --- /dev/null +++ b/Documentation/rel/2.0.linux @@ -0,0 +1,109 @@ +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + ./fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 4 = 640x480(with scanlines) + 5 = 640x480("1 per 4") + 6 = 256x224(with scanlines) @ 120 hz + 7 = 320x240 + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. If you have sound enabled and enable this, + you may have to increase the number of sound fragments + from the default of 8, to reduce buffer underruns. + Buffer underruns will not be preventable if the + current video refresh rate is < ~60 hz (or < ~50 hz + if PAL emulation is enabled). + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("/home/jp/test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -sfragsize x Set sound fragment size to 2^x(2 to the power of x) + SAMPLES. The default value is 7. + -snfrags x Set number of sound fragments to x. The default value + is 8. + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joyx y Joystick mapped to virtual joystick x(1-4). + 0 = Disabled, reset configuration. + Otherwise, y(1-inf) = joystick number. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -connect s Connect to server 's' for TCP/IP network play. + -server Be a host/server for TCP/IP network play. + -netport x Use TCP/IP port x for network play. The default + port is 4046. diff --git a/Documentation/rel/2.0.win b/Documentation/rel/2.0.win new file mode 100644 index 0000000..15bc082 --- /dev/null +++ b/Documentation/rel/2.0.win @@ -0,0 +1,8 @@ +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + FCE Ultra can be started by running the executable "fceu.exe". + I do not recommend running it from a DOS "box". + + diff --git a/Documentation/rel/2.1.dos b/Documentation/rel/2.1.dos new file mode 100644 index 0000000..31f5995 --- /dev/null +++ b/Documentation/rel/2.1.dos @@ -0,0 +1,59 @@ +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + diff --git a/Documentation/rel/2.1.linux b/Documentation/rel/2.1.linux new file mode 100644 index 0000000..85915f3 --- /dev/null +++ b/Documentation/rel/2.1.linux @@ -0,0 +1,62 @@ +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F3 Lock virtual console. + F4 Unlock virtual console. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + diff --git a/Documentation/rel/2.1.win b/Documentation/rel/2.1.win new file mode 100644 index 0000000..4676385 --- /dev/null +++ b/Documentation/rel/2.1.win @@ -0,0 +1,125 @@ +/******************************************************************************/ +/* 2.1) Using FCE Ultra: */ +/******************************************************************************/ + + After starting FCE Ultra, you'll probably want to load a game. Do + this by going to File/Open. + + Menu descriptions: + + File + Open - Loads a new game. + Save State - Saves the current NES state. + Load State - Loads a saved NES state. + Log Sound As - Logs sound to a file. It will not work if sound + is disabled. + Exit - Exit the emulator. + + NES + Reset - Resets the virtual NES. + Power - Power cycles the virtual NES. + Cheats - Activates the cheat interface. See "cheat.txt" for + more details. + + Config + Hide Menu - Hides the menu. + Game Genie - If checked, enable Game Genie emulation. + Game Genie emulation will only begin or end when a new + game is loaded. + PAL Emulation - If checked, enable PAL emulation. Changes take effect + immediately, though I recommend resetting the virtual + NES afterward PAL emulation is enabled or disabled. + Directories - Configure the directories that FCE Ultra will store + its files in. + Input - Enter input configuration dialog. + Note that not all virtual devices are configurable. + Miscellaneous - Enter miscellaneous configuration dialog. + Network Play - Enter network play configuration dialog. + Palette - Enter palette configuration dialog. + Sound - Enter sound configuration dialog. + Sound enabled: + Sound emulation and output are enabled when this is checked. + Force 8-bit sound: + Forces 8-bit sound output. Use only when absolutely + necessary(very rare). + Sample rate: + Specifies how many sound samples will be played back per + second. Unless you know what you are doing, you probably + don't need to change this setting. + Use secondary sound buffer: + Uses a secondary sound buffer. This option may be required + for sound to work with certain sound cards/devices. + Selecting "with global focus" will cause sound to be played + while FCE Ultra has lost window focus, but you will probably + also want to select "Active While Focus Lost" in the Config + menu as well, otherwise you will just get repeating sound + when FCE Ultra loses focus. + Length of sound buffer: + Specifies what length of sound(in milliseconds) should be + buffered by FCE Ultra. DirectSound and the Windows kernel + may or may not cause a little more latency than what you + might expect(usually not any more than a few milliseconds), + depending on your setup. + Use larger values if you have sound problems such as popping + or gaps, though. Larger values will increase the latency of + the sound, however. Finally, larger values are ideal for + background music listening. + Volume: + Specifies the volume of FCE Ultra's sound output. Setting + the volume too high MIGHT cause noticeable clipping on some + sounds(loud drums, for example), but don't let that possibility + stop you from experimenting. + + Video - Enter video configuration dialog. + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + 0-9 Select save state. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + + F3 Hide/Show menu. + F4 Toggle between windowed/full screen modes. + F10 Reset. + F11 Power off/on. + F12 Exit. + + diff --git a/Documentation/rel/3.0.dos b/Documentation/rel/3.0.dos new file mode 100644 index 0000000..73aa6ad --- /dev/null +++ b/Documentation/rel/3.0.dos @@ -0,0 +1,19 @@ +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + FCE Ultra's base directory is the directory in which the executable + is located. + + Sound Blaster sound output requires that the 'BLASTER' environment + variable is set. To set it(permanently), add the following line + to your autoexec.bat file: + + set BLASTER=A240 I5 D1 H7 + + Where 240(hexadecimal) is the Sound Blaster's base I/O address, 5 + is the IRQ number, 1 is the 8-bit DMA channel, and 7 is the 16-bit + DMA channel(if applicable). + *DO NOT GUESS THE SETTINGS* + Invalid settings can result in very bad things happening. + diff --git a/Documentation/rel/3.0.linux b/Documentation/rel/3.0.linux new file mode 100644 index 0000000..faa5407 --- /dev/null +++ b/Documentation/rel/3.0.linux @@ -0,0 +1,37 @@ +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + 1. FCE Ultra uses SVGAlib, so you'll obviously need to have it + installed. + + 2. Be careful when using the non-VGA video modes(such as 640x480). + SVGAlib doesn't always handle virtual terminal switching well when + one of these modes is being used. Also, since FCE Ultra enables a + a linear frame buffer for these modes, heed this warning from + the SVGAlib documentation: + + Furthermore, some cards (Cirrus) just enable this buffer + at a fixed hardware address. For Cirrus it is mapped at + 14MB so you should never used it if you have more than + 14MB of memory (But how does an application know?). The + Mach32 support for this is smarter. It makes this feature + only available when it is safe to be used. + + 3. FCE Ultra must be run as root in order to get access to the + VGA hardware. SVGAlib will drop root privileges if you are running + the program as a normal user and the suid root bit is set and the + executable is owned by root. Example: + chown root fceu ; chmod 4755 fceu + Make sure you have the latest stable release of SVGAlib, though. + + 4. FCE Ultra will save all data(state saves/screen snapshots) in + ~/.fceultra, so make sure your "HOME" environment variable is set + correctly. + + If you believe FCE Ultra is too slow, you can try the following: + * Kill some of the other processes. + * Disable sound emulation. + * Raise the priority of FCE Ultra with the 'nice' command. + Ex: nice -n -20 ./fceu + diff --git a/Documentation/rel/3.0.win b/Documentation/rel/3.0.win new file mode 100644 index 0000000..1fb9dd2 --- /dev/null +++ b/Documentation/rel/3.0.win @@ -0,0 +1,11 @@ +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + Your desktop color depth must be 16bpp, 24bpp, or 32bpp for FCE Ultra + to run properly in windowed mode. + + FCE Ultra's base directory is the directory in which the executable + is located. + + diff --git a/Documentation/rel/3.1 b/Documentation/rel/3.1 new file mode 100644 index 0000000..68b3afc --- /dev/null +++ b/Documentation/rel/3.1 @@ -0,0 +1,20 @@ +/******************************************************************************/ +/* 3.1) Network Play Notes */ +/******************************************************************************/ + + In TCP/IP network play, the server will be player one, and the + client will be player 2. + + Zapper emulation and power pad emulation currently do not work with + network play. + + Having Game Genie or PAL emulation enabled on only one side + will cause problems. + + Both players MUST use the same ROM/disk image and SRAM + file(if applicable). + + When using FDS or VS Unisystem games with network play, only player + 1 will be able to insert disk, eject disk, insert coins, toggle dip + switches, etc. + diff --git a/Documentation/rel/3.2 b/Documentation/rel/3.2 new file mode 100644 index 0000000..725374f --- /dev/null +++ b/Documentation/rel/3.2 @@ -0,0 +1,28 @@ +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + diff --git a/Documentation/rel/3.3 b/Documentation/rel/3.3 new file mode 100644 index 0000000..5cf33f1 --- /dev/null +++ b/Documentation/rel/3.3 @@ -0,0 +1,18 @@ +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + diff --git a/Documentation/rel/3.4 b/Documentation/rel/3.4 new file mode 100644 index 0000000..4d014dd --- /dev/null +++ b/Documentation/rel/3.4 @@ -0,0 +1,23 @@ +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + diff --git a/Documentation/rel/3.5 b/Documentation/rel/3.5 new file mode 100644 index 0000000..474c4c9 --- /dev/null +++ b/Documentation/rel/3.5 @@ -0,0 +1,35 @@ +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + diff --git a/Documentation/rel/3.6 b/Documentation/rel/3.6 new file mode 100644 index 0000000..0c0ff57 --- /dev/null +++ b/Documentation/rel/3.6 @@ -0,0 +1,19 @@ +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + diff --git a/Documentation/rel/3.7 b/Documentation/rel/3.7 new file mode 100644 index 0000000..483a030 --- /dev/null +++ b/Documentation/rel/3.7 @@ -0,0 +1,15 @@ +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + diff --git a/Documentation/rel/4.0 b/Documentation/rel/4.0 new file mode 100644 index 0000000..d22245c --- /dev/null +++ b/Documentation/rel/4.0 @@ -0,0 +1,12 @@ +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + diff --git a/Documentation/rel/4.1 b/Documentation/rel/4.1 new file mode 100644 index 0000000..e177ee2 --- /dev/null +++ b/Documentation/rel/4.1 @@ -0,0 +1,36 @@ +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/d b/Documentation/rel/d new file mode 100644 index 0000000..028eaef --- /dev/null +++ b/Documentation/rel/d @@ -0,0 +1,5 @@ +#!/bin/sh + +./da.sh linux +./da.sh win +./da.sh dos diff --git a/Documentation/rel/da.sh b/Documentation/rel/da.sh new file mode 100644 index 0000000..a0e5e3a --- /dev/null +++ b/Documentation/rel/da.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cat top.$1 new new.$1 toc 1.0 1.1.$1 2.0.$1 2.1.$1 3.0.$1 3.1 3.2 3.3 3.4 3.5 3.6 3.7 4.0 4.1 > readme-$1.txt diff --git a/Documentation/rel/new b/Documentation/rel/new new file mode 100644 index 0000000..c18f25f --- /dev/null +++ b/Documentation/rel/new @@ -0,0 +1,56 @@ + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * Fixed more network play bugs. It should now work correctly(how + many times have I said or implied that...). + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. diff --git a/Documentation/rel/new.dos b/Documentation/rel/new.dos new file mode 100644 index 0000000..9a69443 --- /dev/null +++ b/Documentation/rel/new.dos @@ -0,0 +1,5 @@ + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. diff --git a/Documentation/rel/new.linux b/Documentation/rel/new.linux new file mode 100644 index 0000000..abaee9f --- /dev/null +++ b/Documentation/rel/new.linux @@ -0,0 +1,6 @@ + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. + diff --git a/Documentation/rel/new.win b/Documentation/rel/new.win new file mode 100644 index 0000000..e69de29 diff --git a/Documentation/rel/readme-dos.txt b/Documentation/rel/readme-dos.txt new file mode 100644 index 0000000..150c566 --- /dev/null +++ b/Documentation/rel/readme-dos.txt @@ -0,0 +1,646 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 9x/Me/2000/XP(long filename support) + VGA adapter + Sound Blaster Pro or newer sound card(or compatible) + + +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 6 = 256x224(with scanlines) @ 120 hz + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. This *may* cause sound distortion. + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("C:\nes\test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joy x Joystick mapped to virtual joystick x. + 0 = Disabled, reset configuration. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + FCE Ultra's base directory is the directory in which the executable + is located. + + Sound Blaster sound output requires that the 'BLASTER' environment + variable is set. To set it(permanently), add the following line + to your autoexec.bat file: + + set BLASTER=A240 I5 D1 H7 + + Where 240(hexadecimal) is the Sound Blaster's base I/O address, 5 + is the IRQ number, 1 is the 8-bit DMA channel, and 7 is the 16-bit + DMA channel(if applicable). + *DO NOT GUESS THE SETTINGS* + Invalid settings can result in very bad things happening. + +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/readme-linux.txt b/Documentation/rel/readme-linux.txt new file mode 100644 index 0000000..880e919 --- /dev/null +++ b/Documentation/rel/readme-linux.txt @@ -0,0 +1,707 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * Fixed more network play bugs. It should now work correctly(how + many times have I said or implied that...). + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. + * Added a new video mode(256x224 at a refresh rate of 103 Hz). + * Increased the refresh rate of video mode 2 to 65 Hz. + * Increased the refresh rate of video mode 3 to 120 Hz. + * Added speed throttling used when sound is disabled, and a + command-line switch to control it. + + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.1 Network Play Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system requirements: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Linux 2.0.36 + VGA adapter + + Recommended system specifications(at least): + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Linux 2.2.x + SVGA adapter with 512 KB of RAM + Sound device capable of handling a sample rate of about 44100 hz. + + +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + Start FCE Ultra using the following format: + + ./fceu romimage.nes + + can be one or more of the following: + + -vmode x Select video mode(all are 8 bpp). + 1 = 256x240 @ 72 hz + 2 = 256x256 @ 65 hz + 3 = 256x256(with scanlines) @ 120 hz + 4 = 640x480(with scanlines) + 5 = 640x480("1 per 4") + 6 = 256x224(with scanlines) @ 120 hz + 7 = 320x240 + 8 = 256x224 @ 103 hz + -vsync x Wait for the screen's vertical retrace before updating + the screen. If you have sound enabled and enable this, + you may have to increase the number of sound fragments + from the default of 8, to reduce buffer underruns. + Buffer underruns will not be preventable if the + current video refresh rate is < ~60 hz (or < ~50 hz + if PAL emulation is enabled). + 0 = Disabled. + 1 = Enabled. + -cpalette x Load a custom global palette from file x. + The filename x is saved in the configuration file, + not the data from the file. Note that you should + probably use an absolute("/home/jp/test.pal") filename + rather than a relative("test.pal") filename. If x is 0, + the default (built in) global palette will be used. + -ntsccol Emulate an NTSC's TV's colors based on user-supplied + hue and tint values. + 0 = Disabled. + 1 = Enabled. + -sound x Sound. + 0 = Disabled. + Otherwise, x = playback rate in samples per second. + -soundvol x Sound volume. x is an integral percentage value. + The default value is, obviously, 100. + Setting it higher will increase the likelihood that + sound clipping will occur. This shouldn't be noticeable + if the volume is reasonable(200% or lower; perhaps even + higher for many games). + -sfragsize x Set sound fragment size to 2^x(2 to the power of x) + SAMPLES. The default value is 7. + -snfrags x Set number of sound fragments to x. The default value + is 8. + -f8bit x Force 8-bit sound. Enabling this will decrease + sound quality noticeably without increasing + performance noticeably. Only use when 16-bit sound + is problematic. + 0 = Disabled. + 1 = Enabled. + -nothrottle x Disables speed throttling used when sound is disabled + if x is non-zero. The default value of x is 0. + -joyx y Joystick mapped to virtual joystick x(1-4). + 0 = Disabled, reset configuration. + Otherwise, y(1-inf) = joystick number. + -inputx str Select device mapped to virtual NES-style input port + x(1-2). + The default for both virtual input ports is "gamepad". + str may be: none, gamepad, zapper, powerpada, + powerpadb, arkanoid + -fcexp str Select Famicom expansion port device. If you select + a device other than "none", you should enable the + "gamepad" on both NES-style virtual input ports. + The default is "none". + str may be: none, shadow, arkanoid, 4player, fkb + -nofs x Disables Four-Score emulation if x is 1. Default is 0. + Note that Four-Score emulation will only be active + if "gamepad" is mapped to one or both virtual input + ports. + -gg Enable Game Genie emulation. + -pal Emulate a PAL NES. + -no8lim x Disables the 8 sprites per scanline limitation. + 0 = Limitation enabled. + 1 = Limitation disabled. + -subase x Save extra game data files(such as battery-backed RAM) + under the base directory if enabled. + 0 = Disabled. + 1 = Enabled. + -snapname x Selects what type of file name screen snapshots will + have. + 0 = Numeric(0.png) + 1 = File base and numeric(mario-0.png) + -clipsides x Clip leftmost and rightmost 8 columns of pixels of + video output. + 0 = Disabled. + 1 = Enabled. + -slstart x Set the first drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 8. + -slend x Set the last drawn emulated scanline for NTSC emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -slstartp x Set the first drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 0. + -slendp x Set the last drawn emulated scanline for PAL emulation + mode. Valid values for x are 0 through 239. + The default value is 239. + -connect s Connect to server 's' for TCP/IP network play. + -server Be a host/server for TCP/IP network play. + -netport x Use TCP/IP port x for network play. The default + port is 4046. +/******************************************************************************/ +/* 2.1) General Keyoard Key Mapping: */ +/******************************************************************************/ + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + T Select tint to adjust. + H Select hue to adjust. + +/- Increase/decrease tint or hue. + + 0-9 Select save state. + Caps Lock Select virtual joystick. + + F2 Activate cheat interface. + + F3 Lock virtual console. + F4 Unlock virtual console. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + F10 Reset. + F11 Power off/on. + ESC/F12 Exit. + + +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + 1. FCE Ultra uses SVGAlib, so you'll obviously need to have it + installed. + + 2. Be careful when using the non-VGA video modes(such as 640x480). + SVGAlib doesn't always handle virtual terminal switching well when + one of these modes is being used. Also, since FCE Ultra enables a + a linear frame buffer for these modes, heed this warning from + the SVGAlib documentation: + + Furthermore, some cards (Cirrus) just enable this buffer + at a fixed hardware address. For Cirrus it is mapped at + 14MB so you should never used it if you have more than + 14MB of memory (But how does an application know?). The + Mach32 support for this is smarter. It makes this feature + only available when it is safe to be used. + + 3. FCE Ultra must be run as root in order to get access to the + VGA hardware. SVGAlib will drop root privileges if you are running + the program as a normal user and the suid root bit is set and the + executable is owned by root. Example: + chown root fceu ; chmod 4755 fceu + Make sure you have the latest stable release of SVGAlib, though. + + 4. FCE Ultra will save all data(state saves/screen snapshots) in + ~/.fceultra, so make sure your "HOME" environment variable is set + correctly. + + If you believe FCE Ultra is too slow, you can try the following: + * Kill some of the other processes. + * Disable sound emulation. + * Raise the priority of FCE Ultra with the 'nice' command. + Ex: nice -n -20 ./fceu + +/******************************************************************************/ +/* 3.1) Network Play Notes */ +/******************************************************************************/ + + In TCP/IP network play, the server will be player one, and the + client will be player 2. + + Zapper emulation and power pad emulation currently do not work with + network play. + + Having Game Genie or PAL emulation enabled on only one side + will cause problems. + + Both players MUST use the same ROM/disk image and SRAM + file(if applicable). + + When using FDS or VS Unisystem games with network play, only player + 1 will be able to insert disk, eject disk, insert coins, toggle dip + switches, etc. + +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/readme-win.txt b/Documentation/rel/readme-win.txt new file mode 100644 index 0000000..8c4425d --- /dev/null +++ b/Documentation/rel/readme-win.txt @@ -0,0 +1,641 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + + * Screen snapshots can now be taken while playing an NSF. + * Saving screen snapshots will no longer corrupt the frame buffer. + * Added many more games to the list of games that usually are found + with bad iNES headers. + * Fixed more network play bugs. It should now work correctly(how + many times have I said or implied that...). + * The NSF player will now disable the FDS sound channel on song + initialization(fixes a problem with the Zelda no Densetsu + rip). + * Reads from $4090 and $4092 while emulating the FDS will now return + the current envelope settings. Affects "Ai Senshi Nicole" + and "Bio Miracle Bokutte Upa", at least. + * Merged a lot of pirate MMC3 multicart emulation code with the main + MMC3 emulation code. + * Added support for the MMC5's split-screen mode. This fixes the + introduction in "Uchuu Keibitai SDF". + * Writes to $8000-$FFFF with D7 set during MMC1 emulation will + cause the MMC1 mode register to be OR'd with $C. This fixes + "Robocop 3". + * Replaced an MMC1 hack that I used to get "Bill and Ted's Excellent + Video Game Adventure" to work with something more accurate. + * Fixed the MMC5 read handler to return the data last on the data + bus instead of $FF when a read occured to an unmapped address. + This fixes the lockup problem in "Bandit Kings of Ancient China" + and possibly other games. + * Added support for the game "Ishin no Arashi" in the iNES format + (I added an entry with its CRC32 value and the number of 8KB + WRAM banks it needs into the MMC5 WRAM size table). + * Added support for MMC1 games in the iNES format with 16KB of RAM + via CRC32 comparisons(currently only Genghis Khan(USA), Romance + of the 3 Kingdoms(USA), and Nobunaga's Ambition(USA and Japan) are + recognized). + * iNES mapper 1 now supports pageable CHR RAM if CHR ROM is not + present. Fixes "Family School". + * Added support for iNES mappers 51 and 52. Thanks to Kevin Horton + for the information. + * Modified MMC3(iNES mapper 4/118/119) IRQ counter emulation. Fixes + problems in "MegaMan 3", "Gun Nac", and the Japanese version of + "Klax", but it *might* cause problems with other games. + * Fixed an iNES mapper 32 emulation bug. "Ai Sensei no Oshiete" + works now. + * Fixed iNES mapper 33/48 IRQ emulation. + * Fixed iNES mapper 16 IRQ emulation. + * Added support for "Famicom Jump 2" as iNES mapper 153. + If a good(as far as I can tell) dump is loaded, FCE Ultra will + automatically fix the mapper number. + * The VS Unisystem bit in iNES headers is no longer recognized. + Too many games have this bit set when it shouldn't be set. + Now VS Unisystem games are recognized by CRC32 value. + * Reads from $4015 no longer reset DMC IRQ. This fixes the + title screen of "Romancia". + * PPU NMI now occurs a few cycles later. Fixes the "BattleToads" + lockup problem. + * BRK emulation now sets the I flag. + * Changed a few zero-page address mode functions to read directly + from emulated RAM. + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.1 Network Play Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + +/******************************************************************************/ +/* 1.0) What FCE Ultra is: */ +/******************************************************************************/ + + FCE Ultra is an NTSC and PAL Famicom/NES emulator for various + platforms. It is based upon Bero's original FCE source code. Current + features include good PPU, CPU, pAPU, expansion chip, and joystick + emulation. Also a feature unique to this emulator(at the current + time) is authentic Game Genie emulation. Save states and snapshot + features also have been implemented. The VS Unisystem is emulated + as well. FCE Ultra supports iNES format ROM images, UNIF format ROM + images, headerless and FWNES style FDS disk images, and NSF files. + + FCE Ultra currently supports the following iNES mappers(many partially): + +Number: Description: Game Examples: +-------------------------------------------------------------------------------- + 0 No Mapper Donkey Kong, Mario Bros + 1 Nintendo MMC1 MegaMan 2, Final Fantasy + 2 Simple 16KB PRG Switch MegaMan 1, Archon, 1944 + 3 Simple 8KB CHR Switch Spy Hunter, Gradius + 4 Nintendo MMC3 Recca, TMNT 2, Final Fantasy 3 + 5 Nintendo MMC5 Castlevania 3, Just Breed, Uchuu Keibitai SDF + 6 FFE F4 Series(hacked) Saint Seiya, Ganbare Goemon + 7 AOROM Battle Toads, Lion King + 8 FFE F3 Series(hacked) Doraemon Kaitakuhen + 9 Nintendo MMC2 Punchout! + 10 Nintendo MMC4 Fire Emblem, Fire Emblem Gaiden + 11 Color Dreams Crystal Mines, Bible Adventures + 13 CPROM Videomation + 15 Multi-cart(pirate) 100-in-1: Contra Function 16 + 16 Bandai Dragon Ball Z, Gundam Knight + 17 FFE F8 Series(hacked) Parodius, Last Armageddon + 18 Jaleco SS806 Pizza Pop, Plazma Ball + 19 Namco 106 Splatter House, Mappy Kids + 21 Konami VRC4 2A WaiWai World 2, Ganbare Goemon Gaiden 2 + 22 Konami VRC4 1B Twinbee 3 + 23 Konami VRC2B WaiWai World, Getsufuu Maden + 24 Konami VRC6 Akumajo Densetsu(Dracula 3) + 25 Konami VRC4 Gradius 2, Bio Miracle: Boku tte Upa + 26 Konami VRC6 A0-A1 Inverse Esper Dream 2, Madara + 32 Irem G-101 Image Fight 2, Perman + 33 Taito TC0190/TC0350 Don Doko Don 1&2 + 34 NINA-001 and BNROM Impossible Mission 2, Deadly Towers, Bug Honey + 40 (pirate) Super Mario Bros. 2 + 41 Caltron 6-in-1 Caltron 6-in-1 + 42 (pirate) "Mario Baby" + 43 Multi-cart(pirate) Golden Game 150 in 1 + 44 Multi-cart(pirate) Super HiK 7 in 1 + 45 Multi-cart(pirate) Super 1000000 in 1 + 46 Game Station Rumble Station + 47 NES-QJ Nintendo World Cup/Super Spike V.B. + 48 Taito TC190V Flintstones + 49 Multi-cart(pirate) Super HiK 4 in 1 + 51 Multi-cart(pirate) 11 in 1 Ball Games + 52 Multi-cart(pirate) Mario Party 7 in 1 + 64 Tengen RAMBO-1 Shinobi, Klax + 65 Irem H-3001 Daiku no Gensan 2 + 66 GNROM SMB + Duck Hunt + 67 Sunsoft Mapper "3" Fantasy Zone 2 + 68 Sunsoft Mapper "4" After Burner 2, Nantetta Baseball + 69 Sunsoft FME-7 Batman: ROTJ, Gimmick! + 70 ?? Kamen Rider Club + 71 Camerica Fire Hawk, Linus Spacehead + 72 Jaleco ?? Pinball Quest + 73 Konami VRC3 Salamander + 75 Jaleco SS8805/Konami VRC1 Tetsuwan Atom, King Kong 2 + 76 Namco 109 Megami Tenshi 1 + 77 Irem ?? Napoleon Senki + 78 Irem 74HC161/32 Holy Diver + 79 NINA-06 F15 City War, Krazy Kreatures + 80 Taito X-005 Minelvation Saga + 82 Taito ?? Kyuukyoku Harikiri Stadium - Heisei Gannen Ban + 83 Multi-cart(pirate) Dragon Ball Party + 85 Konami VRC7 Lagrange Point + 86 Jaleco ?? More Pro Baseball + 87 ?? Argus + 89 Sunsoft ?? Mito Koumon + 90 Pirate Super Mario World, Mortal Kombat + 92 Jaleco ?? MOERO Pro Soccer, MOERO Pro Yakyuu '88 + 93 ?? Fantasy Zone + 94 ?? Senjou no Ookami + 95 Namco ?? Dragon Buster + 97 ?? Kaiketsu Yanchamaru + 99 VS System 8KB CHR Switch VS SMB, VS Excite Bike +105 NES-EVENT Nintendo World Championships +112 Asder Sango Fighter, Hwang Di +113 MB-91 Deathbots +118 MMC3-TLSROM/TKSROM Board Ys 3, Goal! 2, NES Play Action Football +119 MMC3-TQROM Board High Speed, Pin*Bot +140 Jaleco ?? Bio Senshi Dan +151 Konami VS System Expansion VS The Goonies, VS Gradius +152 ?? Arkanoid 2, Saint Seiya Ougon Densetsu +153 Bandai ?? Famicom Jump 2 +180 ?? Crazy Climber +182 ?? Super Donkey Kong +184 ?? Wing of Madoola, The +189 Micro Genius TXC ?? Thunder Warrior +225 Multi-cart(pirate) 58-in-1/110-in-1/52 Games +226 Multi-cart(pirate) 76-in-1 +227 Multi-cart(pirate) 1200-in-1 +228 Action 52 Action 52, Cheetahmen 2 +229 Multi-cart(pirate) 31-in-1 +232 BIC-48 Quattro Arcade, Quattro Sports +234 Multi-cart ?? Maxi-15 +240 ?? Gen Ke Le Zhuan, Shen Huo Le Zhuan +242 ?? Wai Xing Zhan Shi +246 ?? Fong Shen Ban +248 ?? Bao Qing Tian +250 ?? Time Diver Avenger + + FCE Ultra currently supports the following UNIF boards(minus the + prefixes HVC-, NES-, BTL-, and BMC-, as they are currently ignored): + +Group: Name: Game Examples: +-------------------------------------------------------------------------------- +Bootleg: + MARIO1-MALEE2 Super Mario Bros. Malee 2 + NovelDiamond9999999in1 Novel Diamond 999999 in 1 + Super24in1SC03 Super 24 in 1 + Supervision16in1 Supervision 16-in-1 + +Unlicensed: + Sachen-8259A Super Cartridge Version 1 + Sachen-8259B Silver Eagle + Sachen-74LS374N Auto Upturn + SA-016-1M Master Chu and the Drunkard Hu + SA-72007 Sidewinder + SA-72008 Jovial Race + SA-0036 Mahjong 16 + SA-0037 Mahjong Trap + TC-U01-1.5M Challenge of the Dragon + +MMC1: + SAROM Dragon Warrior + SBROM Dance Aerobics + SCROM Orb 3D + SEROM Boulderdash + SGROM Defender of the Crown + SKROM Dungeon Magic + SLROM Castlevania 2 + SL1ROM Sky Shark + SNROM Shingen the Ruler + SOROM Nobunaga's Ambition + +MMC3: + TFROM Legacy of the Wizard + TGROM Megaman 4 + TKROM Kirby's Adventure + TKSROM Ys 3 + TLROM Super Spike V'Ball + TLSROM Goal! 2 + TR1ROM Gauntlet + TQROM Pinbot + TSROM Super Mario Bros. 3 + TVROM Rad Racer 2 + +MMC5: + EKROM Gemfire + ELROM Castlevania 3 + ETROM Nobunaga's Ambition 2 + EWROM Romance of the Three Kingdoms 2 + +MMC6: + HKROM Star Tropics + +Nintendo +discrete +logic: + CNROM Gotcha + CPROM Videomation + MHROM + NROM-128 Mario Bros. + NROM-256 Super Mario Bros. + RROM-128 + UNROM Megaman + + +/******************************************************************************/ +/* 1.1) System requirements: */ +/******************************************************************************/ + + Minimum system specifications: + + Pentium 60 MHz + 8 MB RAM + 400 KB free disk space + Windows 95 + DirectX 7.0 + Video adapter + + Recommended minimum system specifications: + + Pentium 233 MHz + 16 MB RAM + 5 MB free disk space + Windows 98SE + Video adapter with 2D acceleration abilities + DirectX 8.0 + Joystick + Mouse + Sound device capable of handling a sample rate of 44100 hz. + + +/******************************************************************************/ +/* 2.0) Starting FCE Ultra */ +/******************************************************************************/ + + FCE Ultra can be started by running the executable "fceu.exe". + I do not recommend running it from a DOS "box". + + +/******************************************************************************/ +/* 2.1) Using FCE Ultra: */ +/******************************************************************************/ + + After starting FCE Ultra, you'll probably want to load a game. Do + this by going to File/Open. + + Menu descriptions: + + File + Open - Loads a new game. + Save State - Saves the current NES state. + Load State - Loads a saved NES state. + Log Sound As - Logs sound to a file. It will not work if sound + is disabled. + Exit - Exit the emulator. + + NES + Reset - Resets the virtual NES. + Power - Power cycles the virtual NES. + Cheats - Activates the cheat interface. See "cheat.txt" for + more details. + + Config + Hide Menu - Hides the menu. + Game Genie - If checked, enable Game Genie emulation. + Game Genie emulation will only begin or end when a new + game is loaded. + PAL Emulation - If checked, enable PAL emulation. Changes take effect + immediately, though I recommend resetting the virtual + NES afterward PAL emulation is enabled or disabled. + Directories - Configure the directories that FCE Ultra will store + its files in. + Input - Enter input configuration dialog. + Note that not all virtual devices are configurable. + Miscellaneous - Enter miscellaneous configuration dialog. + Network Play - Enter network play configuration dialog. + Palette - Enter palette configuration dialog. + Sound - Enter sound configuration dialog. + Sound enabled: + Sound emulation and output are enabled when this is checked. + Force 8-bit sound: + Forces 8-bit sound output. Use only when absolutely + necessary(very rare). + Sample rate: + Specifies how many sound samples will be played back per + second. Unless you know what you are doing, you probably + don't need to change this setting. + Use secondary sound buffer: + Uses a secondary sound buffer. This option may be required + for sound to work with certain sound cards/devices. + Selecting "with global focus" will cause sound to be played + while FCE Ultra has lost window focus, but you will probably + also want to select "Active While Focus Lost" in the Config + menu as well, otherwise you will just get repeating sound + when FCE Ultra loses focus. + Length of sound buffer: + Specifies what length of sound(in milliseconds) should be + buffered by FCE Ultra. DirectSound and the Windows kernel + may or may not cause a little more latency than what you + might expect(usually not any more than a few milliseconds), + depending on your setup. + Use larger values if you have sound problems such as popping + or gaps, though. Larger values will increase the latency of + the sound, however. Finally, larger values are ideal for + background music listening. + Volume: + Specifies the volume of FCE Ultra's sound output. Setting + the volume too high MIGHT cause noticeable clipping on some + sounds(loud drums, for example), but don't let that possibility + stop you from experimenting. + + Video - Enter video configuration dialog. + + Default Key Mapping: + + For emulated Family BASIC Keyboard: + Enable/Disable Keyboard Input Scroll Lock + (enabling emulated keyboard input will disable + commands keys) + All emulated keys are mapped to the closest open key on the PC + keyboard, with a few exceptions. The emulated "@" key is + mapped to the "`"(grave) key, and the emulated "kana" key + is mapped to the "Insert" key(in the 3x2 key block above the + cursor keys). + + For emulated game pads: + Left Control B + Left Alt A + Enter/Return Start + Tab Select + Cursor Down Down + Cursor Up Up + Cursor Left Left + Cursor Right Right + + For emulated power pads(keys correspond to button locations on + side "B"): + O P [ ] + K L ; ' + M , . / + + For FDS games: + I Insert disk. + E Eject disk. + S Select disk/disk side. + + For VS Unisystem games: + C Insert coin. + V Show/Hide dip switches. + 1-8 Toggle dip switches(when dip switches + are shown). + + 0-9 Select save state. + + F5/F7 Save/Load state. + F9 Save screen snapshot. + + F3 Hide/Show menu. + F4 Toggle between windowed/full screen modes. + F10 Reset. + F11 Power off/on. + F12 Exit. + + +/******************************************************************************/ +/* 3.0) Platform Specific Notes */ +/******************************************************************************/ + + Your desktop color depth must be 16bpp, 24bpp, or 32bpp for FCE Ultra + to run properly in windowed mode. + + FCE Ultra's base directory is the directory in which the executable + is located. + + +/******************************************************************************/ +/* 3.1) Network Play Notes */ +/******************************************************************************/ + + In TCP/IP network play, the server will be player one, and the + client will be player 2. + + Zapper emulation and power pad emulation currently do not work with + network play. + + Having Game Genie or PAL emulation enabled on only one side + will cause problems. + + Both players MUST use the same ROM/disk image and SRAM + file(if applicable). + + When using FDS or VS Unisystem games with network play, only player + 1 will be able to insert disk, eject disk, insert coins, toggle dip + switches, etc. + +/******************************************************************************/ +/* 3.2) VS Unisystem Notes */ +/******************************************************************************/ + + FCE Ultra currently only supports VS Unisystem ROM images in the + iNES format. + + ROM Images: + + * VS Unisystem games that are about 49,000 bytes in size most likely + use mapper 99. + * Other VS Unisystem games will use other mappers. Here is a short + list of games and the mappers they use: + + CastleVania - 2 + Dr. Mario - 1 + Goonies - 151 + Gradius - 151 + Ice Climber - 99 + Platoon - 68 + + Palette(s): + + * The colors in many VS Unisystem games may be incorrect. This + is due to each game having its own PPU, and thus using a + different palette than games that use a different PPU. + + +/******************************************************************************/ +/* 3.3) Famicom Disk System Notes */ +/******************************************************************************/ + + You will need the FDS BIOS ROM image in the base FCE Ultra directory. + It must be named "disksys.rom". I will not give this file to you, so + don't ask. + + Two types of FDS disk images are supported: disk images with the + FWNES-style header, and disk images with no header. + + You should make backups of all of your FDS games you use with FCE + Ultra. This is because FCE Ultra will write the disk image back to + the storage medium, and the disk image in RAM might have been corrupted + because of inaccurate emulation(this case is not likely to occur, but + it could occur). + + +/******************************************************************************/ +/* 3.4) Light Gun Notes */ +/******************************************************************************/ + + Currently, the NES Zapper and the light gun used with the VS + Unisystem(I will call both the same name, Zapper) are supported. + Most(all?) NES games expect the Zapper to be plugged into port 2. + and most(all?) VS Unisystem games expect the Zapper to be plugged + into port(?) 1. + + The LEFT mouse button is the emulated trigger button for the + Zapper. The RIGHT mouse button is also emulated as the trigger, + but as long as you have the RIGHT mouse button held down, no color + detection will take place, which is effectively like pulling the + trigger while the Zapper is pointed away from the television screen. + Note that you must hold the RIGHT button down for a short + time(greater than just a fast click, shorter than a second). + + Zapper emulation currently does NOT work with network play, so + don't even try it. I may add support in the future if enough + people want it or if I want it. + + +/******************************************************************************/ +/* 3.5) Palette Notes */ +/******************************************************************************/ + + Palettes files are expected to contain 64 8-bit RGB triplets(each in + that order; red comes first in the triplet in the file, then green, + then blue). Each 8-bit value represents brightness for that particular + color. 0 is minimum, 255 is maximum. + + Palettes can be set on a per-game basis. To do this, put a palette + file in the "gameinfo" directory with the same base filename + as the game you wish to associate with and the extension "pal". + Examples: + + File name: Palette file name: + BigBad.nes BigBad.pal + BigBad.zip BigBad.pal + BigBad.Better.nes BigBad.Better.pal + + + With so many ways to choose a palette, figuring out which one will + be active may be difficult. Here's a list of what palettes will + be used, in order from highest priority to least priority(if a condition + doesn't exist for a higher priority palette, the emulator will + continue down its list of palettes). + + NSF Palette(for NSFs only) + Palette loaded from the "gameinfo" directory. + NTSC Color Emulation(only for NTSC NES games). + VS Unisystem palette(if the game is a VS Unisystem game and a palette + is available). + Custom global palette. + Default NES palette. + + +/******************************************************************************/ +/* 3.6) Compressed File Notes */ +/******************************************************************************/ + + FCE Ultra can load data from both PKZIP-format files and + gzip-format files. Only one type of (de)compression algorithm is + supported: "deflate"; this seems to be the most popular compression + algorithm, though. + + A compressed FDS disk image will only be saved back to disk if it + uses the gzip format. + + All files in a PKZIP format file will be scanned for the + followings extensions: .nes, .fds, .nsf, .unf, .nez, .unif + The first compressed file to have one of these extensions will be + loaded. If no compressed file has one of these extensions, the + first compressed file will be loaded. + + +/******************************************************************************/ +/* 3.7) Game Genie Notes */ +/******************************************************************************/ + + The Game Genie ROM image is loaded from the file "gg.rom" in the + base directory the first time Game Genie emulation is enabled and + a ROM image is loaded since the time FCE Ultra has run. + + The ROM image may either be the 24592 byte iNES-format image, or + the 4352 raw ROM image. + + Remember that enabling/disabling Game Genie emulation will not take + effect until a new game is loaded(this statement shouldn't concern + any of the "run once" command-line driven ports). + +/******************************************************************************/ +/* 4.0) Contacting the author */ +/******************************************************************************/ + + I can be reached via email at xodnizel@users.sourceforge.net. + Bero can be reached via email at 9bero9@geocities.co.jp + (Note that Bero can not and will not answer any questions + regarding the operation of FCE Ultra, so please don't ask him. + If you understand this, remove the 9's from the email address + provided to get his real email address.) + + +/******************************************************************************/ +/* 4.1) Credits */ +/******************************************************************************/ + +\Firebug\ - High-level mapper information. +Bero - Original FCE source code. +Brad Taylor - NES sound channel guide. +Chris Hickman - Archaic Ruins. +Donald Moore - DC PasoFami NES packs. +Fredrik Olson - NES four-player adapter information. +Gilles Vollant - PKZIP file loading functions. +goroh - Various documents. +Jeff Tamer - Insaniacal fun. +Jeremy Chadwick - General NES information. +Justin Smith - Giving me obscure ROM images in the "dark ages of + NES emulation". +Kevin Horton - Low level NES information and sound information. +Ki - Various technical information. +Mark Knibbs - Various NES information. +Marat Fayzullin - General NES information. +Matthew Conte - Sound information. +N. Andou - Awesome NES/SNES emulators, at the time... +nori - FDS sound information. +Quietust - VRC7 sound translation code by The Quietust + (quietust@ircN.org). + Ideas and corrections. +R. Hoelscher - Famicom four-player adapter information. +Rob Mocca - DC PasoFami NES packs, testing. +Sean Whalen - Node99. +Tatsuyuki Satoh - OPL2 emulator +TheRedEye - ROM images, testing. + + +Info-ZIP - ZLIB + +...and everyone else who has helped me. diff --git a/Documentation/rel/toc b/Documentation/rel/toc new file mode 100644 index 0000000..5abafb3 --- /dev/null +++ b/Documentation/rel/toc @@ -0,0 +1,23 @@ + + +Contents: + + 1. Basic information + 1.0 What FCE Ultra is. + 1.1 System requirements. + 2. How to use + 2.0 Starting FCE Ultra + 2.1 Using FCE Ultra + 3. Notes + 3.0 Platform Specific Notes + 3.1 Network Play Notes + 3.2 VS Unisystem Notes + 3.3 Famicom Disk System Notes + 3.4 Light Gun Notes + 3.5 Palette Notes + 3.6 Compressed File Notes + 3.7 Game Genie Notes + 4. Extra + 4.0 Contacting the author + 4.1 Credits + diff --git a/Documentation/rel/top.dos b/Documentation/rel/top.dos new file mode 100644 index 0000000..372f204 --- /dev/null +++ b/Documentation/rel/top.dos @@ -0,0 +1,8 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + diff --git a/Documentation/rel/top.linux b/Documentation/rel/top.linux new file mode 100644 index 0000000..372f204 --- /dev/null +++ b/Documentation/rel/top.linux @@ -0,0 +1,8 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + diff --git a/Documentation/rel/top.win b/Documentation/rel/top.win new file mode 100644 index 0000000..372f204 --- /dev/null +++ b/Documentation/rel/top.win @@ -0,0 +1,8 @@ + FCE Ultra + 0.81 + + http://fceultra.sourceforge.net/ + + +What is new: + diff --git a/Documentation/tech/README.now b/Documentation/tech/README.now new file mode 100644 index 0000000..4575e0c --- /dev/null +++ b/Documentation/tech/README.now @@ -0,0 +1,6 @@ +Many(possibly all) of these documents contain flaws or are incomplete, so +don't pull out your hair if there are inconsistencies between the documents, +what's in FCE Ultra, and what you observe. That's not to say that FCE Ultra +doesn't have its share of (emulation) flaws, though... + +For many more NES-related documents, try http://nesdev.parodius.com diff --git a/Documentation/tech/README.sound b/Documentation/tech/README.sound new file mode 100644 index 0000000..cce9504 --- /dev/null +++ b/Documentation/tech/README.sound @@ -0,0 +1,2 @@ +Sound information is in the "cpu" subdirectory, due to the intimate +relationship between the sound circuitry and the cpu. diff --git a/Documentation/tech/UNIF_current.txt b/Documentation/tech/UNIF_current.txt new file mode 100644 index 0000000..31e17c8 --- /dev/null +++ b/Documentation/tech/UNIF_current.txt @@ -0,0 +1,302 @@ +**************************************************************************** + UNIF file format specifications + (Universal NES Image) file format + + Created by Tennessee Carmel-Veilleux (veilleux@ameth.org) + REV 7b, November 28th, 2000 + + +***********THIS IS AN OPEN STANDARD. IT IS OPEN TO SUGGESTIONS************** + +Overview +-------- +The UNIF format is a portable, flexible REPLACEMENT of the NES standard +(Designed by Marat Fayzullin). It is a chunked file format in the lines of +the Amiga IFF (LBM), Microsoft RIFF (WAV) and Autodesk 3D studio mesh +files (3DS). The goal of having a chunked definition is to provide +flexibility and ease of implementation, as data is described in blocks +with type IDs referring to them and header information to provide a +selective data reading. The format uses symetric data conversion for +numerical compatibility between the different platforms' byte ordering. +The ordering used is the 6502 Byte order (Intel), so that no more +bickering arrises from people who do not appreciate Network Byte Ordering +(Motorola). + +*** +The extension suggested for use with this format is .UNF (.UNIF under Operating +systems which support longer extensions). +*** + +Byte ordering +------------- +Byte ordering used throughout the file for DWORDs and WORDs is 6502 Byte +order. The 6502 byte order is LSB, least significant byte first +(Little-endian): + + 3 2 1 0 <- Byte order for MSB (Network Byte order, m68k, PowerPC) + 0 1 2 3 <- Byte order for LSB (80x86, 6502, Z80) + +Care must be taken to convert the WORDs and DWORDs if you are using a +non-LSB platform (Mac, Amiga, some others). + +File format +----------- +00h-1Fh : Header +20h-EOF : Chunks + +I can not think of an easier format to describe :) + +Header +------ +The 4-byte header contains the string "UNIF" , NON null-terminated, case +as written. This is for identification purposes, a little like .NES' "NES^Z" +It is followed by Revision number and reserved bytes. + +Format: 00h-03h: "UNIF" tag identifier + 04h-07h: DWORD -> Revision number, currently 4 + 08h-1Fh: Reserved for future usage + +Sample structure: + +structure UNIF_header [ + char identification[4]; /* MUST be "UNIF" */ + dword revision; /* Revision number */ + byte expansion[24]; +]; + +Chunks +------ +Each chunks is composed of 3 distinct elements: + 00h-03h: Chunk ID string + 04h-07h: DWORD -> Block Length of Data + 08h-?? : Data + +All the chunks are written sequentially in the file. If you do not understand +a chunk by its ID, simply jump over it using the data length information. +*** ALL THE CHUNKS ARE OPTIONAL *** +That means that there are NO mandatory chunks, and you support only the +ONES YOU WISH, passing over the others while you are interpreting the +file. + +Sample structure: + +structure UNIF_chunk [ + char chunk_ID[4]; /* Chunk identification string. Can also be considered a + number. ASCII format */ + dword length; /* Data length, in little-endian format */ +]; + +The different chunks: +--------------------- +******************************************************************************* +How chunks are described: +ID field: Contains the 4-characters string identifier for the chunk + +Length: Length of the block. If it is "??", then the block may have + variable data size depending on the cartridge. + +Revision: First revision in which the chunk appeared. If your reader supports + a lower revision, you might be unable to read the chunk, simply pass + over it. The Revision used by the cart is written in the header. The + number represents the Revision Number of the most recent chunk + contained in the file. Example : If you have 5 chunks of revision 1 + and 2 chunks of revision 4 in the file, the Revision number in the + header will be 4. + +Description: Complete description of the contents and encoding of the chunk +******************************************************************************* + +ID: [MAPR] +Length: ?? (Suggested max: 32 chars) +Revision: 1 +Description: This is supplemental information about the mapper. DO NOT USE + A MAPPER NUMBER HERE ! Rather use the BOARD NAME. There is + already a list in progress describing each NES cart and the + board it uses. The string is NULL-TERMINATED. + +examples: N,R,O,M,,0 -> This "No mapper" + U,N,R,O,M,0 -> This is (LS161+LS32) + +NOTA: This mapper organization suggests that emulators must be rewritten +to emulate the ACTUAL CARTRIDGE HARDWARE, and not solely a case of another +mapper number. That means you have to make for UNROM: +1- Mapper handler (74LS161+74LS32) +2- CHR-RAM Handler + +Those components can be reused, since many boards only have a slight +difference in them compared to similar ones. + +**IT SHOULD BE NOTED THAT**: A board name tells you EVERYTHING there is to +know about a board. You do not need other chunks to tell you how much RAM +there is, or if it is VRAM. It is all implied by the board name. A list +will soon be distributed containing board name information. + +Address of board table for North American Games and Board Names description: +http://www.parodius.com/~veilleux/boardtable.txt +http://www.parodius.com/~veilleux/boardnames + +ID: [READ] +Length: ?? +Revision: 1 +Description: Commentaries for the user of the ROM image. In the case of a +homebrew game, this can be very useful to store credit and maker +information. *** This could be "Incitation to littering". Please do not +put garbage in there. It is meant for either mapper information or +licensing information for homebrew games.*** + + +ID: [NAME] +Length: ?? +Revision: 1 +Description: NULL-terminated string containing the name of the game. If not + present, use the filename as the name. + +ID: [TVCI] +Length: BYTE +Revision: 6 +Description: Television Standards Compatability Information set to: +0- Originally NTSC cartridge +1- Originally PAL cartridge +2- Does not matter +NOTE: ALL North American carts that are dumps of the North American +Version are NTSC. All licensed famicom games are NTSC. + +ID: [DINF] +Length: 204 +Revision: 2 +Description: Dumper information block: + structure dumper_info [ + + char dumper_name[100]; /* NULL-terminated string containing the name + of the person who dumped the cart. */ + byte day; /* Day of the month when cartridge was dumped */ + byte month; /* Month of the year when cartridge was dumped */ + word year; /* Year during which the cartridge was dumped */ + char dumper_agent[100]; /* NULL-terminated string containing the name of + the ROM-dumping means used */ + ] + +ID: [CTRL] +Length: BYTE +Revision: 7 +Description: Bitfield containing information about the controllers used by the +cartridge. + +Bit 0: Regular Joypad +Bit 1: Zapper +Bit 2: R.O.B +Bit 3: Arkanoid Controller +Bit 4: Power Pad +Bit 5: Four-Score adapter +Bit 6: Expansion (Do not touch) +Bit 7: Expansion (Do not touch) + +ID: [PCK0] through [PCKF] +Length: DWORD +Reivision: 5 +Description: This block contains a 32-bit CRC which can be used to make +sure that the ROM content matches a checksum when burning on EPROM. This +block provides a checksum for [PRG0] through [PRGF] inclusively + +ID: [CCK0] through [CCKF] +Length: DWORD +Reivision: 5 +Description: This block contains a 32-bit CRC which can be used to make +sure that the ROM content matches a checksum when burning on EPROM. This +block provides a checksum for [CHR0] through [CHRF] inclusively + +ID: [PRG0] through [PRGF] +Length: ?? +Revision: 4 +Description: Chunks containing the Binary data of the PRG ROM. If there +are more than 1 PRG chips on the PRG bus, use PRG1, PRG2, PRG4, etc. +The way PRGs are handled depends on the mapper and emulator. Most generaly +(99%), only use PRG0. (Some carts have been witnessed with 8 PRG ROMs). + +ID: [CHR0] through [CHRF] +Length: ?? +Revision: 4 +Description: Chunks containing the binary data of the CHR ROM. If there +are more than 1 CHR chips on the CHR bus, use CHR1, CHR2, CHR4, etc. The +way CHRs are handled depends on the mapper and emulator. Most generaly +(99%), only CHR0 is used. + +ID: [BATR] +Length: BYTE +Revision: 5 +Description: The presence of this block indicates that the board indeed +contains a battery. This is necessary because many boards have the +capability of a battery (the traces and holes are there), but they only +use RAM and don't add the battery at manufacturing time. Examples: + * SAROM: MMC1B, PRG ROM, CHR ROM, optional 8k of RAM (battery) + * SKROM: MMC1B, PRG ROM, CHR ROM, 8k optional RAM (battery) + +Both these boards (SAROM and SKROM) can have a battery, but usually they +don't have it. + +ID: [VROR] +Length: BYTE +Revision: 5 +Description: This is a VRAM Override. If this chunk is present, then the +CHR-ROM area will be considered as RAM even if ROM is present. This +overrides board identification. This is present so that homemade carts +which use NROM or others and replace the CHR-ROM with CHR-RAM can still be +interpreted (since NROM is always CHR-ROM in commercial games). + +ID: [MIRR] +Length: BYTE +Revision: 5 +Description: This chunk tells you how the hardwired mirroring is setup on +the board. The board name CANNOT tell you (in most cases) what the +mirroring is, since the all have solder pads to select the mirroring at +manufacturing time. The following values are legal: + + * $00 - Horizontal Mirroring (Hard Wired) + * $01 - Vertical Mirroring (Hard Wired) + * $02 - Mirror All Pages From $2000 (Hard Wired) + * $03 - Mirror All Pages From $2400 (Hard Wired) + * $04 - Four Screens of VRAM (Hard Wired) + * $05 - Mirroring Controlled By Mapper Hardware + +Conclusion +---------- +This ends the specification for Revision 6 of UNIF. If you have ANY +suggestions to make regarding the UNIF file format, such as chunk ideas or +modifications, or would like to collaborate to the elaboration and design +process, e-mail me at veilleux@ameth.org. + +A multi-platform C Code Library for UNIF support was made by Evan Teran. It +is available at http://www.pretendo.org/~proxy/. + +[References] +{.NES file format specifications} by Marat Fayzullin (fms@cs.umd.edu) +{NESDEV mailing list} by Various authors +{NES technical documentation} by Jeremy Chadwick (yoshi@parodius.com) + +[Credits] +Neal Tew for his neat emulator and great contribution to the NESdev +community. + +Jeremy Chadwick (yoshi@parodius.com) for his contribution to the NESdev +community and great advice over the time. + +Mark Knibbs (mark_k@iname.com) for his excellent web site as well as his +more than honorable contribution to the NES world + +Matthew Conte (itsbroke@classicgaming.com) for his CajoNES and Nofrendo +programs + +Michael Iwaniec (mrbananmos@yahoo.com) for his interest in UNIF and +constructive criticism. + +Kevin Horton (khorton@iquest.net) for his proposals and support of +UNIF. He's also been a fantastic help to me in my learning curve of the +NES's hardware aspects. + +/Firebug/ (firebug@cfl.rr.com) for the ideas brought with NIFF + +T. Alex Reed {W1k} (rizen@netzero.net) for suggestions of additions +to UNIF. He also pointed out some mistakes in the original specifications. + +Evan Teran {PrOxY} (emt3734@rit.edu) for making suggestions as well as +writing a .NES->UNIF converter and the LIB_UNIF library. diff --git a/Documentation/tech/cpu/4017.txt b/Documentation/tech/cpu/4017.txt new file mode 100644 index 0000000..25cb839 --- /dev/null +++ b/Documentation/tech/cpu/4017.txt @@ -0,0 +1,97 @@ +This is an email posted to nesdev by Ki a while back. I have removed one +line at the end regarding the B flag of the cpu(the information was +incorrect, which Ki noted in a later email). + +-------------------------------------------------------------------------------- + + By reading Brad's NESSOUND document, we know that there is a +"frame counter" in the NES/FC APU. I would like to post +some more on this. + + The frame counter is reset upon any write to $4017. It is +reset at system power-on as well, but is NOT reset upon +system reset. + + Thanks to Samus Aran, we now know the exact period of the +PPU's single frame. In another words, we are now sure that +the NMI occurs on every 29780 2/3 CPU cycles. + + However, the APU's single frame is NOT 29780 2/3 CPU cycles. +What I mean by "APU's single frame" here is that it is the +number of CPU cycles taken between the frame IRQs. + + The APU's single frame seems to be: + + 1789772.727... / 60 = 29829 6/11 [CPU CYCLE] + + Below is a simple diagram which shows the difference +in periods of the PPU's single frame and the APU's. + + + RESET 29780 2/3 CPU CYCLES NMI +PPU |------------------------------------------| + | 29829 6/11 CPU CYCLES IRQ +APU |----------|----------|----------|----------| + + + Note that if you write $00 to $4017 on every NMI, the frame +IRQ would NEVER go off even if it is enabled. This is because +the the period of NMI is slightly shorter than the period of +the frame IRQ. This causes the frame counter to be reset +before the frame IRQ goes off. + +When you write zero to bit 7 of $4017, the frame counter will +be reset, and the first sound update will be done after 7457 CPU +cycles (i.e. 29829/4). 2nd update will be done 7457 after that, +same goes for 3rd update and 4th update, but the frame IRQ occurs +on 4th update, resetting the frame counter as well. + +When you write 1 to bit 7 of $4017, the frame counter will be +reset, but the first sound update will occur at the same time. +2nd, 3rd, and 4th update will be done after 7457, 14914, 22371 +CPU cycles after the first update respectively, but the 5th +update will be 14914 cycles after the 4th update. This causes +sound output to last 1.25 times longer than that of bit 7 = 0. + + +$4017W: + +o when the MSB of $4017 is 0: + +bit7=0 + |---------|---------|---------|---------|---------|---------|---- + 1st 2nd 3rd 4th 5th(1st) 6th(2nd) + + +o when the MSB of $4017 is 1: + +bit7=1 + |---------|---------|---------|-------------------|---------|---- + 1st 2nd 3rd 4th 5th(1st) 6th(2nd) + + +On 1st, 3rd, 5th, ... updates, the envelope decay and the +linear counter are updated. + +On 2nd, 4th, 6th, ... updates, the envelope decay, the +linear counter, the length counter, and the frequency sweep +are updated. +---- + + The original info was provided by goroh, and verified by me. +However, it could still be wrong. Please tell me if you +find anything wrong. +---- + +(Correction from my last posting) + + I have checked once again and it turned out that the frame IRQ +was NOT disabled upon system reset. What actually prevented the +frame IRQ to occur after system reset was, in fact, the I flag. +I checked this flag shortly after system reset (right after stack +pointer was initialized), and the flag was 1, although I never +executed "sei" after reset. Therefore the I flag of the PR2A03G +is 1 on system reset. + + Thanks Matthew Conte and Samus Aran for pointing out the +inaccuracy. diff --git a/Documentation/tech/cpu/6502_cpu.txt b/Documentation/tech/cpu/6502_cpu.txt new file mode 100644 index 0000000..938556c --- /dev/null +++ b/Documentation/tech/cpu/6502_cpu.txt @@ -0,0 +1,1537 @@ +# +# $Id: 6502_cpu.txt,v 1.1 2002/05/21 00:42:27 xodnizel Exp $ +# +# This file is part of Commodore 64 emulator +# and Program Development System. +# +# See README for copyright notice +# +# This file contains documentation for 6502/6510/8500/8502 instruction set. +# +# +# Written by +# John West (john@ucc.gu.uwa.edu.au) +# Marko MŠkelŠ (msmakela@kruuna.helsinki.fi) +# +# +# $Log: 6502_cpu.txt,v $ +# Revision 1.1 2002/05/21 00:42:27 xodnizel +# updates +# +# Revision 1.8 1994/06/03 19:50:04 jopi +# Patchlevel 2 +# +# Revision 1.7 1994/04/15 13:07:04 jopi +# 65xx Register descriptions added +# +# Revision 1.6 1994/02/18 16:09:36 jopi +# +# Revision 1.5 1994/01/26 16:08:37 jopi +# X64 version 0.2 PL 1 +# +# Revision 1.4 1993/11/10 01:55:34 jopi +# +# Revision 1.3 93/06/21 13:37:18 jopi +# X64 version 0.2 PL 0 +# +# Revision 1.2 93/06/21 13:07:15 jopi +# *** empty log message *** +# +# + + Note: To extract the uuencoded ML programs in this article most + easily you may use e.g. "uud" by Edwin Kremer , + which extracts them all at once. + + +Documentation for the NMOS 65xx/85xx Instruction Set + + 6510 Instructions by Addressing Modes + 6502 Registers + 6510/8502 Undocumented Commands + Register selection for load and store + Decimal mode in NMOS 6500 series + 6510 features + Different CPU types + 6510 Instruction Timing + How Real Programmers Acknowledge Interrupts + Memory Management + Autostart Code + Notes + References + + +6510 Instructions by Addressing Modes + +off- ++++++++++ Positive ++++++++++ ---------- Negative ---------- +set 00 20 40 60 80 a0 c0 e0 mode + ++00 BRK JSR RTI RTS NOP* LDY CPY CPX Impl/immed ++01 ORA AND EOR ADC STA LDA CMP SBC (indir,x) ++02 t t t t NOP*t LDX NOP*t NOP*t ? /immed ++03 SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* (indir,x) ++04 NOP* BIT NOP* NOP* STY LDY CPY CPX Zeropage ++05 ORA AND EOR ADC STA LDA CMP SBC Zeropage ++06 ASL ROL LSR ROR STX LDX DEC INC Zeropage ++07 SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* Zeropage + ++08 PHP PLP PHA PLA DEY TAY INY INX Implied ++09 ORA AND EOR ADC NOP* LDA CMP SBC Immediate ++0a ASL ROL LSR ROR TXA TAX DEX NOP Accu/impl ++0b ANC** ANC** ASR** ARR** ANE** LXA** SBX** SBC* Immediate ++0c NOP* BIT JMP JMP () STY LDY CPY CPX Absolute ++0d ORA AND EOR ADC STA LDA CMP SBC Absolute ++0e ASL ROL LSR ROR STX LDX DEC INC Absolute ++0f SLO* RLA* SRE* RRA* SAX* LAX* DCP* ISB* Absolute + ++10 BPL BMI BVC BVS BCC BCS BNE BEQ Relative ++11 ORA AND EOR ADC STA LDA CMP SBC (indir),y ++12 t t t t t t t t ? ++13 SLO* RLA* SRE* RRA* SHA** LAX* DCP* ISB* (indir),y ++14 NOP* NOP* NOP* NOP* STY LDY NOP* NOP* Zeropage,x ++15 ORA AND EOR ADC STA LDA CMP SBC Zeropage,x ++16 ASL ROL LSR ROR STX y) LDX y) DEC INC Zeropage,x ++17 SLO* RLA* SRE* RRA* SAX* y) LAX* y) DCP* ISB* Zeropage,x + ++18 CLC SEC CLI SEI TYA CLV CLD SED Implied ++19 ORA AND EOR ADC STA LDA CMP SBC Absolute,y ++1a NOP* NOP* NOP* NOP* TXS TSX NOP* NOP* Implied ++1b SLO* RLA* SRE* RRA* SHS** LAS** DCP* ISB* Absolute,y ++1c NOP* NOP* NOP* NOP* SHY** LDY NOP* NOP* Absolute,x ++1d ORA AND EOR ADC STA LDA CMP SBC Absolute,x ++1e ASL ROL LSR ROR SHX**y) LDX y) DEC INC Absolute,x ++1f SLO* RLA* SRE* RRA* SHA**y) LAX* y) DCP* ISB* Absolute,x + + ROR intruction is available on MC650x microprocessors after + June, 1976. + + Legend: + + t Jams the machine + *t Jams very rarely + * Undocumented command + ** Unusual operation + y) indexed using Y instead of X + () indirect instead of absolute + +Note that the NOP instructions do have other addressing modes than the +implied addressing. The NOP instruction is just like any other load +instruction, except it does not store the result anywhere nor affects the +flags. + +6502 Registers + +The NMOS 65xx processors are not ruined with too many registers. In addition +to that, the registers are mostly 8-bit. Here is a brief description of each +register: + + PC Program Counter + This register points the address from which the next instruction + byte (opcode or parameter) will be fetched. Unlike other + registers, this one is 16 bits in length. The low and high 8-bit + halves of the register are called PCL and PCH, respectively. The + Program Counter may be read by pushing its value on the stack. + This can be done either by jumping to a subroutine or by causing + an interrupt. + S Stack pointer + The NMOS 65xx processors have 256 bytes of stack memory, ranging + from $0100 to $01FF. The S register is a 8-bit offset to the stack + page. In other words, whenever anything is being pushed on the + stack, it will be stored to the address $0100+S. + + The Stack pointer can be read and written by transfering its value + to or from the index register X (see below) with the TSX and TXS + instructions. + P Processor status + This 8-bit register stores the state of the processor. The bits in + this register are called flags. Most of the flags have something + to do with arithmetic operations. + + The P register can be read by pushing it on the stack (with PHP or + by causing an interrupt). If you only need to read one flag, you + can use the branch instructions. Setting the flags is possible by + pulling the P register from stack or by using the flag set or + clear instructions. + + Following is a list of the flags, starting from the 8th bit of the + P register (bit 7, value $80): + N Negative flag + This flag will be set after any arithmetic operations + (when any of the registers A, X or Y is being loaded + with a value). Generally, the N flag will be copied from + the topmost bit of the register being loaded. + + Note that TXS (Transfer X to S) is not an arithmetic + operation. Also note that the BIT instruction affects + the Negative flag just like arithmetic operations. + Finally, the Negative flag behaves differently in + Decimal operations (see description below). + V oVerflow flag + Like the Negative flag, this flag is intended to be used + with 8-bit signed integer numbers. The flag will be + affected by addition and subtraction, the instructions + PLP, CLV and BIT, and the hardware signal -SO. Note that + there is no SEV instruction, even though the MOS + engineers loved to use East European abbreviations, like + DDR (Deutsche Demokratische Republik vs. Data Direction + Register). (The Russian abbreviation for their former + trade association COMECON is SEV.) The -SO (Set + Overflow) signal is available on some processors, at + least the 6502, to set the V flag. This enables response + to an I/O activity in equal or less than three clock + cycles when using a BVC instruction branching to itself + ($50 $FE). + + The CLV instruction clears the V flag, and the PLP and + BIT instructions copy the flag value from the bit 6 of + the topmost stack entry or from memory. + + After a binary addition or subtraction, the V flag will + be set on a sign overflow, cleared otherwise. What is a + sign overflow? For instance, if you are trying to add + 123 and 45 together, the result (168) does not fit in a + 8-bit signed integer (upper limit 127 and lower limit + -128). Similarly, adding -123 to -45 causes the + overflow, just like subtracting -45 from 123 or 123 from + -45 would do. + + Like the N flag, the V flag will not be set as expected + in the Decimal mode. Later in this document is a precise + operation description. + + A common misbelief is that the V flag could only be set + by arithmetic operations, not cleared. + 1 unused flag + To the current knowledge, this flag is always 1. + B Break flag + This flag is used to distinguish software (BRK) + interrupts from hardware interrupts (IRQ or NMI). The B + flag is always set except when the P register is being + pushed on stack when jumping to an interrupt routine to + process only a hardware interrupt. + + The official NMOS 65xx documentation claims that the BRK + instruction could only cause a jump to the IRQ vector + ($FFFE). However, if an NMI interrupt occurs while + executing a BRK instruction, the processor will jump to + the NMI vector ($FFFA), and the P register will be + pushed on the stack with the B flag set. + D Decimal mode flag + This flag is used to select the (Binary Coded) Decimal + mode for addition and subtraction. In most applications, + the flag is zero. + + The Decimal mode has many oddities, and it operates + differently on CMOS processors. See the description of + the ADC, SBC and ARR instructions below. + I Interrupt disable flag + This flag can be used to prevent the processor from + jumping to the IRQ handler vector ($FFFE) whenever the + hardware line -IRQ is active. The flag will be + automatically set after taking an interrupt, so that the + processor would not keep jumping to the interrupt + routine if the -IRQ signal remains low for several clock + cycles. + Z Zero flag + The Zero flag will be affected in the same cases than + the Negative flag. Generally, it will be set if an + arithmetic register is being loaded with the value zero, + and cleared otherwise. The flag will behave differently + in Decimal operations. + C Carry flag + This flag is used in additions, subtractions, + comparisons and bit rotations. In additions and + subtractions, it acts as a 9th bit and lets you to chain + operations to calculate with bigger than 8-bit numbers. + When subtracting, the Carry flag is the negative of + Borrow: if an overflow occurs, the flag will be clear, + otherwise set. Comparisons are a special case of + subtraction: they assume Carry flag set and Decimal flag + clear, and do not store the result of the subtraction + anywhere. + + There are four kinds of bit rotations. All of them store + the bit that is being rotated off to the Carry flag. The + left shifting instructions are ROL and ASL. ROL copies + the initial Carry flag to the lowmost bit of the byte; + ASL always clears it. Similarly, the ROR and LSR + instructions shift to the right. + A Accumulator + The accumulator is the main register for arithmetic and logic + operations. Unlike the index registers X and Y, it has a direct + connection to the Arithmetic and Logic Unit (ALU). This is why + many operations are only available for the accumulator, not the + index registers. + X Index register X + This is the main register for addressing data with indices. It has + a special addressing mode, indexed indirect, which lets you to + have a vector table on the zero page. + Y Index register Y + The Y register has the least operations available. On the other + hand, only it has the indirect indexed addressing mode that + enables access to any memory place without having to use + self-modifying code. + +6510/8502 Undocumented Commands + +-- A brief explanation about what may happen while using don't care states. + + ANE $8B A = (A | #$EE) & X & #byte + same as + A = ((A & #$11 & X) | ( #$EE & X)) & #byte + + In real 6510/8502 the internal parameter #$11 + may occasionally be #$10, #$01 or even #$00. + This occurs when the video chip starts DMA + between the opcode fetch and the parameter fetch + of the instruction. The value probably depends + on the data that was left on the bus by the VIC-II. + + LXA $AB C=Lehti: A = X = ANE + Alternate: A = X = (A & #byte) + + TXA and TAX have to be responsible for these. + + SHA $93,$9F Store (A & X & (ADDR_HI + 1)) + SHX $9E Store (X & (ADDR_HI + 1)) + SHY $9C Store (Y & (ADDR_HI + 1)) + SHS $9B SHA and TXS, where X is replaced by (A & X). + + Note: The value to be stored is copied also + to ADDR_HI if page boundary is crossed. + + SBX $CB Carry and Decimal flags are ignored but the + Carry flag will be set in substraction. This + is due to the CMP command, which is executed + instead of the real SBC. + + ARR $6B This instruction first performs an AND + between the accumulator and the immediate + parameter, then it shifts the accumulator to + the right. However, this is not the whole + truth. See the description below. + +Many undocumented commands do not use AND between registers, the CPU +just throws the bytes to a bus simultaneously and lets the +open-collector drivers perform the AND. I.e. the command called 'SAX', +which is in the STORE section (opcodes $A0...$BF), stores the result +of (A & X) by this way. + +More fortunate is its opposite, 'LAX' which just loads a byte +simultaneously into both A and X. + + $6B ARR + +This instruction seems to be a harmless combination of AND and ROR at +first sight, but it turns out that it affects the V flag and also has +a special kind of decimal mode. This is because the instruction has +inherited some properties of the ADC instruction ($69) in addition to +the ROR ($6A). + +In Binary mode (D flag clear), the instruction effectively does an AND +between the accumulator and the immediate parameter, and then shifts +the accumulator to the right, copying the C flag to the 8th bit. It +sets the Negative and Zero flags just like the ROR would. The ADC code +shows up in the Carry and oVerflow flags. The C flag will be copied +from the bit 6 of the result (which doesn't seem too logical), and the +V flag is the result of an Exclusive OR operation between the bit 6 +and the bit 5 of the result. This makes sense, since the V flag will +be normally set by an Exclusive OR, too. + +In Decimal mode (D flag set), the ARR instruction first performs the +AND and ROR, just like in Binary mode. The N flag will be copied from +the initial C flag, and the Z flag will be set according to the ROR +result, as expected. The V flag will be set if the bit 6 of the +accumulator changed its state between the AND and the ROR, cleared +otherwise. + +Now comes the funny part. If the low nybble of the AND result, +incremented by its lowmost bit, is greater than 5, the low nybble in +the ROR result will be incremented by 6. The low nybble may overflow +as a consequence of this BCD fixup, but the high nybble won't be +adjusted. The high nybble will be BCD fixed in a similar way. If the +high nybble of the AND result, incremented by its lowmost bit, is +greater than 5, the high nybble in the ROR result will be incremented +by 6, and the Carry flag will be set. Otherwise the C flag will be +cleared. + +To help you understand this description, here is a C routine that +illustrates the ARR operation in Decimal mode: + + unsigned + A, /* Accumulator */ + AL, /* low nybble of accumulator */ + AH, /* high nybble of accumulator */ + + C, /* Carry flag */ + Z, /* Zero flag */ + V, /* oVerflow flag */ + N, /* Negative flag */ + + t, /* temporary value */ + s; /* value to be ARRed with Accumulator */ + + t = A & s; /* Perform the AND. */ + + AH = t >> 4; /* Separate the high */ + AL = t & 15; /* and low nybbles. */ + + N = C; /* Set the N and */ + Z = !(A = (t >> 1) | (C << 7)); /* Z flags traditionally */ + V = (t ^ A) & 64; /* and V flag in a weird way. */ + + if (AL + (AL & 1) > 5) /* BCD "fixup" for low nybble. */ + A = (A & 0xF0) | ((A + 6) & 0xF); + + if (C = AH + (AH & 1) > 5) /* Set the Carry flag. */ + A = (A + 0x60) & 0xFF; /* BCD "fixup" for high nybble. */ + + $CB SBX X <- (A & X) - Immediate + +The 'SBX' ($CB) may seem to be very complex operation, even though it +is a combination of the subtraction of accumulator and parameter, as +in the 'CMP' instruction, and the command 'DEX'. As a result, both A +and X are connected to ALU but only the subtraction takes place. Since +the comparison logic was used, the result of subtraction should be +normally ignored, but the 'DEX' now happily stores to X the value of +(A & X) - Immediate. That is why this instruction does not have any +decimal mode, and it does not affect the V flag. Also Carry flag will +be ignored in the subtraction but set according to the result. + + Proof: + +begin 644 vsbx +M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```*D`H#V1*Z`_D2N@09$KJ0>% +M^QBE^VEZJ+$KH#F1*ZD`2"BI`*(`RP`(:-B@.5$K*4#P`E@`H#VQ*SAI`)$K +JD-Z@/[$K:0"1*Y#4J2X@TO\XH$&Q*VD`D2N0Q,;[$+188/_^]_:_OK>V +` +end + + and + +begin 644 sbx +M`0@9$,D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI`*!-D2N@3Y$KH%&1*ZD# +MA?L8I?M*2)`#J1@LJ3B@29$K:$J0`ZGX+*G8R)$K&/BXJ?2B8\L)AOP(:(7] +MV#B@3;$KH$\Q*Z!1\2L(1?SP`0!H1?TIM]#XH$VQ*SAI`)$KD,N@3[$K:0"1 +9*Y#!J2X@TO\XH%&Q*VD`D2N0L<;[$))88-#X +` +end + +These test programs show if your machine is compatible with ours +regarding the opcode $CB. The first test, vsbx, proves that SBX does +not affect the V flag. The latter one, sbx, proves the rest of our +theory. The vsbx test tests 33554432 SBX combinations (16777216 +different A, X and Immediate combinations, and two different V flag +states), and the sbx test doubles that amount (16777216*4 D and C flag +combinations). Both tests have run successfully on a C64 and a Vic20. +They ought to run on C16, +4 and the PET series as well. The tests +stop with BRK, if the opcode $CB does not work as expected. Successful +operation ends in RTS. As the tests are very slow, they print dots on +the screen while running so that you know that the machine has not +jammed. On computers running at 1 MHz, the first test prints +approximately one dot every four seconds and a total of 2048 dots, +whereas the second one prints half that amount, one dot every seven +seconds. + +If the tests fail on your machine, please let us know your processor's +part number and revision. If possible, save the executable (after it +has stopped with BRK) under another name and send it to us so that we +know at which stage the program stopped. + +The following program is a Commodore 64 executable that Marko M"akel"a +developed when trying to find out how the V flag is affected by SBX. +(It was believed that the SBX affects the flag in a weird way, and +this program shows how SBX sets the flag differently from SBC.) You +may find the subroutine at $C150 useful when researching other +undocumented instructions' flags. Run the program in a machine +language monitor, as it makes use of the BRK instruction. The result +tables will be written on pages $C2 and $C3. + +begin 644 sbx-c100 +M`,%XH`",#L&,$,&,$L&XJ8*B@LL7AOL(:(7\N#BM#L$M$,'M$L$(Q?OP`B@` +M:$7\\`,@4,'N#L'0U.X0P=#/SB#0[A+!T,<``````````````)BJ\!>M#L$M +L$,'=_\'0":T2P=W_PM`!8,K0Z:T.P2T0P9D`PID`!*T2P9D`PYD`! + +Other undocumented instructions usually cause two preceding opcodes +being executed. However 'NOP' seems to completely disappear from 'SBC' +code $EB. + +The most difficult to comprehend are the rest of the instructions +located on the '$0B' line. + +All the instructions located at the positive (left) side of this line +should rotate either memory or the accumulator, but the addressing +mode turns out to be immediate! No problem. Just read the operand, let +it be ANDed with the accumulator and finally use accumulator +addressing mode for the instructions above them. + +RELIGION_MODE_ON +/* This part of the document is not accurate. You can + read it as a fairy tale, but do not count on it when + performing your own measurements. */ + +The rest two instructions on the same line, called 'ANE' and 'LXA' +($8B and $AB respectively) often give quite unpredictable results. +However, the most usual operation is to store ((A | #$ee) & X & #$nn) +to accumulator. Note that this does not work reliably in a real 64! +In the Commodore 128 the opcode $8B uses values 8C, CC, EE, and +occasionally 0C and 8E for the OR instead of EE,EF,FE and FF used in +the C64. With a C128 running at 2 MHz #$EE is always used. Opcode $AB +does not cause this OR taking place on 8502 while 6510 always performs +it. Note that this behaviour depends on processor and/or video chip +revision. + +Let's take a closer look at $8B (6510). + + A <- X & D & (A | VAL) + + where VAL comes from this table: + + X high D high D low VAL + even even --- $EE (1) + even odd --- $EE + odd even --- $EE + odd odd 0 $EE + odd odd not 0 $FE (2) + +(1) If the bottom 2 bits of A are both 1, then the LSB of the result may + be 0. The values of X and D are different every time I run the test. + This appears to be very rare. +(2) VAL is $FE most of the time. Sometimes it is $EE - it seems to be random, + not related to any of the data. This is much more common than (1). + + In decimal mode, VAL is usually $FE. + +Two different functions have been discovered for LAX, opcode $AB. One +is A = X = ANE (see above) and the other, encountered with 6510 and +8502, is less complicated A = X = (A & #byte). However, according to +what is reported, the version altering only the lowest bits of each +nybble seems to be more common. + +What happens, is that $AB loads a value into both A and X, ANDing the +low bit of each nybble with the corresponding bit of the old +A. However, there are exceptions. Sometimes the low bit is cleared +even when A contains a '1', and sometimes other bits are cleared. The +exceptions seem random (they change every time I run the test). Oops - +that was in decimal mode. Much the same with D=0. + +What causes the randomness? Probably it is that it is marginal logic +levels - when too much wired-anding goes on, some of the signals get +very close to the threshold. Perhaps we're seeing some of them step +over it. The low bit of each nybble is special, since it has to cope +with carry differently (remember decimal mode). We never see a '0' +turn into a '1'. + +Since these instructions are unpredictable, they should not be used. + +There is still very strange instruction left, the one named SHA/X/Y, +which is the only one with only indexed addressing modes. Actually, +the commands 'SHA', 'SHX' and 'SHY' are generated by the indexing +algorithm. + +While using indexed addressing, effective address for page boundary +crossing is calculated as soon as possible so it does not slow down +operation. As a result, in the case of SHA/X/Y, the address and data +are processed at the same time making AND between them to take place. +Thus, the value to be stored by SAX, for example, is in fact (A & X & +(ADDR_HI + 1)). On page boundary crossing the same value is copied +also to high byte of the effective address. + +RELIGION_MODE_OFF + + +Register selection for load and store + + bit1 bit0 A X Y + 0 0 x + 0 1 x + 1 0 x + 1 1 x x + +So, A and X are selected by bits 1 and 0 respectively, while + ~(bit1|bit0) enables Y. + +Indexing is determined by bit4, even in relative addressing mode, +which is one kind of indexing. + +Lines containing opcodes xxx000x1 (01 and 03) are treated as absolute +after the effective address has been loaded into CPU. + +Zeropage,y and Absolute,y (codes 10x1 x11x) are distinquished by bit5. + + +Decimal mode in NMOS 6500 series + + Most sources claim that the NMOS 6500 series sets the N, V and Z +flags unpredictably when performing addition or subtraction in decimal +mode. Of course, this is not true. While testing how the flags are +set, I also wanted to see what happens if you use illegal BCD values. + + ADC works in Decimal mode in a quite complicated way. It is amazing +how it can do that all in a single cycle. Here's a C code version of +the instruction: + + unsigned + A, /* Accumulator */ + AL, /* low nybble of accumulator */ + AH, /* high nybble of accumulator */ + + C, /* Carry flag */ + Z, /* Zero flag */ + V, /* oVerflow flag */ + N, /* Negative flag */ + + s; /* value to be added to Accumulator */ + + AL = (A & 15) + (s & 15) + C; /* Calculate the lower nybble. */ + + AH = (A >> 4) + (s >> 4) + (AL > 15); /* Calculate the upper nybble. */ + + if (AL > 9) AL += 6; /* BCD fixup for lower nybble. */ + + Z = ((A + s + C) & 255 != 0); /* Zero flag is set just + like in Binary mode. */ + + /* Negative and Overflow flags are set with the same logic than in + Binary mode, but after fixing the lower nybble. */ + + N = (AH & 8 != 0); + V = ((AH << 4) ^ A) & 128 && !((A ^ s) & 128); + + if (AH > 9) AH += 6; /* BCD fixup for upper nybble. */ + + /* Carry is the only flag set after fixing the result. */ + + C = (AH > 15); + A = ((AH << 4) | (AL & 15)) & 255; + + The C flag is set as the quiche eaters expect, but the N and V flags +are set after fixing the lower nybble but before fixing the upper one. +They use the same logic than binary mode ADC. The Z flag is set before +any BCD fixup, so the D flag does not have any influence on it. + +Proof: The following test program tests all 131072 ADC combinations in + Decimal mode, and aborts with BRK if anything breaks this theory. + If everything goes well, it ends in RTS. + +begin 600 dadc +M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH(V1 +M*Q@(I?PI#X7]I?LI#V7]R0J0 FD%J"D/A?VE^RGP9?PI\ C $) ":0^JL @H +ML ?)H) &""@X:5\X!?V%_0AH*3W@ ! ""8"HBD7[$ JE^T7\, 28"4"H**7[ +M9?S0!)@) J@8N/BE^V7\V A%_= G:(3]1?W0(.;[T(?F_-"#:$D8\ )88*D= +0&&4KA?NI &4LA?RI.&S[ A% + +end + + All programs in this chapter have been successfully tested on a Vic20 +and a Commodore 64 and a Commodore 128D in C64 mode. They should run on +C16, +4 and on the PET series as well. If not, please report the problem +to Marko M"akel"a. Each test in this chapter should run in less than a +minute at 1 MHz. + +SBC is much easier. Just like CMP, its flags are not affected by +the D flag. + +Proof: + +begin 600 dsbc-cmp-flags +M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'B@ (3[A/RB XH8:66HL2N@ +M09$KH$R1*XII::BQ*Z!%D2N@4)$K^#BXI?OE_-@(:(7].+BE^^7\"&A%_? ! +5 .;[T./F_-#?RA"_8!@X&#CEY<7% + +end + + The only difference in SBC's operation in decimal mode from binary mode +is the result-fixup: + + unsigned + A, /* Accumulator */ + AL, /* low nybble of accumulator */ + AH, /* high nybble of accumulator */ + + C, /* Carry flag */ + Z, /* Zero flag */ + V, /* oVerflow flag */ + N, /* Negative flag */ + + s; /* value to be added to Accumulator */ + + AL = (A & 15) - (s & 15) - !C; /* Calculate the lower nybble. */ + + if (AL & 16) AL -= 6; /* BCD fixup for lower nybble. */ + + AH = (A >> 4) - (s >> 4) - (AL & 16); /* Calculate the upper nybble. */ + + if (AH & 16) AH -= 6; /* BCD fixup for upper nybble. */ + + /* The flags are set just like in Binary mode. */ + + C = (A - s - !C) & 256 != 0; + Z = (A - s - !C) & 255 != 0; + V = ((A - s - !C) ^ s) & 128 && (A ^ s) & 128; + N = (A - s - !C) & 128 != 0; + + A = ((AH << 4) | (AL & 15)) & 255; + + Again Z flag is set before any BCD fixup. The N and V flags are set +at any time before fixing the high nybble. The C flag may be set in any +phase. + + Decimal subtraction is easier than decimal addition, as you have to +make the BCD fixup only when a nybble overflows. In decimal addition, +you had to verify if the nybble was greater than 9. The processor has +an internal "half carry" flag for the lower nybble, used to trigger +the BCD fixup. When calculating with legal BCD values, the lower nybble +cannot overflow again when fixing it. +So, the processor does not handle overflows while performing the fixup. +Similarly, the BCD fixup occurs in the high nybble only if the value +overflows, i.e. when the C flag will be cleared. + + Because SBC's flags are not affected by the Decimal mode flag, you +could guess that CMP uses the SBC logic, only setting the C flag +first. But the SBX instruction shows that CMP also temporarily clears +the D flag, although it is totally unnecessary. + + The following program, which tests SBC's result and flags, +contains the 6502 version of the pseudo code example above. + +begin 600 dsbc +M 0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@ 'BI&* A/N$_$B@+)$KH':1 +M*S@(I?PI#X7]I?LI#^7]L /I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL KI7RBP +M#ND/.+ )*+ &Z0^P NE?A/T%_87]*+BE^^7\"&BH.+CXI?OE_-@(1?W0FVB$ +8_47]T)3F^]">YOS0FFA)&- $J3C0B%A@ + +end + + Obviously the undocumented instructions RRA (ROR+ADC) and ISB +(INC+SBC) have inherited also the decimal operation from the official +instructions ADC and SBC. The program droradc proves this statement +for ROR, and the dincsbc test proves this for ISB. Finally, +dincsbc-deccmp proves that ISB's and DCP's (DEC+CMP) flags are not +affected by the D flag. + +begin 644 droradc +M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH(V1 +M*S@(I?PI#X7]I?LI#V7]R0J0`FD%J"D/A?VE^RGP9?PI\`C`$)`":0^JL`@H +ML`?)H)`&""@X:5\X!?V%_0AH*3W@`!`""8"HBD7[$`JE^T7\,`28"4"H**7[ +M9?S0!)@)`J@XN/BE^R;\9_S8"$7]T"=HA/U%_=`@YOO0A>;\T(%H21CP`EA@ +2J1T892N%^ZD`92R%_*DX;/L` +` +end + +begin 644 dincsbc +M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'BI&*``A/N$_$B@+)$KH':1 +M*S@(I?PI#X7]I?LI#^7]L`/I!1@I#ZBE_"GPA?VE^RGP"#CE_2GPL`KI7RBP +M#ND/.+`)*+`&Z0^P`NE?A/T%_87]*+BE^^7\"&BH.+CXI?O&_.?\V`A%_="9 +::(3]1?W0DN;[T)SF_-"8:$D8T`2I.-"&6&#\ +` +end + +begin 644 dincsbc-deccmp +M`0@9",D'GL(H-#,IJC(U-JS"*#0T*:HR-@```'B@`(3[A/RB`XH8:7>HL2N@ +M3Y$KH%R1*XII>ZBQ*Z!3D2N@8)$KBFE_J+$KH%61*Z!BD2OX.+BE^^;\Q_S8 +L"&B%_3BXI?OF_,?\"&A%_?`!`.;[T-_F_-#;RA"M8!@X&#CFYL;&Q\?GYP#8 +` +end + + +6510 features + + o PHP always pushes the Break (B) flag as a `1' to the stack. + Jukka Tapanim"aki claimed in C=lehti issue 3/89, on page 27 that the + processor makes a logical OR between the status register's bit 4 + and the bit 8 of the stack pointer register (which is always 1). + He did not give any reasons for this argument, and has refused to clarify + it afterwards. Well, this was not the only error in his article... + + o Indirect addressing modes do not handle page boundary crossing at all. + When the parameter's low byte is $FF, the effective address wraps + around and the CPU fetches high byte from $xx00 instead of $xx00+$0100. + E.g. JMP ($01FF) fetches PCL from $01FF and PCH from $0100, + and LDA ($FF),Y fetches the base address from $FF and $00. + + o Indexed zero page addressing modes never fix the page address on + crossing the zero page boundary. + E.g. LDX #$01 : LDA ($FF,X) loads the effective address from $00 and $01. + + o The processor always fetches the byte following a relative branch + instruction. If the branch is taken, the processor reads then the + opcode from the destination address. If page boundary is crossed, it + first reads a byte from the old page from a location that is bigger + or smaller than the correct address by one page. + + o If you cross a page boundary in any other indexed mode, + the processor reads an incorrect location first, a location that is + smaller by one page. + + o Read-Modify-Write instructions write unmodified data, then modified + (so INC effectively does LDX loc;STX loc;INX;STX loc) + + o -RDY is ignored during writes + (This is why you must wait 3 cycles before doing any DMA -- + the maximum number of consecutive writes is 3, which occurs + during interrupts except -RESET.) + + o Some undefined opcodes may give really unpredictable results. + + o All registers except the Program Counter remain unmodified after -RESET. + (This is why you must preset D and I flags in the RESET handler.) + + +Different CPU types + +The Rockwell data booklet 29651N52 (technical information about R65C00 +microprocessors, dated October 1984), lists the following differences between +NMOS R6502 microprocessor and CMOS R65C00 family: + + + 1. Indexed addressing across page boundary. + NMOS: Extra read of invalid address. + CMOS: Extra read of last instruction byte. + + + 2. Execution of invalid op codes. + NMOS: Some terminate only by reset. Results are undefined. + CMOS: All are NOPs (reserved for future use). + + + 3. Jump indirect, operand = XXFF. + NMOS: Page address does not increment. + CMOS: Page address increments and adds one additional cycle. + + + 4. Read/modify/write instructions at effective address. + NMOS: One read and two write cycles. + CMOS: Two read and one write cycle. + + + 5. Decimal flag. + NMOS: Indeterminate after reset. + CMOS: Initialized to binary mode (D=0) after reset and interrupts. + + + 6. Flags after decimal operation. + NMOS: Invalid N, V and Z flags. + CMOS: Valid flag adds one additional cycle. + + + 7. Interrupt after fetch of BRK instruction. + NMOS: Interrupt vector is loaded, BRK vector is ignored. + CMOS: BRK is executed, then interrupt is executed. + + +6510 Instruction Timing + + The NMOS 6500 series processors always perform at least two reads +for each instruction. In addition to the operation code (opcode), they +fetch the next byte. This is quite efficient, as most instructions are +two or three bytes long. + + The processors also use a sort of pipelining. If an instruction does +not store data in memory on its last cycle, the processor can fetch +the opcode of the next instruction while executing the last cycle. For +instance, the instruction EOR #$FF truly takes three cycles. On the +first cycle, the opcode $49 will be fetched. During the second cycle +the processor decodes the opcode and fetches the parameter #$FF. On +the third cycle, the processor will perform the operation and store +the result to accumulator, but simultaneously it fetches the opcode +for the next instruction. This is why the instruction effectively +takes only two cycles. + + The following tables show what happens on the bus while executing +different kinds of instructions. + + Interrupts + + NMI and IRQ both take 7 cycles. Their timing diagram is much like + BRK's (see below). IRQ will be executed only when the I flag is + clear. IRQ and BRK both set the I flag, whereas the NMI does not + affect its state. + + The processor will usually wait for the current instruction to + complete before executing the interrupt sequence. To process the + interrupt before the next instruction, the interrupt must occur + before the last cycle of the current instruction. + + There is one exception to this rule: the BRK instruction. If a + hardware interrupt (NMI or IRQ) occurs before the fourth (flags + saving) cycle of BRK, the BRK instruction will be skipped, and + the processor will jump to the hardware interrupt vector. This + sequence will always take 7 cycles. + + You do not completely lose the BRK interrupt, the B flag will be + set in the pushed status register if a BRK instruction gets + interrupted. When BRK and IRQ occur at the same time, this does + not cause any problems, as your program will consider it as a + BRK, and the IRQ would occur again after the processor returned + from your BRK routine, unless you cleared the interrupt source in + your BRK handler. But the simultaneous occurrence of NMI and BRK + is far more fatal. If you do not check the B flag in the NMI + routine and subtract two from the return address when needed, the + BRK instruction will be skipped. + + If the NMI and IRQ interrupts overlap each other (one interrupt + occurs before fetching the interrupt vector for the other + interrupt), the processor will most probably jump to the NMI + vector in every case, and then jump to the IRQ vector after + processing the first instruction of the NMI handler. This has not + been measured yet, but the IRQ is very similar to BRK, and many + sources state that the NMI has higher priority than IRQ. However, + it might be that the processor takes the interrupt that comes + later, i.e. you could lose an NMI interrupt if an IRQ occurred in + four cycles after it. + + After finishing the interrupt sequence, the processor will start + to execute the first instruction of the interrupt routine. This + proves that the processor uses a sort of pipelining: it finishes + the current instruction (or interrupt sequence) while reading the + opcode of the next instruction. + + RESET does not push program counter on stack, and it lasts + probably 6 cycles after deactivating the signal. Like NMI, RESET + preserves all registers except PC. + + Instructions accessing the stack + + 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 + + RTI + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S R increment S + 4 $0100,S R pull P from stack, increment S + 5 $0100,S R pull PCL from stack, increment S + 6 $0100,S R pull PCH from stack + + RTS + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S R increment S + 4 $0100,S R pull PCL from stack, increment S + 5 $0100,S R pull PCH from stack + 6 PC R increment PC + + PHA, PHP + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S W push register on stack, decrement S + + PLA, PLP + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + 3 $0100,S R increment S + 4 $0100,S R pull register from stack + + JSR + + # address R/W description + --- ------- --- ------------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch low address byte, increment PC + 3 $0100,S R internal operation (predecrement S?) + 4 $0100,S W push PCH on stack, decrement S + 5 $0100,S W push PCL on stack, decrement S + 6 PC R copy low address byte to PCL, fetch high address + byte to PCH + + Accumulator or implied addressing + + # address R/W description + --- ------- --- ----------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R read next instruction byte (and throw it away) + + Immediate addressing + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch value, increment PC + + Absolute addressing + + JMP + + # address R/W description + --- ------- --- ------------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch low address byte, increment PC + 3 PC R copy low address byte to PCL, fetch high address + byte to PCH + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, 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, increment PC + 4 address R read from effective address + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # 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, increment PC + 4 address R read from effective address + 5 address W write the value back to effective address, + and do the operation on it + 6 address W write the new value to effective address + + Write instructions (STA, STX, STY, SAX) + + # 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, increment PC + 4 address W write register to effective address + + Zero page addressing + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, NOP) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from effective address + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from effective address + 4 address W write the value back to effective address, + and do the operation on it + 5 address W write the new value to effective address + + Write instructions (STA, STX, STY, SAX) + + # address R/W description + --- ------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address W write register to effective address + + Zero page indexed addressing + + Read instructions (LDA, LDX, LDY, EOR, AND, ORA, ADC, SBC, CMP, BIT, + LAX, NOP) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from address, add index register to it + 4 address+I* R read from effective address + + Notes: I denotes either index register (X or Y). + + * The high byte of the effective address is always zero, + i.e. page boundary crossings are not handled. + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # address R/W description + --- --------- --- --------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from address, add index register X to it + 4 address+X* R read from effective address + 5 address+X* W write the value back to effective address, + and do the operation on it + 6 address+X* W write the new value to effective address + + Note: * The high byte of the effective address is always zero, + i.e. page boundary crossings are not handled. + + Write instructions (STA, STX, STY, SAX) + + # address R/W description + --- --------- --- ------------------------------------------- + 1 PC R fetch opcode, increment PC + 2 PC R fetch address, increment PC + 3 address R read from address, add index register to it + 4 address+I* W write to effective address + + Notes: I denotes either index register (X or Y). + + * The high byte of the effective address is always zero, + i.e. page boundary crossings are not handled. + + Absolute indexed addressing + + 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. + + Read-Modify-Write instructions (ASL, LSR, ROL, ROR, INC, DEC, + SLO, SRE, RLA, RRA, ISB, DCP) + + # 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 X to low address byte, + increment PC + 4 address+X* R read from effective address, + fix the high byte of effective address + 5 address+X R re-read from effective address + 6 address+X W write the value back to effective address, + and do the operation on it + 7 address+X W write the new value to effective address + + Notes: * The high byte of the effective address may be invalid + at this time, i.e. it may be smaller by $100. + + Write instructions (STA, STX, STY, SHA, SHX, SHY) + + # 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 W write to 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. Because + the processor cannot undo a write to an invalid + address, it always reads from the address first. + + 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. + + Indexed indirect addressing + + Read instructions (LDA, ORA, EOR, AND, ADC, CMP, SBC, LAX) + + # 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 R read from effective address + + Note: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISB, DCP) + + # 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 R read from effective address + 7 address W write the value back to effective address, + and do the operation on it + 8 address W write the new value to effective address + + Note: The effective address is always fetched from zero page, + i.e. the zero page boundary crossing is not handled. + + 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 indexed addressing + + 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. + + Read-Modify-Write instructions (SLO, SRE, RLA, RRA, ISB, DCP) + + # 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 + 7 address+Y W write the value back to effective address, + and do the operation on it + 8 address+Y W write the new value 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. + + 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. + + Absolute indirect addressing (JMP) + + # address R/W description + --- --------- --- ------------------------------------------ + 1 PC R fetch opcode, increment PC + 2 PC R fetch pointer address low, increment PC + 3 PC R fetch pointer address high, increment PC + 4 pointer R fetch low address to latch + 5 pointer+1* R fetch PCH, copy latch to PCL + + Note: * The PCH will always be fetched from the same page + than PCL, i.e. page boundary crossing is not handled. + + How Real Programmers Acknowledge Interrupts + + With RMW instructions: + + ; beginning of combined raster/timer interrupt routine + LSR $D019 ; clear VIC interrupts, read raster interrupt flag to C + BCS raster ; jump if VIC caused an interrupt + ... ; timer interrupt routine + + Operational diagram of LSR $D019: + + # data address R/W + --- ---- ------- --- --------------------------------- + 1 4E PC R fetch opcode + 2 19 PC+1 R fetch address low + 3 D0 PC+2 R fetch address high + 4 xx $D019 R read memory + 5 xx $D019 W write the value back, rotate right + 6 xx/2 $D019 W write the new value back + + The 5th cycle acknowledges the interrupt by writing the same + value back. If only raster interrupts are used, the 6th cycle + has no effect on the VIC. (It might acknowledge also some + other interrupts.) + + With indexed addressing: + + ; acknowledge interrupts to both CIAs + LDX #$10 + LDA $DCFD,X + + Operational diagram of LDA $DCFD,X: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 BD PC R fetch opcode + 2 FD PC+1 R fetch address low + 3 DC PC+2 R fetch address high, add X to address low + 4 xx $DC0D R read from address, fix high byte of address + 5 yy $DD0D R read from right address + + ; acknowledge interrupts to CIA 2 + LDX #$10 + STA $DDFD,X + + Operational diagram of STA $DDFD,X: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 9D PC R fetch opcode + 2 FD PC+1 R fetch address low + 3 DC PC+2 R fetch address high, add X to address low + 4 xx $DD0D R read from address, fix high byte of address + 5 ac $DE0D W write to right address + + With branch instructions: + + ; acknowledge interrupts to CIA 2 + LDA #$00 ; clear N flag + JMP $DD0A + DD0A BPL $DC9D ; branch + DC9D BRK ; return + + You need the following preparations to initialize the CIA registers: + + LDA #$91 ; argument of BPL + STA $DD0B + LDA #$10 ; BPL + STA $DD0A + STA $DD08 ; load the ToD values from the latches + LDA $DD0B ; freeze the ToD display + LDA #$7F + STA $DC0D ; assure that $DC0D is $00 + + Operational diagram of BPL $DC9D: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 10 $DD0A R fetch opcode + 2 91 $DD0B R fetch argument + 3 xx $DD0C R fetch opcode, add argument to PCL + 4 yy $DD9D R fetch opcode, fix PCH + ( 5 00 $DC9D R fetch opcode ) + + ; acknowledge interrupts to CIA 1 + LSR ; clear N flag + JMP $DCFA + DCFA BPL $DD0D + DD0D BRK + + ; Again you need to set the ToD registers of CIA 1 and the + ; Interrupt Control Register of CIA 2 first. + + Operational diagram of BPL $DD0D: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 10 $DCFA R fetch opcode + 2 11 $DCFB R fetch argument + 3 xx $DCFC R fetch opcode, add argument to PCL + 4 yy $DC0D R fetch opcode, fix PCH + ( 5 00 $DD0D R fetch opcode ) + + ; acknowledge interrupts to CIA 2 automagically + ; preparations + LDA #$7F + STA $DD0D ; disable all interrupt sources of CIA2 + LDA $DD0E + AND #$BE ; ensure that $DD0C remains constant + STA $DD0E ; and stop the timer + LDA #$FD + STA $DD0C ; parameter of BPL + LDA #$10 + STA $DD0B ; BPL + LDA #$40 + STA $DD0A ; RTI/parameter of LSR + LDA #$46 + STA $DD09 ; LSR + STA $DD08 ; load the ToD values from the latches + LDA $DD0B ; freeze the ToD display + LDA #$09 + STA $0318 + LDA #$DD + STA $0319 ; change NMI vector to $DD09 + LDA #$FF ; Try changing this instruction's operand + STA $DD05 ; (see comment below). + LDA #$FF + STA $DD04 ; set interrupt frequency to 1/65536 cycles + LDA $DD0E + AND #$80 + ORA #$11 + LDX #$81 + STX $DD0D ; enable timer interrupt + STA $DD0E ; start timer + + LDA #$00 ; To see that the interrupts really occur, + STA $D011 ; use something like this and see how + LOOP DEC $D020 ; changing the byte loaded to $DD05 from + BNE LOOP ; #$FF to #$0F changes the image. + + When an NMI occurs, the processor jumps to Kernal code, which jumps to + ($0318), which points to the following routine: + + DD09 LSR $40 ; clear N flag + BPL $DD0A ; Note: $DD0A contains RTI. + + Operational diagram of BPL $DD0A: + + # data address R/W description + --- ---- ------- --- --------------------------------- + 1 10 $DD0B R fetch opcode + 2 11 $DD0C R fetch argument + 3 xx $DD0D R fetch opcode, add argument to PCL + 4 40 $DD0A R fetch opcode, (fix PCH) + + With RTI: + + ; the fastest possible interrupt handler in the 6500 family + ; preparations + SEI + LDA $01 ; disable ROM and enable I/O + AND #$FD + ORA #$05 + STA $01 + LDA #$7F + STA $DD0D ; disable CIA 2's all interrupt sources + LDA $DD0E + AND #$BE ; ensure that $DD0C remains constant + STA $DD0E ; and stop the timer + LDA #$40 + STA $DD0C ; store RTI to $DD0C + LDA #$0C + STA $FFFA + LDA #$DD + STA $FFFB ; change NMI vector to $DD0C + LDA #$FF ; Try changing this instruction's operand + STA $DD05 ; (see comment below). + LDA #$FF + STA $DD04 ; set interrupt frequency to 1/65536 cycles + LDA $DD0E + AND #$80 + ORA #$11 + LDX #$81 + STX $DD0D ; enable timer interrupt + STA $DD0E ; start timer + + LDA #$00 ; To see that the interrupts really occur, + STA $D011 ; use something like this and see how + LOOP DEC $D020 ; changing the byte loaded to $DD05 from + BNE LOOP ; #$FF to #$0F changes the image. + + When an NMI occurs, the processor jumps to Kernal code, which + jumps to ($0318), which points to the following routine: + + DD0C RTI + + How on earth can this clear the interrupts? Remember, the + processor always fetches two successive bytes for each + instruction. + + A little more practical version of this is redirecting the NMI + (or IRQ) to your own routine, whose last instruction is JMP + $DD0C or JMP $DC0C. If you want to confuse more, change the 0 + in the address to a hexadecimal digit different from the one + you used when writing the RTI. + + Or you can combine the latter two methods: + + DD09 LSR $xx ; xx is any appropriate BCD value 00-59. + BPL $DCFC + DCFC RTI + + This example acknowledges interrupts to both CIAs. + + If you want to confuse the examiners of your code, you can use any +of these techniques. Although these examples use no undefined opcodes, +they do not necessarily run correctly on CMOS processors. However, the +RTI example should run on 65C02 and 65C816, and the latter branch +instruction example might work as well. + + The RMW instruction method has been used in some demos, others were +developed by Marko M"akel"a. His favourite is the automagical RTI +method, although it does not have any practical applications, except +for some time dependent data decryption routines for very complicated +copy protections. + + diff --git a/Documentation/tech/cpu/NESSOUND.txt b/Documentation/tech/cpu/NESSOUND.txt new file mode 100644 index 0000000..bb6d059 --- /dev/null +++ b/Documentation/tech/cpu/NESSOUND.txt @@ -0,0 +1,697 @@ +The NES sound channel guide 1.8 +Written by Brad Taylor. +btmine@hotmail.com + +Last updated: July 27th, 2000. + +All results were obtained by studying prior information available (from +nestech 1.00, and postings on NESDev from miscellanious people), and through +a series of experiments conducted by me. Results acquired by individuals +prior to my reverse-engineering have been double checked, and final results +have been confirmed. Credit is due to those individual(s) who contributed +any information in regards to the the miscellanious sound channels wihtin +the NES. + +A special thanks goes out to Matthew Conte, for his expertise on +pseudo-random number generation (amoung other things), which allowed for the +full reverse engineering of the NES's noise channel to take place. Without +his help, I would still be trying to find a needle in a haystack, as far as +the noise's method of pseudo-random number generation goes. Additionally, +his previous findings / reverse engineering work on the NES's sound hardware +really got the ball of NES sound emulation rolling. If it weren't for Matt's +original work, this document wouldn't exist. + +Thanks to Kentaro Ishihara, for his excellent work on finding the difference +in upward frequency sweep between the 2 square wave channels. + +**************** +* Introduction * +**************** + +The 2A03 (NES's integrated CPU) has 4 internal channels to it that have the +ability to generate semi-analog sound, for musical playback purposes. These +channels are 2 square wave channels, one triangle wave channel, and a noise +generation channel. This document will go into full detail on every aspect +of the operation and timing of the mentioned sound channels. + + +******************* +* Channel details * +******************* + +Each channel has different characteristics to it that make up it's +operation. + +The square channel(s) have the ability to generate a square wave frequency +in the range of 54.6 Hz to 12.4 KHz. It's key features are frequency sweep +abilities, and output duty cycle adjustment. + +The triangle wave channel has the ability to generate an output triangle +wave with a resolution of 4-bits (16 steps), in the range of 27.3 Hz to 55.9 +KHz. The key features this channel has is it's analog triangle wave output, +and it's linear counter, which can be set to automatically disable the +channel's sound after a certain period of time has gone by. + +The noise channel is used for producing random frequencys, which results in +a "noisey" sounding output. Output frequencys can range anywhere from 29.3 +Hz to 447 KHz. It's key feature is it's pseudo- random number generator, +which generates the random output frequencys heard by the channel. + + +***************** +* Frame counter * +***************** + +The 2A03 has an internal frame counter. It has the ability to generate 60 Hz +(1/1 framerate), 120 Hz (1/2 framerate), and 240 Hz (1/4 framerate) signals, +used by some of the sound hardware. The 1/4 framerate is calculated by +taking twice the CPU clock speed (3579545.454545 Hz), and dividing it by +14915 (i.e., the divide-by-14915 counter is decremented on the rising AND +falling edge of the CPU's clock signal). + + +************************ +* Sound hardware delay * +************************ + +After resetting the 2A03, the first time any sound channel(s) length counter +contains a non-zero value (channel is enabled), there will be a 2048 CPU +clock cycle delay before any of the sound hardware is clocked. After the 2K +clock cycles go by, the NES sound hardware will be clocked normally. This +phenomenon only occurs prior to a system reset, and only occurs during the +first 2048 CPU clocks for any sound channel prior to a sound channel being +enabled. + +The information in regards to this delay is only provided to keep this +entire document persistently accurate on the 2A03's sound hardware, but may +not be 100% accurate in itself. I haven't done much tests on the behaviour +of this delay (mainly because I don't care, as I view it as a inconvenience +anyway), so that's why I believe there could be some inaccuracies. + + +************************ +* Register Assignments * +************************ + +The sound hardware internal to the 2A03 has been designated these special +memory addresses in the CPU's memory map. + +$4000-$4003 Square wave 1 +$4004-$4007 Square wave 2 (identical to the first, except for upward +frequency sweeps (see "sweep unit" section)) +$4008-$400B Triangle +$400C-$400F Noise +$4015 Channel enable / length counter status + +Note that $4015 is the only R/W register. All others are write only (attempt +to read them will most likely result in a returned 040H, due to heavy +capacitance on the NES's data bus). Reading a "write only" register, will +have no effect on the specific register, or channel. + +Every sound channel has 4 registers affiliated with it. The description of +the register sets are as follows: + ++----------------+ +| Register set 1 | ++----------------+ + +$4000(sq1)/$4004(sq2)/$400C(noise) bits +--------------------------------------- +0-3 volume / envelope decay rate +4 envelope decay disable +5 length counter clock disable / envelope decay looping enable +6-7 duty cycle type (unused on noise channel) + +$4008(tri) bits +--------------- +0-6 linear counter load register +7 length counter clock disable / linear counter start + + ++----------------+ +| Register set 2 | ++----------------+ + +$4001(sq1)/$4005(sq2) bits +-------------------------- +0-2 right shift amount +3 decrease / increase (1/0) wavelength +4-6 sweep update rate +7 sweep enable + +$4009(tri)/$400D(noise) bits +---------------------------- +0-7 unused + + ++----------------+ +| Register set 3 | ++----------------+ + +$4002(sq1)/$4006(sq2)/$400A(Tri) bits +------------------------------------- +0-7 8 LSB of wavelength + +$400E(noise) bits +----------------- +0-3 playback sample rate +4-6 unused +7 random number type generation + + ++----------------+ +| Register set 4 | ++----------------+ + +$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits +-------------------------------------------------- +0-2 3 MS bits of wavelength (unused on noise channel) +3-7 length counter load register + + ++--------------------------------+ +| length counter status register | ++--------------------------------+ + +$4015(read) +----------- +0 square wave channel 1 +1 square wave channel 2 +2 triangle wave channel +3 noise channel +4 DMC (see "DMC.TXT" for details) +5-6 unused +7 IRQ status of DMC (see "DMC.TXT" for details) + + ++-------------------------+ +| channel enable register | ++-------------------------+ + +$4015(write) +------------ +0 square wave channel 1 +1 square wave channel 2 +2 triangle wave channel +3 noise channel +4 DMC channel (see "DMC.TXT" for details) +5-7 unused + + +************************ +* Channel architecture * +************************ + +This section will describe the internal components making up each individual +channel. Each component will then be described in full detail. + +Device Triangle Noise Square +------ -------- ------ ------ +triangle step generator X +linear counter X +programmable timer X X X +length counter X X X +4-bit DAC X X X +volume/envelope decay unit X X +sweep unit X +duty cycle generator X +wavelength converter X +random number generator X + + ++-------------------------+ +| Triangle step generator | ++-------------------------+ + +This is a 5-bit, single direction counter, and it is only used in the +triangle channel. Each of the 4 LSB outputs of the counter lead to one input +on a corresponding mutually exclusive XNOR gate. The 4 XNOR gates have been +strobed together, which results in the inverted representation of the 4 LSB +of the counter appearing on the outputs of the gates when the strobe is 0, +and a non-inverting action taking place when the strobe is 1. The strobe is +naturally connected to the MSB of the counter, which effectively produces on +the output of the XNOR gates a count sequence which reflects the scenario of +a near- ideal triangle step generator (D,E,F,F,E,D,...,2,1,0,0,1,2,...). At +this point, the outputs of the XNOR gates will be fed into the input of a +4-bit DAC. + +This 5-bit counter will be halted whenever the Triangle channel's length or +linear counter contains a count of 0. This results in a "latching" +behaviour; the counter will NOT be reset to any definite state. + +On system reset, this counter is loaded with 0. + +The counter's clock input is connected directly to the terminal count output +pin of the 11-bit programmable timer in the triangle channel. As a result of +the 5-bit triangle step generator, the output triangle wave frequency will +be 32 times less than the frequency of the triangle channel's programmable +timer is set to generate. + + ++----------------+ +| Linear counter | ++----------------+ + +The linear counter is only found in the triangle channel. It is a 7-bit +presettable down counter, with a decoded output condition of 0 available +(not exactly the same as terminal count). Here's the bit assignments: + +$4008 bits +---------- +0-6 bits 0-6 of the linear counter load register (NOT the linear counter +itself) +7 linear counter start + +The counter is clocked at 240 Hz (1/4 framerate), and the calculated length +in frames is 0.25*N, where N is the 7-bit loaded value. The counter is +always being clocked, except when 0 appears on the output of the counter. At +this point, the linear counter & triangle step counter clocks signals are +disabled, which results in both counters latching their current state (the +linear counter will stay at 0, and the triangle step counter will stop, and +the channel will be silenced due to this). + +The linear counter has 2 modes: load, and count. When the linear counter is +in load mode, it essentially becomes transparent (i.e. whatever value is +currently in, or being written to $4008, will appear on the output of the +counter). Because of this, no count action can occur in load mode. When the +mode changes from load to count, the counter will now latch the value +currently in it, and start counting down from there. In the count mode, the +current value of $4008 is ignored by the counter (but still retained in +$4008). Described below is how the mode of the linear counter is set: + +Writes to $400B +--------------- +cur mode +--- ---- +1 load +0 load (during the write cycle), count + +Cur is the current state of the MSB of $4008. + +Writes to $4008 +--------------- +old new mode +--- --- ---- +0 X count +1 0 no change (during the write cycle), count +1 1 no change + +Old and new represent the state(s) of the MSB of $4008. Old is the value +being replaced in the MSB of $4008 on the write, and new is the value +replacing the old one. + +"no change" indicates that the mode of the linear counter will not change +from the last. + + ++--------------------+ +| Programmable timer | ++--------------------+ + +The programmable timer is a 11-bit presettable down counter, and is found in +the square, triangle, and noise channel(s). The bit assignments are as +follows: + +$4002(sq1)/$4006(sq2)/$400A(Tri) bits +------------------------------------- +0-7 represent bits 0-7 of the 11-bit wavelength + +$4003(sq1)/$4007(sq2)/$400B(Tri) bits +------------------------------------- +0-2 represent bits 8-A of the 11-bit wavelength + +Note that on the noise channel, the 11 bits are not available directly. See +the wavelength converter section, for more details. + +The counter has automatic syncronous reloading upon terminal count +(count=0), therefore the counter will count for N+1 (N is the 11-bit loaded +value) clock cycles before arriving at terminal count, and reloading. This +counter will typically be clocked at the 2A03's internal 6502 speed (1.79 +MHz), and produces an output frequency of 1.79 MHz/(N+1). The terminal +count's output spike length is typically no longer than half a CPU clock. +The TC signal will then be fed to the appropriate device for the particular +sound channel (for square, this terminal count spike will lead to the duty +cycle generator. For the triangle, the spike will be fed to the triangle +step generator. For noise, this signal will go to the random number +generator unit). + + ++----------------+ +| Length counter | ++----------------+ + +The length counter is found in all sound channels. It is essentially a 7-bit +down counter, and is conditionally clocked at a frequency of 60 Hz. + +When the length counter arrives at a count of 0, the counter will be stopped +(stay on 0), and the appropriate channel will be silenced. + +The length counter clock disable bit, found in all the channels, can also be +used to halt the count sequence of the length counter for the appropriate +channel, by writing a 1 out to it. A 0 condition will permit counting +(unless of course, the counter's current count = 0). Location(s) of the +length counter clock disable bit: + +$4000(sq1)/$4004(sq2)/$400C(noise) bits +--------------------------------------- +5 length counter clock disable + +$4008(tri) bits +--------------- +7 length counter clock disable + +To load the length counter with a specified count, a write must be made out +to the length register. Location(s) of the length register: + +$4003(sq1)/$4007(sq2)/$400B(tri)/$400F(noise) bits +-------------------------------------------------- +3-7 length + +The 5-bit length value written, determines what 7-bit value the length +counter will start counting from. A conversion table here will show how the +values are translated. + + +-----------------------+ + | bit3=0 | + +-------+---------------+ + | |frames | + |bits +-------+-------+ + |4-6 |bit7=0 |bit7=1 | + +-------+-------+-------+ + |0 |05 |06 | + |1 |0A |0C | + |2 |14 |18 | + |3 |28 |30 | + |4 |50 |60 | + |5 |1E |24 | + |6 |07 |08 | + |7 |0E |10 | + +-------+-------+-------+ + + +---------------+ + | bit3=1 | + +-------+-------+ + |bits | | + |4-7 |frames | + +-------+-------+ + |0 |7F | + |1 |01 | + |2 |02 | + |3 |03 | + |4 |04 | + |5 |05 | + |6 |06 | + |7 |07 | + |8 |08 | + |9 |09 | + |A |0A | + |B |0B | + |C |0C | + |D |0D | + |E |0E | + |F |0F | + +-------+-------+ + +The length counter's real-time status for each channel can be attained. A 0 +is returned for a zero count status in the length counter (channel's sound +is disabled), and 1 for a non-zero status. Here's the bit description of the +length counter status register: + +$4015(read) +----------- +0 length counter status of square wave channel 1 +1 length counter status of square wave channel 2 +2 length counter status of triangle wave channel +3 length counter status of noise channel +4 length counter status of DMC (see "DMC.TXT" for details) +5-6 unused +7 IRQ status of DMC (see "DMC.TXT" for details) + +Writing a 0 to the channel enable register will force the length counters to +always contain a count equal to 0, which renders that specific channel +disabled (as if it doesn't exist). Writing a 1 to the channel enable +register disables the forced length counter value of 0, but will not change +the count itself (it will still be whatever it was prior to the writing of +1). + +Bit description of the channel enable register: + +$4015(write) +------------ +0 enable square wave channel 1 +1 enable square wave channel 2 +2 enable triangle wave channel +3 enable noise channel +4 enable DMC channel (see "DMC.TXT" for details) +5-7 unused + +Note that all 5 used bits in this register will be set to 0 upon system +reset. + + ++-----------+ +| 4-bit DAC | ++-----------+ + +This is just a standard 4-bit DAC with 16 steps of output voltage +resolution, and is used by all 4 sound channels. + +On the 2A03, square wave 1 & 2 are mixed together, and are available via pin +1. Triangle & noise are available on pin 2. These analog outputs require a +negative current source, to attain linear symmetry on the various output +voltage levels generated by the channel(s) (moreover, to get the sound to be +audible). Since the NES just uses external 100 ohm pull-down resistors, this +results in the output waveforms being of very small amplitude, but with +minimal linearity asymmetry. + + ++------------------------------+ +| Volume / envelope decay unit | ++------------------------------+ + +The volume / envelope decay hardware is found only in the square wave and +noise channels. + +$4000(sq1)/$4004(sq2)/$400C(noise) +---------------------------------- +0-3 volume / envelope decay rate +4 envelope decay disable +5 envelope decay looping enable + +When the envelope decay disable bit (bit 4) is set (1), the current volume +value (bits 0-3) is sent directly to the channel's DAC. However, depending +on certain conditions, this 4-bit volume value will be ignored, and a value +of 0 will be sent to the DAC instead. This means that while the channel is +enabled (producing sound), the output of the channel (what you'll hear from +the DAC) will either be the 4-bit volume value, or 0. This also means that a +4-bit volume value of 0 will result in no audible sound. These conditions +are as follows: + +- When hardware in the channel wants to disable it's sound output (like the +length counter, or sweep unit (square channels only)). + +- On the negative portion of the output frequency signal coming from the +duty cycle / random number generator hardware (square wave channel / noise +channel). + +When the envelope decay disable bit is cleared, bits 0-3 now control the +envelope decay rate, and an internal 4-bit down counter (hereon the envelope +decay counter) now controls the channel's volume level. "Envelope decay" is +used to describe the action of the channel's audio output volume starting +from a certain value, and decreasing by 1 at a fixed (linear) rate (which +produces a "fade-out" sounding effect). This fixed decrement rate is +controlled by the envelope decay rate (bits 0-3). The calculated decrement +rate is 240Hz/(N+1), where N is any value between $0-$F. + +When the channel's envelope decay counter reaches a value of 0, depending on +the status of the envelope decay looping enable bit (bit 5, which is shared +with the length counter's clock disable bit), 2 different things will +happen: + +bit 5 action +----- ------ +0 The envelope decay count will stay at 0 (channel silenced). +1 The envelope decay count will wrap-around to $F (upon the next clock +cycle). The envelope decay counter will then continue to count down +normally. + +Only a write out to $4003/$4007/$400F will reset the current envelope decay +counter to a known state (to $F, the maximum volume level) for the +appropriate channel's envelope decay hardware. Otherwise, the envelope decay +counter is always counting down (by 1) at the frequency currently contained +in the volume / envelope decay rate bits (even when envelope decays are +disabled (setting bit 4)), except when the envelope decay counter contains a +value of 0, and envelope decay looping (bit 5) is disabled (0). + + ++------------+ +| Sweep unit | ++------------+ + +The sweep unit is only found in the square wave channels. The controls for +the sweep unit have been mapped in at $4001 for square 1, and $4005 for +square 2. + +The controls +------------ +Bit 7 when this bit is set (1), sweeping is active. This results in +real-time increasing or decreasing of the the current wavelength value (the +audible frequency will decrease or increase, respectively). The wavelength +value in $4002/3 ($4006/7) is constantly read & updated by the sweep. +Modifying the contents of $4002/3 will be immediately audible, and will +result in the sweep now starting from this new wavelength value. + +Bits 6-4 These 3 bits represent the sweep refresh rate, or the frequency at +which $4002/3 is updated with the new calculated wavelength. The refresh +rate frequency is 120Hz/(N+1), where N is the value written, between 0 and +7. + +Bit 3 This bit controls the sweep mode. When this bit is set (1), sweeps +will decrease the current wavelength value, as a 0 will increase the current +wavelength. + +Bits 2-0 These bits control the right shift amount of the new calculated +sweep update wavelength. Code that shows how the sweep unit calculates a new +sweep wavelength is as follows: + +bit 3 +----- +0 New = Wavelength + (Wavelength >> N) +1 New = Wavelength - (Wavelength >> N) (minus an additional 1, if using +square wave channel 1) + +where N is the the shift right value, between 0-7. + +Note that in decrease mode, for subtracting the 2 values: +1's compliment (NOT) is being used for square wave channel 1 +2's compliment (NEG) is being used for square wave channel 2 + +This information is currently the only known difference between the 2 square +wave channels. + +On each sweep refresh clock, the Wavelength register will be updated with +the New value, but only if all 3 of these conditions are met: + +- bit 7 is set (sweeping enabled) +- the shift value (which is N in the formula) does not equal to 0 +- the channel's length counter contains a non-zero value + +Notes +----- +There are certain conditions that will cause the sweep unit to silence the +channel, and halt the sweep refresh clock (which effectively stops sweep +action, if any). Note that these conditions pertain regardless of any sweep +refresh rate values, or if sweeping is enabled/disabled (via bit 7). + +- an 11-bit wavelength value less than $008 will cause this condition +- if the sweep unit is currently set to increase mode, the New calculated +wavelength value will always be tested to see if a carry (bit $B) was +generated or not (if sweeping is enabled, this carry will be examined before +the Wavelength register is updated) from the shift addition calculation. If +carry equals 1, the channel is silenced, and sweep action is halted. + + ++----------------------+ +| Duty cycle generator | ++----------------------+ + +The duty cycle generator takes the fequency produced from the 11-bit +programmable timer, and uses a 4 bit counter to produce 4 types of duty +cycles. The output frequency is then 1/16 that of the programmable timer. +The duty cycle hardware is only found in the square wave channels. The bit +assignments are as follows: + +$4000(sq1)/$4004(sq2) +--------------------- +6-7 Duty cycle type + + duty (positive/negative) +val in clock cycles +--- --------------- +00 2/14 +01 4/12 +10 8/ 8 +11 12/ 4 + +Where val represents bits 6-7 of $4000/$4004. + +The output frequency at this point will now be fed to the volume/envelope +decay hardware. + + ++----------------------+ +| Wavelength converter | ++----------------------+ + +The wavelength converter is only used in the noise channel. It is used to +convert a given 4-bit value to an 11-bit wavelength, which then is sent to +the noise's own programmable timer. Here is the bit descriptions: + +$400E bits +---------- +0-3 The 4-bit value to be converted + +Below is a conversion chart that shows what 4-bit value will represent the +11-bit wavelength to be fed to the channel's programmable timer: + +value octave scale CPU clock cycles (11-bit wavelength+1) +----- ------ ----- -------------------------------------- +0 15 A 002 +1 14 A 004 +2 13 A 008 +3 12 A 010 +4 11 A 020 +5 11 D 030 +6 10 A 040 +7 10 F 050 +8 10 C 065 +9 9 A 07F +A 9 D 0BE +B 8 A 0FE +C 8 D 17D +D 7 A 1FC +E 6 A 3F9 +F 5 A 7F2 + +Octave and scale information is provided for the music enthusiast programmer +who is more familiar with notes than clock cycles. + + ++-------------------------+ +| Random number generator | ++-------------------------+ + +The noise channel has a 1-bit pseudo-random number generator. It's based on +a 15-bit shift register, and an exclusive or gate. The generator can produce +two types of random number sequences: long, and short. The long sequence +generates 32,767-bit long number patterns. The short sequence generates +93-bit long number patterns. The 93-bit mode will generally produce higher +sounding playback frequencys on the channel. Here is the bit that controls +the mode: + +$400E bits +---------- +7 mode + +If mode=0, then 32,767-bit long number sequences will be produced (32K +mode), otherwise 93-bit long number sequences will be produced (93-bit +mode). + +The following diagram shows where the XOR taps are taken off the shift +register to produce the 1-bit pseudo-random number sequences for each mode. + +mode <----- +---- EDCBA9876543210 +32K ** +93-bit * * + +The current result of the XOR will be transferred into bit position 0 of the +SR, upon the next shift cycle. The 1-bit random number output is taken from +pin E, is inverted, then is sent to the volume/envelope decay hardware for +the noise channel. The shift register is shifted upon recieving 2 clock +pulses from the programmable timer (the shift frequency will be half that of +the frequency from the programmable timer (one octave lower)). + +On system reset, this shift register is loaded with a value of 1. + + diff --git a/Documentation/tech/cpu/dmc.txt b/Documentation/tech/cpu/dmc.txt new file mode 100644 index 0000000..c33f4de --- /dev/null +++ b/Documentation/tech/cpu/dmc.txt @@ -0,0 +1,235 @@ +Delta modulation channel tutorial 1.0 +Written by Brad Taylor + +Last updated: August 20th, 2000. + +All results were obtained by studying prior information available (from +nestech 1.00, and postings on NESDev from miscellanious people), and through +a series of experiments conducted by me. Results aquired by individuals +prior to my reverse-engineering have been double checked, and final results +have been confirmed. Credit is due to those individual(s) who contributed +any information in regards to the DMC. + +Description +----------- + +The delta modulation channel (DMC) is a complex digital network of counters +and registers used to produce analog sound. It's primary function is to play +"samples" from memory, and have an internal counter connected to a digital +to analog converter (DAC) updated accordingly. The channel is able to be +assigned a pointer to a chunk of memory to be played. At timed intervals, +the DMC will halt the 2A03 (NES's CPU) for 1 clock cycle to retrieve the +sample to pe played. This method of playback will be refered to here on as +direct memory access (DMA). Another method of playback known as pulse code +modulation (PCM) is available by the channel, which requires the constant +updating of one of the DMC's memory-mapped registers. + +Registers +--------- + +The DMC has 5 registers assigned to it. They are as follows: + +$4010: play mode and DMA frequency +$4011: delta counter +$4012: play code's starting address +$4013: length of play code +$4015: DMC/IRQ status + +Note that $4015 is the only R/W register. All others are write only (attempt +to read them will most likely result in a returned 040H, due to heavy +capacitance on the NES's data bus). + +$4010 - Play mode and DMA frequency +----------------------------------- +This register is used to control the frequency of the DMA fetches, and to +control the playback mode. + +Bits +---- +6-7 this is the playback mode. + + 00 - play DMC sample until length counter reaches 0 (see $4013) + x1 - loop the DMC sample (x = immaterial) + 10 - play DMC sample until length counter reaches 0, then generate a CPU +IRQ + +Looping (playback mode "x1") will have the chunk of memory played over and +over, until the channel is disabled (via $4015). In this case, after the +length counter reaches 0, it will be reloaded with the calculated length +value of $4013. + +If playback mode "10" is chosen, an interrupt will be dispached when the +length counter reaches 0 (after the sample is done playing). There are 2 +ways to acknowledge the DMC's interrupt request upon recieving it. The first +is a write to this register ($4010), with the MSB (bit 7) cleared (0). The +second is any write to $4015 (see the $4015 register description for more +details). + +If playback mode "00" is chosen, the sample plays until the length counter +reaches 0. No interrupt is generated. + +5-4 appear to be unused + +3-0 this is the DMC frequency control. Valid values are from 0 - F. The +value of this register determines how many CPU clocks to wait before the DMA +will fetch another byte from memory. The # of clocks to wait -1 is initially +loaded into an internal 12-bit down counter. The down counter is then +decremented at the frequency of the CPU (1.79MHz). The channel fetches the +next DMC sample byte when the count reaches 0, and then reloads the count. +This process repeats until the channel is disabled by $4015, or when the +length counter has reached 0 (if not in the looping playback mode). The +exact number of CPU clock cycles is as follows: + +value CPU +written clocks octave scale +------- ------ ------ ----- +F 1B0 8 C +E 240 7 G +D 2A0 7 E +C 350 7 C +B 400 6 A +A 470 6 G +9 500 6 F +8 5F0 6 D +7 6B0 6 C +6 710 5 B +5 7F0 5 A +4 8F0 5 G +3 A00 5 F +2 AA0 5 E +1 BE0 5 D +0 D60 5 C + +The octave and scale values shown represent the DMC DMA clock cycle rate +equivelant. These values are merely shown for the music enthusiast +programmer, who is more familiar with notes than clock cycles. + +Every fetched byte is loaded into a internal 8-bit shift register. The shift +register is then clocked at 8x the DMA frequency (which means that the CPU +clock count would be 1/8th that of the DMA clock count), or shifted at +3 +the octave of the DMA (same scale). The data shifted out of the register is +in serial form, and the least significant bit (LSB, or bit 0) of the fetched +byte is the first one to be shifted out (then bit 1, bit 2, etc.). + +The bits shifted out are then fed to the UP/DOWN control pin of the internal +delta counter, which will effectively have the counter increment it's +retained value by one on "1" bit samples, and decrement it's value by one on +"0" bit samples. This counter is clocked at the same frequency of the shift +register's. + +The counter is only 6 bits in size, and has it's 6 outputs tied to the 6 MSB +inputs of a 7 bit DAC. The analog output of the DAC is then what you hear +being played by the DMC. + +Wrap around counting is not allowed on this counter. Instead, a "clipping" +behaviour is exhibited. If the internal value of the counter has reached 0, +and the next bit sample is a 0 (instructing a decrement), the counter will +take no action. Likewise, if the counter's value is currently at -1 +(111111B, or 03FH), and the bit sample to be played is a 1, the counter will +not increment. + + +$4011 - Delta counter load register +----------------------------------- + +bits +---- +7 appears to be unused +1-6 the load inputs of the internal delta counter +0 LSB of the DAC + +A write to this register effectively loads the internal delta counter with a +6 bit value, but can be used for 7 bit PCM playback. Bit 0 is connected +directly to the LSB (bit 0) of the DAC, and has no effect on the internal +delta counter. Bit 7 appears to be unused. + +This register can be used to output direct 7-bit digital PCM data to the +DMC's audio output. To use this register for PCM playback, the programmer +would be responsible for making sure that this register is updated at a +constant rate. The rate is completely user-definable. For the regular CD +quality 44100 Hz playback sample rate, this register would have to be +written to approximately every 40 CPU cycles (assuming the 2A03 is running @ +1.79 MHz). + + +$4012 - DMA address load register +---------------------------- + +This register contains the initial address where the DMC is to fetch samples +from memory for playback. The effective address value is $4012 shl 6 or +0C000H. This register is connected to the load pins of the internal DMA +address pointer register (counter). The counter is incremented after every +DMA byte fetch. The counter is 15 bits in size, and has addresses wrap +around from $FFFF to $8000 (not $C000, as you might have guessed). The DMA +address pointer register is reloaded with the initial calculated address, +when the DMC is activated from an inactive state, or when the length counter +has arrived at terminal count (count=0), if in the looping playback mode. + + +$4013 - DMA length register +--------------------------- + +This register contains the length of the chunk of memory to be played by the +DMC, and it's size is measured in bytes. The value of $4013 shl 4 is loaded +into a 12 bit internal down counter, dubbed the length counter. The length +counter is decremented after every DMA fetch, and when it arrives at 0, the +DMC will take action(s) based on the 2 MSB of $4010. This counter will be +loaded with the current calculated address value of $4013 when the DMC is +activated from an inactive state. Because the value that is loaded by the +length counter is $4013 shl 4, this effectively produces a calculated byte +sample length of $4013 shl 4 + 1 (i.e. if $4013=0, sample length is 1 byte +long; if $4013=FF, sample length is $FF1 bytes long). + + +$4015 - DMC status +------------------ + +This contains the current status of the DMC channel. There are 2 read bits, +and 1 write bit. + +bits +---- +7(R) DMC's IRQ status (1=CPU IRQ being caused by DMC) +4(R) DMC is currently enabled (playing a stream of samples) +4(W) enable/disable DMC (1=start/continue playing a sample;0=stop playing) + +When an IRQ goes off inside the 2A03, Bit 7 of $4015 can tell the interrupt +handler if it was caused by the DMC hardware or not. This bit will be set +(1) if the DMC is responsible for the IRQ. Of course, if your program has no +other IRQ-generating hardware going while it's using the DMC, then reading +this register is not neccessary upon IRQ generation. Note that reading this +register will NOT clear bit 7 (meaning that the DMC's IRQ will still NOT be +acknowledged). Also note that if the 2 MSB of $4010 were set to 10, no IRQ +will be generated, and bit 7 will always be 0. + +Upon generation of a IRQ, to let the DMC know that the software has +acknowledged the /IRQ (and to reset the DMC's internal IRQ flag), any write +out to $4015 will reset the flag, or a write out to $4010 with the MSB set +to 0 will do. These practices should be performed inside the IRQ handler +routine. To replay the same sample that just finished, all you need to do is +just write a 1 out to bit 4 of $4015. + +Bit 4 of $4015 reports the real-time status of the DMC. A returned value of +1 denotes that the channel is currently playing a stream of samples. A +returned value of 0 indicates that the channel is inactive. If the +programmer needed to know when a stream of samples was finished playing, but +didn't want to use the IRQ generation feature of the DMC, then polling this +bit would be a valid option. + +Writing a value to $4015's 4th bit has the effect of enabling the channel +(start, or continue playing a stream of samples), or disabling the channel +(stop all DMC activity). Note that writing a 1 to this bit while the channel +is currently enabled, will have no effect on counters or registers internal +to the DMC. + +The conditions that control the time the DMC will stay enabled are +determined by the 2 MSB of $4010, and register $4013 (if applicable). + + +System Reset +------------ + +On system reset, all 7 used bits of $4011 are reset to 0, the IRQ flag is +cleared (disabled), and the channel is disabled. All other registers will +remain unmodified. + diff --git a/Documentation/tech/exp/mmc5-e.txt b/Documentation/tech/exp/mmc5-e.txt new file mode 100644 index 0000000..eab191a --- /dev/null +++ b/Documentation/tech/exp/mmc5-e.txt @@ -0,0 +1,250 @@ +========= mmc5 infomation ========== +date 1998/05/31 +by goroh +translated May 31, 1998 by Sgt. Bowhack +mail goroh_kun@geocities.co.jp + +5000,5004 ch1,ch2 Pulse Control + bit CCwevvvv + CC Duty Cycle (Positive vs. Negative) + #0:87.5% #1:75.0% #2:50.0% #3:25.0% + w Waveform Hold (e.g. Looping) + 0: Off 1: On + e Envelope Select + 0: Varied 1: Fixed + < e=0 > + vvvv Playback Rate + #0<-fast<--->-slow--> #15 + < e=1 > + vvvv Output Volume + +5002,5006 ch1,ch2 frequency L + bit ffffffff +5003,5007 ch1,ch2 frequency H + bit tttttfff + ttttt sound occurence time + +Objective is to remove the continuous changing of frequency for +square wave setup and do the same to the main part of the square wave +of studying the main part of the famicom. (?- Sgt. Bowhack) + +5010 ch3 synthetic voice business channel + bit -------O + O wave output 0:Off 1:On + +5011 ch4 synthetic voice business channel 2 + bit vvvvvvvv + vvvvvvvv wave size + +5015 sound output channel + bit ------BA + A: ch1 output 1:enable 0:disable + B: ch2 output 1:enable 0:disable + +5100 PRG-page size Setting + bit ------SS + SS PRG-page size + 0: 32k 1:16k 2,3:8k +* Reset is misled the first times for about 8k (?- SB) + +5101 CHR-page size Setting + bit ------SS + SS CHR-page size + 0:8k 1:4k 2:2k 3:1k + +5102 W BBR-RAM Write Protect 1 + bit ------AA +5103 W BBR-RAM Write Protect 2 + bit ------BB + (AA,BB) = (2,1) permitted to write to BBR-RAM only when crowded +*Reset write around becomes prohibited when crowded + +5104 Grafix Mode Setting + $5c00-$5fff decides how it should be used + bit ------MM + #00:Enable only Split Mode + #01:Enable Split Mode & ExGrafix Mode + #02:ExRAM Mode + #03:ExRAM Mode & Write Protect + +Consideration + MMC5 has 2 graphic mode extensions that allow more than 256 characters +on one standard game screen. It uses Split Mode so it can display the +specified CHR-page and scroll position seperate from ExGrafix Mode to +be able to choose a palette, and the other divides it vertically. + +5105 W NameTable Setting + bit ddccbbaa + aa: Select VRAM at 0x2000-0x23ff + bb: Select VRAM at 0x2400-0x27ff + cc: Select VRAM at 0x2800-0x2bff + dd: Select VRAM at 0x2c00-0x2fff + #0:use VRAM 0x000-0x3ff + #1:use VRAM 0x400-0x7ff + #2:use ExVRAM 0x000-0x3ff + #3:use ExNameTable(Fill Mode) + +Consideration + The name table can designate 4 kinds of this resister and be a useful +special quality for this because painting and smashing it with a +character that there is 1 sheet for the remaining sheets can generally +be used. (?-SB) + +5106 W Fill Mode Setting 1 + bit vvvvvvvv + Fill chr-table + For whether it paints or smashes it at any non-designated character + +5107 W Fill Mode Setting 2 + bit ------pp + Whether or not it uses any non-designated palettes + +5113 RAM-page for $6000-$7FFF + bit -----p-- + +5114-5117 Program Bank switch + < page_size=32k > + $5117 [8]-[F] bit pppppp-- + + < page_size=16k > + $5115 [8]-[B] bit ppppppp- + $5117 [C]-[F] bit ppppppp- + + < page_size=8k > + $5114 [8][9] bit pppppppp + $5115 [A][B] bit pppppppp + $5116 [C][D] bit pppppppp + $5117* [E][F] bit pppppppp + +*Reset is around early, Last Page misled + +5120-512b Charactor Bank switch + < page_size=8k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5127 [0]-[7] modeA + $512b [0]-[7] modeB + + < page_size=4k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5123 [0]-[3] modeA + $5127 [4]-[7] modeA + $512b [0]-[3],[4]-[7] modeB + + < page_size=2k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5121 [0]-[1] modeA + $5123 [2]-[3] modeA + $5125 [4]-[5] modeA + $5127 [6]-[7] modeA + $5129 [0]-[1],[4]-[5] modeB + $512b [2]-[3],[6]-[7] modeB + + < page_size=1k > + $5120-$5127 switch to mode A + $5128-$512b switch to mode B + $5120 [0] modeA + $5121 [1] modeA + $5122 [2] modeA + $5123 [3] modeA + $5124 [4] modeA + $5125 [5] modeA + $5126 [6] modeA + $5127 [7] modeA + $5128 [0],[4] modeB + $5129 [1],[5] modeB + $512a [2],[6] modeB + $512b [3],[7] modeB + +Consideration + MMC5 has mode A ,mode B and 2 kinds of CHR-page memory resistors. +They can be used for refreshing it. (?-SB) + +5130 ??? +analyzing it... + +5200 W Split Mode Control 1 + bit Ec-vvvvv + For the E function 0:don't use 1:use + c boundary's side is for using Split Mode extension of graphics + 0: left side 1: right side + vvvvv left boundary is designated with the char. # to count places + +Sample. + 5200 <- #00 + (not?) used yet + 5200 <- #82 + Used for SplitMode GFX extension from left 1-2 character + 5200 <- #c2 + Used for SplitMode GFX extension from the right side 3 chars. + 5200 <- #c0 + Used for SplitMode GFX extension on the whole screen + 5200 <- #d0 + Used for SplitMode GFX extension on the right side of the screen + 5200 <- #90 + Used for SplitMode GFX extension on the left side of the screen + +5201 W SplitMode setup for SplitMode Ext. GFX use 1 + $2005 determines the vertical movement; it can also delay ext. gfx's + vert. movement if necessary. It's written 2 times in bulk in the same + way as it would slip off a grade in $2005 (??-SB) + +5202 W SplitMode setup for SplitMode Ext. GFX use 2 + bit --pppppp + uses vertical division of ext. gfx CHR-page designation + index_size=4k(0x1000byte) +In case it uses a character 0x4000-0x4fff for the ext. gfx in question + $5202 <- 4 + +5203 W scanline break point + For scanline # that it splits and wants to make it designate it in bulk + +5204 WR IRQ enable/disable + W bit I------- + I 1:IRQ Enable 0:IRQ Disable + R bit I------- + I 1:Scanline Hit 0:Scanline not Hit + $5203 is designated as scanline when arrived. + +5205 WR mult input/output +5206 WR mult input/output +($5205in)*($5206in) = $5205,$5206out + +5c00-5fbf ext. gfx business VRAM + shows an attribute of every position character + + + bit PPpppppp + PP: use character palette number + pppppp: use background CHR-PAGE number index=4k + #0-#3F are designations, $0000-$3FFF is CHR-data's range + Use for extension gfx + + + SplitMode uses a Name Table for extension gfx use. + bit pppppppp + pppppppp: use for background char. number designation + + + Used for Extension RAM + +5fc0-5fff + + (not?) used yet + + + SplitMode uses gfx's Attribute Table extension. + PPU uses $23c0-$23ff in the same way as the Attribute Table + + + Used for Extension RAM + +Consideration + 5c00-5fff has 3 uses. + Split Mode and ExGrafix Mode's VBlank is written so as to become + crowded, it writes a 0 and becomes crowded. + Every mode tries to go around ExRAM mode including reading but it + writes it, is effective in bulk and #5c-#5f is the output at times + where it is effective. \ No newline at end of file diff --git a/Documentation/tech/exp/mmc5_bank_switch.txt b/Documentation/tech/exp/mmc5_bank_switch.txt new file mode 100644 index 0000000..b82f38c --- /dev/null +++ b/Documentation/tech/exp/mmc5_bank_switch.txt @@ -0,0 +1,128 @@ +MMC5 Bankswitching +by Kevin Horton +-------------------- + +5100: Controls paging of RAM and ROM + +Bits: ???? ??xx + +For xx: + +00: 32K bankswitching. Only 5117 can be used to control banks. 5114 thru + 5116 have no effect. + + +01: 16K bankswitching. Only 5115 and 5117 can be used to control banks. + 5114 and 5116 have no effect. + + +10: 8K/16K bankswitching. 5115-5117 are used. 5114 has no effect. + +11: 8K bankswitching. 5114-5117 are used. + +(See below for detailed description) + +-- + +5113: RAM page 6000-7FFF bank. Lower 3 bits are used, for a possible + 64K of WRAM. (Note more bits *may* be possible for more RAM. + This has not been confirmed yet). + +Bits: ???? ?xxx + +WRAM follows a certain convention, based on the style of MMC5 board used. +8K and 32K carts are usually implemented with a single chip, while 16K +carts use two 8K'ers. This is important since enabling changes, and hence +valid banks. + +for xxx: + + 8K 16K 32K 40K 64K + +0: bank 0 bank 0 bank 0 bank 0 bank 0 +1: bank 0 bank 0 bank 1 bank 1 bank 1 +2: bank 0 bank 0 bank 2 bank 2 bank 2 +3: bank 0 bank 0 bank 3 bank 3 bank 3 +4: open bus bank 1 open bus bank 4 bank 4 +5: open bus bank 1 open bus bank 4 bank 5 +6: open bus bank 1 open bus bank 4 bank 6 +7: open bus bank 1 open bus bank 4 bank 7 + +Note that the 40K and 64K examples are hypothetical. The first three, +however *are* real and is what you find inside a real MMC5 cart. + +Also note, that 5114-5116 follow this identical convention, if set up +to switch in RAM banks. + +-- + +Bankswitching is a bit complicated. This table should make things clearer. +The numbers at the top are what you write to 5100 to select mode. + +Legend: + +- = this has no effect +--- = this register is not used, and writes to it are ignored +R = PRG ROM/WRAM select. 0=WRAM, 1=PRG ROM +b = bank bits + + + +5100: 00 01 10 11 + +5114 --- --- --- Rbbb bbbb +5115 --- Rbbb bbb- Rbbb bbb- Rbbb bbbb +5116 --- --- Rbbb bbbb Rbbb bbbb +5117 -bbb bb-- -bbb bbb- -bbb bbbb -bbb bbbb + + +Mode 00 +------- + +Only one 32K page can be selected. The lower 2 bits of the desired bank +are ANDed out. writing 084h, 085h, 086h, and 087h to 5117 in this mode +all result in selection of the same 32K. No RAM is available in this mode. + +Mode 01 +------- + +There are two selectable 16K pages. Similar to above, the lowest bit written +is not used to select banks. In this mode, writing to 5115 selects 16K +at 8000-BFFF, and 5117 selects 16K at C000-FFFF. RAM can be enabled in this +mode for 8000-BFFF. If RAM is enabled for 8000-BFFF, remember that the +lowest bank select bit is not used. + +Mode 10 +------- + +This is the oddest one. There is one 16K selectable page, and two 8K +selectable pages. 5115 selects the 16K page at 8000-BFFF, 5116 selects +an 8K page at C000-DFFF, and 5117 selects an 8K page at E000-FFFF. +RAM can be enabled for 8000-DFFF. (16K of RAM at 8000-BFFF via bit 7 of +D115, and 8K of RAM at C000-DFFF via bit 7 of d116). Note that RAM banking +works the same as mode 01, above for the 16K bank. + + +Mode 11 +------- + +There are 4 8K selectable pages. 5114 controls 8000-9FFF, etc. all the way +up to 5117 that controls E000-FFFF. The first 3 pages can use RAM, while +the last page cannot. + + +-- + +WRAM write enable. + +5102, 5103 + +To enable writing to RAM, 5102 must have 02h written to it, and 5103 +must have 01h written to it. If this is not the case, you can still +*read* the RAM, but writes to it have no effect. Supposidly only the +lower two bits of 5102 and 5103 are checked, but I didn't verify this. +I *did* however verify that setting the two registers to the above +values allows writing. If voltage goes out of tolerance (Read: you +turn the power on/off) the RAM writing is disabled. (To prevent +corruption of saved-games during power cycling) + diff --git a/Documentation/tech/nsfspec.txt b/Documentation/tech/nsfspec.txt new file mode 100644 index 0000000..2ef526d --- /dev/null +++ b/Documentation/tech/nsfspec.txt @@ -0,0 +1,336 @@ + NES Music Format Spec + --------------------- + + +By: Kevin Horton khorton@iquest.net + + +NOTE: +----- + + +Remember that I am very willing to add stuff and update this spec. If +you find a new sound chip or other change let me know and I will get back +with you. E-mail to the above address. + + +V1.61 - 06/27/2000 Updated spec a bit +V1.60 - 06/01/2000 Updated Sunsoft, MMC5, and Namco chip information +V1.50 - 05/28/2000 Updated FDS, added Sunsoft and Namco chips +V1.32 - 11/27/1999 Added MMC5 register locations +V1.30 - 11/14/1999 Added MMC5 audio bit, added some register info +V1.20 - 09/12/1999 VRC and FDS prelim sound info added +V1.00 - 05/11/1999 First official NSF specification file + + + +This file encompasses a way to transfer NES music data in a small, easy to +use format. + +The basic idea is one rips the music/sound code from an NES game and prepends +a small header to the data. + +A program of some form (6502/sound emulator) then takes the data and loads +it into the proper place into the 6502's address space, then inits and plays +the tune. + +Here's an overview of the header: + +offset # of bytes Function +---------------------------- + +0000 5 STRING "NESM",01Ah ; denotes an NES sound format file +0005 1 BYTE Version number (currently 01h) +0006 1 BYTE Total songs (1=1 song, 2=2 songs, etc) +0007 1 BYTE Starting song (1= 1st song, 2=2nd song, etc) +0008 2 WORD (lo/hi) load address of data (8000-FFFF) +000a 2 WORD (lo/hi) init address of data (8000-FFFF) +000c 2 WORD (lo/hi) play address of data (8000-FFFF) +000e 32 STRING The name of the song, null terminated +002e 32 STRING The artist, if known, null terminated +004e 32 STRING The Copyright holder, null terminated +006e 2 WORD (lo/hi) speed, in 1/1000000th sec ticks, NTSC (see text) +0070 8 BYTE Bankswitch Init Values (see text, and FDS section) +0078 2 WORD (lo/hi) speed, in 1/1000000th sec ticks, PAL (see text) +007a 1 BYTE PAL/NTSC bits: + bit 0: if clear, this is an NTSC tune + bit 0: if set, this is a PAL tune + bit 1: if set, this is a dual PAL/NTSC tune + bits 2-7: not used. they *must* be 0 +007b 1 BYTE Extra Sound Chip Support + bit 0: if set, this song uses VRCVI + bit 1: if set, this song uses VRCVII + bit 2: if set, this song uses FDS Sound + bit 3: if set, this song uses MMC5 audio + bit 4: if set, this song uses Namco 106 + bit 5: if set, this song uses Sunsoft FME-07 + bits 6,7: future expansion: they *must* be 0 +007c 4 ---- 4 extra bytes for expansion (must be 00h) +0080 nnn ---- The music program/data follows + +This may look somewhat familiar; if so that's because this is somewhat +sorta of based on the PSID file format for C64 music/sound. + + +Loading a tune into RAM +----------------------- + +If offsets 0070h to 0077h have 00h in them, then bankswitching is *not* +used. If one or more bytes are something other than 00h then bankswitching +is used. If bankswitching is used then the load address is still used, +but you now use (ADDRESS AND 0FFFh) to determine where on the first bank +to load the data. + + +Each bank is 4K in size, and that means there are 8 of them for the +entire 08000h-0ffffh range in the 6502's address space. You determine where +in memory the data goes by setting bytes 070h thru 077h in the file. +These determine the inital bank values that will be used, and hence where +the data will be loaded into the address space. + +Here's an example: + +METROID.NSF will be used for the following explaination. + +The file is set up like so: (starting at 070h in the file) + + +0070: 05 05 05 05 05 05 05 05 - 00 00 00 00 00 00 00 00 +0080: ... music data goes here... + +Since 0070h-0077h are something other than 00h, then we know that this +tune uses bankswitching. The load address for the data is specified as +08000h. We take this AND 0fffh and get 0000h, so we will load data in +at byte 0 of bank 0, since data is loaded into the banks sequentially +starting from bank 0 up until the music data is fully loaded. + +Metroid has 6 4K banks in it, numbered 0 through 5. The 6502's address +space has 8 4K bankswitchable blocks on it, starting at 08000h-08fffh, +09000h-09fffh, 0a000h-0afffh ... 0f000h-0ffffh. Each one of these is 4K in +size, and the current bank is controlled by writes to 05ff8h thru 05fffh, +one byte per bank. So, 05ff8h controls the 08000h-08fffh range, 05ff9h +controls the 09000h-09fffh range, etc. up to 05fffh which controls the +0f000h-0ffffh range. When the song is loaded into RAM, it is loaded into +the banks and not the 6502's address space. Once this is done, then the +bank control registers are written to set up the inital bank values. +To do this, the value at 0070h in the file is written to 05ff8h, 0071h +is written to 05ff9h, etc. all the way to 0077h is written to 05fffh. +This is should be done before every call to the init routine. + +If the tune was not bankswitched, then it is simply loaded in at the +specified load address, until EOF + + +Initalizing a tune +------------------ + +This is pretty simple. Load the desired song # into the accumulator, +minus 1 and set the X register to specify PAL (X=1) or NTSC (X=0). +If this is a single standard tune (i.e. PAL *or* NTSC but not both) +then the X register contents should not matter. Once the song # and +optional PAL/NTSC standard are loaded, simply call the INIT address. +Once init is done, it should perform an RTS. + + +Playing a tune +-------------- + +Once the tune has been initalized, it can now be played. To do this, +simply call the play address several times a second. How many times +per second is determined by offsets 006eh and 006fh in the file. +These bytes denote the speed of playback in 1/1000000ths of a second. +For the "usual" 60Hz playback rate, set this to 411ah. + +To generate a differing playback rate, use this formula: + + + 1000000 +PBRATE= --------- + speed + +Where PBRATE is the value you stick into 006e/006fh in the file, and +speed is the desired speed in hertz. + + +"Proper" way to load the tune +----------------------------- + +1) If the tune is bankswitched, go to #3. + +2) Load the data into the 6502's address space starting at the specified + load address. Go to #4. + +3) Load the data into a RAM area, starting at (start_address AND 0fffh). + +4) Tune load is done. + + +"Proper" way to init a tune +--------------------------- + +1) Clear all RAM at 0000h-07ffh. + +2) Clear all RAM at 6000h-7fffh. + +3) Init the sound registers by writing 00h to 04000-0400Fh, 10h to 4010h, + and 00h to 4011h-4013h. + +4) Set volume register 04015h to 00fh. + +5) If this is a banked tune, load the bank values from the header into + 5ff8-5fffh. + +6) Set the accumulator and X registers for the desired song. + +7) Call the music init routine. + + +"Proper" way to play a tune +--------------------------- + +1) Call the play address of the music at periodic intervals determined + by the speed words. Which word to use is determined by which mode + you are in- PAL or NTSC. + + +Sound Chip Support +------------------ + +Byte 007bh of the file stores the sound chip flags. If a particular flag +is set, those sound registers should be enabled. If the flag is clear, +then those registers should be disabled. + +* VRCVI Uses registers 9000-9002, A000-A002, and B000-B002, write only. + +Caveats: 1) The above registers are *write only* and must not disrupt music + code that happens to be stored there. + + 2) Major caveat: The A0 and A1 lines are flipped on a few games!! + If you rip the music and it sounds all funny, flip around + the xxx1 and xxx2 register pairs. (i.e. 9001 and 9002) 9000 + and 9003 can be left untouched. I decided to do this since it + would make things easier all around, and this means you only + will have to change the music code in a very few places (6). + Esper2 and Madara will need this change, while Castlevania 3j + will not for instance. + + 3) See my VRCVI.TXT doc for a complete register description. + +* VRCVII Uses registers 9010 and 9030, write only. + +Caveats: 1) Same caveat as #1, above. + + 2) See my VRCVII.TXT doc for a complete register description. + +* FDS Sound uses registers from 4040 through 4092. + +Caveats: 1) 6000-DFFF is assumed to be RAM, since 6000-DFFF is RAM on the + FDS. E000-FFFF is usually not included in FDS games because + it is the BIOS ROM. However, it can be used on FDS rips to help + the ripper (for modified play/init addresses). + + 2) Bankswitching operates slightly different on FDS tunes. + 5FF6 and 5FF7 control the banks 6000-6FFF and 7000-7FFF + respectively. NSF header offsets 76h and 77h correspond to + *both* 6000-7FFF *AND* E000-FFFF. Keep this in mind! + +* MMC5 Sound Uses registers 5000-5015, write only as well as 5205 and 5206, + and 5C00-5FF5 + +Caveats: 1) Generating a proper doc file. Be patient. + + 2) 5205 and 5206 are a hardware 8*8 multiplier. The idea being + you write your two bytes to be multiplied into 5205 and 5206 + and after doing so, you read the result back out. Still working + on what exactly triggers it (I think a write to either 5205 + or 5206 triggers the multiply). + + 3) 5C00-5FF5 should be RAM to emulate EXRAM while in MMC5 mode. + +Note: Thanks to Mamiya for the EXRAM info. + + +* Namco 106 Sound Uses registers 4800 and F800. + + This works similar to VRC7. 4800 is the "data" port which is + readable and writable, while F800 is the "address" port and is + writable only. + + The address is 7 bits plus a "mode" bit. Bit 7 controls + address auto-incrementing. If bit 7 is set, the address will + auto-increment after a byte of data is read or written from/to + 4800. + + $40 ffffffff f:frequency L + $42 ffffffff f:frequency M + $44 ---sssff f:frequency H s:tone length (8-s)*4 in 4bit-samples + $46 tttttttt t:tone address(4bit-address,$41 means high-4bits of $20) + $47 -cccvvvv v:linear volume 1+c:number of channels in use($7F only) + $40-47:ch1 $48-4F:ch2 ... $78-7F:ch8 + ch2-ch8 same to ch1 + + $00-3F(8ch)...77(1ch) hhhhllll tone data + h:odd address data(signed 4bit) + l:even address data(signed 4bit) + + real frequency = (f * NES_BASECYCLES) / (40000h * (c+1) * (8-s)*4 * 45) + NES_BASECYCLES 21477270(Hz) + +Note: Very Special thanks to Mamiya for this information! + + +* Sunsoft FME-07 Sound uses registers C000 and E000 + + This is similar to the common AY 3-8910 sound chip that is + used on tons of arcade machines, and in the Intellivision. + + C000 is the address port + E000 is the data port + + Both are write-only, and behave like the AY 3-8910. + +Note: Special thanks to Mamiya for this information as well + + +Caveats +------- + +1) The starting song number and maximum song numbers start counting at + 1, while the init address of the tune starts counting at 0. To + "fix", simply pass the desired song number minus 1 to the init + routine. + +2) The NTSC speed word is used *only* for NTSC tunes, or dual PAL/NTSC tunes. + The PAL speed word is used *only* for PAL tunes, or dual PAL/NTSC tunes. + +3) The length of the text in the name, artist, and copyright fields must + be 31 characters or less! There has to be at least a single NULL byte + (00h) after the text, between fields. + +4) If a field is not known (name, artist, copyright) then the field must + contain the string "" (without quotes). + +5) There should be 8K of RAM present at 6000-7FFFh. MMC5 tunes need RAM at + 5C00-5FF7 to emulate its EXRAM. 8000-FFFF Should be read-only (not + writable) after a tune has loaded. The only time this area should be + writable is if an FDS tune is being played. + +6) Do not assume the state of *anything* on entry to the init routine + except A and X. Y can be anything, as can the flags. + +7) Do not assume the state of *anything* on entry to the play routine either. + Flags, X, A, and Y could be at any state. I've fixed about 10 tunes + because of this problem and the problem, above. + +8) The stack sits at 1FFh and grows down. Make sure the tune does not + attempt to use 1F0h-1FFh for variables. (Armed Dragon Villigust did and + I had to relocate its RAM usage to 2xx) + +9) Variables should sit in the 0000h-07FFh area *only*. If the tune writes + outside this range, say 1400h this is bad and should be relocated. + (Terminator 3 did this and I relocated it to 04xx). + +That's it! + + + diff --git a/Documentation/tech/ppu/loopy1.txt b/Documentation/tech/ppu/loopy1.txt new file mode 100644 index 0000000..bda6d85 --- /dev/null +++ b/Documentation/tech/ppu/loopy1.txt @@ -0,0 +1,63 @@ +Subject: [nesdev] the skinny on nes scrolling +Date: Tue, 13 Apr 1999 16:42:00 -0600 +From: loopy +Reply-To: nesdev@onelist.com +To: nesdev@onelist.com + +From: loopy + +--------- +the current information on background scrolling is sufficient for most games; +however, there are a few that require a more complete understanding. + +here are the related registers: + (v) vram address, a.k.a. 2006 which we all know and love. (16 bits) + (t) another temp vram address (16 bits) + (you can really call them 15 bits, the last isn't used) + (x) tile X offset (3 bits) + +the ppu uses the vram address for both reading/writing to vram thru 2007, +and for fetching nametable data to draw the background. as it's drawing the +background, it updates the address to point to the nametable data currently +being drawn. bits 0-11 hold the nametable address (-$2000). bits 12-14 are +the tile Y offset. + +--------- +stuff that affects register contents: +(sorry for the shorthand logic but i think it's easier to see this way) + +2000 write: + t:0000110000000000=d:00000011 +2005 first write: + t:0000000000011111=d:11111000 + x=d:00000111 +2005 second write: + t:0000001111100000=d:11111000 + t:0111000000000000=d:00000111 +2006 first write: + t:0011111100000000=d:00111111 + t:1100000000000000=0 +2006 second write: + t:0000000011111111=d:11111111 + v=t +scanline start (if background and sprites are enabled): + v:0000010000011111=t:0000010000011111 +frame start (line 0) (if background and sprites are enabled): + v=t + +note! 2005 and 2006 share the toggle that selects between first/second +writes. reading 2002 will clear it. + +note! all of this info agrees with the tests i've run on a real nes. BUT +if there's something you don't agree with, please let me know so i can verify +it. + +________________________________________________________ +NetZero - We believe in a FREE Internet. Shouldn't you? +Get your FREE Internet Access and Email at +http://www.netzero.net/download.html + +------------------------------------------------------------------------ +New hobbies? New curiosities? New enthusiasms? +http://www.ONElist.com +Sign up for a new e-mail list today! diff --git a/Documentation/tech/ppu/loopy2.txt b/Documentation/tech/ppu/loopy2.txt new file mode 100644 index 0000000..7a4585e --- /dev/null +++ b/Documentation/tech/ppu/loopy2.txt @@ -0,0 +1,33 @@ +Subject: [nesdev] Re: the skinny on nes scrolling +Date: Tue, 13 Apr 1999 17:48:54 -0600 +From: loopy +Reply-To: nesdev@onelist.com +To: nesdev@onelist.com + +From: loopy + +(more notes on ppu logic) + +you can think of bits 0,1,2,3,4 of the vram address as the "x scroll"(*8) +that the ppu increments as it draws. as it wraps from 31 to 0, bit 10 is +switched. you should see how this causes horizontal wrapping between name +tables (0,1) and (2,3). + +you can think of bits 5,6,7,8,9 as the "y scroll"(*8). this functions +slightly different from the X. it wraps to 0 and bit 11 is switched when +it's incremented from _29_ instead of 31. there are some odd side effects +from this.. if you manually set the value above 29 (from either 2005 or +2006), the wrapping from 29 obviously won't happen, and attrib data will be +used as name table data. the "y scroll" still wraps to 0 from 31, but +without switching bit 11. this explains why writing 240+ to 'Y' in 2005 +appeared as a negative scroll value. + +________________________________________________________ +NetZero - We believe in a FREE Internet. Shouldn't you? +Get your FREE Internet Access and Email at +http://www.netzero.net/download.html + +------------------------------------------------------------------------ +Looking for a new hobby? Want to make a new friend? +http://www.ONElist.com +Come join one of the 115,000 e-mail communities at ONElist! diff --git a/Documentation/tech/ppu/timing.txt b/Documentation/tech/ppu/timing.txt new file mode 100644 index 0000000..6a669b4 --- /dev/null +++ b/Documentation/tech/ppu/timing.txt @@ -0,0 +1,266 @@ +NTSC PPU timing +by Samus Aran (livingmonolith@hotmail.com) +date: Sept. 25th, Y2K + +This weekend, I setup an experiment with my NTSC NES MB & my PC so's I could +RE the PPU's timing. What I did was (using a PC interface) analyse the +changes that occur on the PPU's address and data pins on every rising & +falling edge of the PPU's clock. I was not planning on removing the PPU from +the motherboard (yet), so basically I just kept everything intact (minus the +stuff I added onto the MB so I could monitor the PPU's signals), and popped +in a game, so that it would initialize the PPU for me (I used DK classics, +since it was only taking somthing like 4 frames before it was turning on the +background/sprites). + +The only change I made was taking out the 21 MHz clock generator circuitry. +To replace the clock signal, I connected a port controlled latch to the +NES's main clock line instead. Now, by writing a 0 or a 1 out to an PC ISA +port of my choice (I was using $104), I was able to control the 21 MHz +clockline of the NES. After I would create a rise or a fall on the NES's +clock line, I would then read in the data that appeared on the PPU's address +and data pins, which included monitoring what PPU registers the game +read/wrote to (& the data that was read/written). + +My findings: + +- The PPU makes NO external access to name or character tables, unless the +background or sprites are enabled. This means that the PPU's address and +data busses are dead while in this state. + +- Because the PPU's palette RAM is internal to it, the PPU has multiport +access to it, and therefore, instant access to it at all times (this is why +reading palette RAM via $2007 does not require a throw-away read). This is +why when a scanline is being rendered, never does the PPU put the palette +address on it's bus; it's simply unneccessary. Additionally, when the +programmer accesses palette RAM via $2006/7, the palette address accessed +actually does show up on the PPU's external address bus, but the PPU's /R & +/W flags are not activated. This is required; to prevent writing over name +table data falling under the approprite mirrored area. I don't know why +Nintendo didn't just devote an exclusive area for palette RAM, like it did +for sprite RAM. + +- Sprite DMA is 6144 clock cycles long (or in CPU clock cycles, 6144/12). +256 individual transfers are made from CPU memory to a temp register inside +the CPU, then from the CPU's temp reg, to $2004. + +- One scanline is EXACTLY 1364 cycles long. In comparison to the CPU's +speed, one scanline is 1364/12 CPU cycles long. + +- One frame is EXACTLY 357368 cycles long, or EXACTLY 262 scanlines long. + + +Sequence of pixel rendering +--------------------------- + +External PPU memory is accessed every 8 clock cycles by the PPU when it's +drawing the background. Therefore, the PPU will typically access external +memory 170 times per scanline. After the 170th fetch, the PPU does nothing +for 4 clock cycles (except in the case of a 1360 clock cycle scanline (more +on this later)), and thus making the scanline up of 1364 cycles. + + accesses + -------- + + 1 thru 128: + + 1. Fetch 1 name table byte + 2. Fetch 1 attribute table byte + 3. Fetch 2 pattern table bitmap bytes + + This process is repeated 32 times (32 tiles in a scanline). + + This is when the PPU retrieves the appropriate data from PPU memory for +rendering the background. The first background tile fetched here is actually +the 3rd to be drawn on the screen (the background data for the first 2 tiles +to be rendered on the next scanline are fetched at the end of the scanline +prior to this one). + +In one complete cycle of fetches (4 fetches, or 32 cycles), the PPU renders +or draws 8 pixels on the screen. However, this does not suggest that the PPU +is always drawing on-screen results while background data is being fetched. +There is a delay inside the PPU from when the first background tile is +fetched, and when the first pixel to be displayed on the screen is rendered. +It is important to be aware of this delay, since it specifically relates to +the "sprite 0 hit" flag's timing. I currently do not know what the delay +time is (as far as clock cycles go). + + Note that the PPU fetches a nametable byte for every 8 horizontal pixels +it draws. It should be understood that with some custom cartridge hardware, +the PPU's color area could be increased (more about this at the end of this +document). + + It is also during this time that the PPU evaluates the "Y coordinate" +entries of all 64 sprites (starting with sprite 0) in sprite RAM, to see if +the sprites are within range (to be drawn on the screen) FOR THE NEXT +SCANLINE. For sprite entries that have been found to be in range, they (that +is, the sprite's nametable, and x coordinate bytes, attribute (5 bits) and +fine y scroll (3 or 4 bits, depending on bit 5 of $2000 ("sprite size")) +bits) accumulate into a part of PPU memory called the "sprite temporary +memory", which is big enough to hold the data for up to 8 sprites. If 8 +sprites have accumulated into the temporary memory and the PPU is still +finding more sprites in range for drawing on the next scanline, then the +sprite data is ignored (not loaded into the sprite temporary memory), and +the PPU raises a flag (bit 5 of $2002) indicating that it is going to be +dropping sprites for the next scanline. + + 129 thru 160: + + 1. Fetch 2 garbage name table bytes + 2. Fetch 2 pattern table bitmap bytes for applicable sprites ON THE NEXT +SCANLINE + + This process is repeated 8 times. + + This is the period of time when the PPU retrieves the appropriate pattern +table data for the sprites to be drawn on the next scanline. Where the PPU +fetches pattern table data for an individual sprite depends on the nametable +byte, and fine y scroll bits of a single sprite entry in the sprite +temporary memory, and bits 3 and 5 of $2000 ("sprite pattern table select" +and "sprite size" bits, respectively). The fetched pattern table data (which +is 2 bytes), plus the associated 5 attribute bytes, and the x coordinate +byte in sprite temporary memory are then loaded into a part of the PPU +called the "sprite buffer memory". This memory area again, is large enough +to hold the contents for 8 sprites. The makeup of one sprite memory cell +here is composed of 2 8-bit shift registers (the fetched pattern table data +is loaded in here, where it will be serialized at the appropriate time), a +5-bit latch (which holds the attribute data for a sprite), and a 8-bit down +counter (this is where the x coordinate is loaded). The counter is +decremented every time the PPU draws a pixel on screen, and when the counter +reaches 0, the pattern table data in the shift registers will start to +serialize, and be drawn on the screen. + + Even if no sprites exist on the next scanline, a pattern table fetch takes +place. + + Although the fetched name table data is thrown away, I still can't make +much sense out of the name table address accesses the PPU makes during this +time. However, the address does seem to relate to the first name table tile +to be rendered on the screen. + + It should also be noted that because this fetch is required for sprites on +the next line, it is neccessary for a garbage scanline to exist prior to the +very first scanline to be actually rendered, so that sprite RAM entries can +be evaluated, and the appropriate bitmap data retrieved. + + Finally, it would appear to me that the PPU's 8 sprite/scanline +bottleneck exists clearly because the PPU could only find the time in one +scanline to fetch the pattern bitmaps for 8 sprites. However, why the PPU +doesn't attempt to access pattern table data in the time when it fetches 2 +garbage name table bytes is a good question. + + 161 thru 168: + + 1. Fetch 1 name table byte + 2. Fetch 1 attribute table byte + 3. Fetch 2 pattern table bitmap bytes + + This process is repeated 2 times. + + It is during this time that the PPU fetches the appliciable background +data for the first and second tiles to be rendered on the screen for the +next scanline. The rest of tiles (3..128) are fetched at the beginning of +the following scanline. + + 169 thru 170: + + 1. Fetch 1 name table byte + + This process is repeated 2 times. + + I'm unclear of the reason why this particular access to memory is made. +The nametable address that is accessed 2 times in a row here, is also the +same nametable address that points to the 3rd tile to be rendered on the +screen (or basically, the first nametable address that will be accessed when +the PPU is fetching background data on the next scanline). + + + After memory access 170, the PPU simply rests for 4 cycles (or the +equivelant of half a memory access cycle) before repeating the whole +pixel/scanline rendering process. If the scanline being rendered is the very +first one on every second frame, then this delay simply doesn't exist. + + +Sequence of line rendering +-------------------------- + + 1. Starting at the instant the VINT flag is pulled down (when a NMI is +generated), 20 scanlines make up the period of time on the PPU which I like +to call the VINT period. During this time, the PPU makes NO access to it's +external memory (i.e. name / pattern tables, etc.). + + 2. After 20 scanlines worth of time go by (since the VINT flag was set), +the PPU starts to render scanlines. Now, the first scanline it renders is a +dummy one; although it will access it's external memory in the same sequence +it would for drawing a valid scanline, the fetched background data is thrown +away, and the places that the PPU accesses name table data is unexplainable +(for now). + +IMPORTANT! this is the only scanline that has variable length. On every +second rendered frame, this scanline is only 1360 cycles. Otherwise it's +1364. + + 3. after rendering 1 dummy scanline, the PPU starts to render the actual +data to be displayed on the screen. This is done for 240 scanlines, of +course. + + 4. after the very last rendered scanline finishes, the PPU does nothing for +1 scanline (i.e. makes no external memory accesses). When this scanline +finishes, the VINT flag is set, and the process of drawing lines starts all +over again. + +This makes a total of 262 scanlines. Although one scanline is slightly +shorter on every second rendered frame (4 cycles), I don't know if this +feature is neccessary to implement in emulators, since it only makes 1/3 a +CPU cycle difference per frame (and there's NO way that a game could take +into account 1/3 of a CPU cycle). + + +Food for thought +---------------- + +What's important to remember about the NES's 2C02 or picture proecssing unit +(hereon PPU) is that all screen data is fetched & drawn on a real-time +basis. For example, let's consider how the PPU draws background tiles. + +We know that one name table byte is associated with an 8x8 cluster of pixels +(and therefore, 16 bytes worth of pattern bitmap data, plus 2 attribute +bits). Therefore, it would make sense for the PPU to only have to fetch a +name table byte once for each 8x8 pixel array it draws (one tile), and 1 +attribute byte fetch for every 4x4 tile matrix that it draws. However, since +the PPU always draws one complete scanline before drawing the next, The PPU +will actually fetch the same name table byte 8 times, once each scanline at +the appropriate x coordinate. Since these name table address access reads +are redundant, with some custom cartridge hardware, it would be possible to +make the PPU appear as if it had background tiles as small as 8x1 pixels! + +Additionally, an attribute table byte is fetched from name table RAM once +per 2 fetched pattern bitmap bytes (or, every 8 pixels worth of pattern +bitmap data). This is useful information to keep in mind, for with some +custom cartridge hardware, this would allow the NES's PPU to appear to have +an effective color area as small as of 8*1 pixels (!), where only the 8 +pixels are limited to having 4 exclusive colors, which, is *alot* better +than the PPU's default color area of 16x16 pixels. + +So basically, what I'm getting at here, is that the PPU has absolutely NO +memory whatsoever of what it rendered last scanline, and therefore all data +must be processed/evaluated again, whether it's name table accesses, +attribute table accesses, or even it's internal sprite RAM accesses. + +What's good, and what's bad about the way the PPU draws it's pictures: + +What's good about it is that it makes the PPU a hell of alot more versatile, +provided you have the appropriate hardware to assist in the improvement of +the PPU's background drawing techniques (MMC5 comes to mind). Also, by doing +background rendering in the real time, the PPU complexity is less, and less +internal temporary registers are required. + +What's bad about it is that it eats up memory bandwidth like it's going out +of style. When the PPU is rendering scanlines, the PPU is accessing the VRAM +every chance it gets, which takes away from the time that the programmer +gets to access the VRAM. In contrast, if redundantly loaded data (like +attribute bytes) were kept in internal PPU RAM, this would allow some time +for the PPU to allow access to it's VRAM. + +All in all though, Nintendo engineered quite a cost effective, versatile +graphic processor. Now, if only they brought the 4 expansion pins on the PPU +out of the deck! diff --git a/Makefile.base b/Makefile.base new file mode 100644 index 0000000..3e3f273 --- /dev/null +++ b/Makefile.base @@ -0,0 +1,42 @@ +CFLAGS = -Wall -Winline ${TFLAGS} +OBJECTS = fce.o x6502.o video.o general.o endian.o svga.o sound.o nsf.o fds.o netplay.o ines.o state.o unif.o input.o file.o cart.o crc32.o memory.o cheat.o debug.o + +fceu: fceu2 +include mappers/Makefile +include boards/Makefile +include mbshare/Makefile +include input/Makefile + +fceu2: ${OBJECTS} ${MOBJS} ${MUOBJS} ${MUSOBJS} ${INPOBJS} ${OBJDRIVER} + ${CC} -s -o fceu ${OBJECTS} ${MOBJS} ${MUOBJS} ${MUSOBJS} ${INPOBJS} ${OBJDRIVER} ${LDRIVER} + +clean: + ${RM} fceu fceu.exe ${OBJECTS} ${INPOBJS} ${MOBJS} ${MUOBJS} ${MUSOBJS} ${OBJDRIVER} + +nsf.o: nsf.c nsf.h fce.h x6502.h svga.h video.h sound.h nsfbgnew.h general.h file.h +x6502.o: x6502.c x6502.h ops.h fce.h sound.h +video.o: video.c types.h video.h svga.h version.h general.h +sound.o: sound.c sound.h types.h fce.h svga.h x6502.h +svga.o: svga.c svga.h types.h palette.h state.h netplay.h fds.h fce.h nsf.h video.h sound.h palettes/*.h driver.h drawing.h +netplay.o: netplay.c netplay.h types.h svga.h + +state.o: state.c state.h + +unif.o: unif.c unif.h file.h cart.h + +memory.o: memory.c memory.h + +cart.o: cart.c cart.h types.h version.h fce.h +fce.o: fce.c *.h +fds.o: fds.h x6502.h types.h version.h fce.h svga.h sound.h general.h state.h file.h memory.h +ines.o: ines.c ines.h x6502.h types.h fce.h ines.h version.h svga.h general.h state.h file.h memory.h cart.h crc32.h banksw.h +input.o: input.c input.h x6502.h types.h fce.h sound.h netplay.h driver.h svga.h + +crc32.o: crc32.c crc32.h types.h +endian.o: endian.c endian.h types.h +file.o: file.c file.h types.h endian.h memory.h driver.h +general.o: general.c general.h types.h state.h version.h + +cheat.o: cheat.c driver.h + +debug.o: debug.c debug.h fce.h diff --git a/Makefile.beos b/Makefile.beos new file mode 100644 index 0000000..0537114 --- /dev/null +++ b/Makefile.beos @@ -0,0 +1,23 @@ +CC = gcc +TFLAGS = -no-fpic -DC80x86 -DNETWORK -DFPS `sdl-config --cflags` -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DLSB_FIRST -DSDL -DUNIX -DPSS_STYLE=1 -DZLIB +RM = rm -f +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}sdl.o ${B}main.o ${B}throttle.o ${B}sdl-netplay.o ${B}sdl-sound.o ${B}sdl-video.o ${B}sdl-joystick.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o drivers/common/vidblit.o ${UNZIPOBJS} +LDRIVER = -static `sdl-config --libs` -lz -lSDL_net + +include Makefile.base + +${B}sdl-joystick.o: ${B}sdl-joystick.c +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c ${B}keyscan.h +${B}sdl.o: ${B}sdl.c ${B}sdl.h +${B}sdl-video.o: ${B}sdl-video.c +${B}sdl-sound.o: ${B}sdl-sound.c +${B}sdl-netplay.o: ${B}sdl-netplay.c +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h + +include Makefile.common diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 0000000..891b77a --- /dev/null +++ b/Makefile.common @@ -0,0 +1,6 @@ +drivers/common/cheat.o: drivers/common/cheat.c drivers/common/cheat.h +drivers/common/config.o: drivers/common/config.c drivers/common/config.h +drivers/common/args.o: drivers/common/args.c drivers/common/args.h +drivers/common/unixdsp.o: drivers/common/unixdsp.c drivers/common/unixdsp.h +drivers/common/videblit.o: drivers/common/vidblit.c drivers/common/vidblit.h + diff --git a/Makefile.dos b/Makefile.dos new file mode 100644 index 0000000..bef31aa --- /dev/null +++ b/Makefile.dos @@ -0,0 +1,23 @@ +CC = gcc +TFLAGS = -O2 -mcpu=i686 -Izlib -fomit-frame-pointer -DC80x86 -DLSB_FIRST -DDOS -DPSS_STYLE=2 -DZLIB -D_USE_LIBM_MATH_H +RM = del +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}main.o ${B}dos.o ${B}throttle.o ${B}dos-joystick.o ${B}dos-keyboard.o ${B}dos-mouse.o ${B}dos-sound.o ${B}dos-video.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o ${ZLIBOBJS} ${UNZIPOBJS} +LDRIVER = -lm + +include Makefile.base + +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c +${B}dos.o: ${B}dos.c ${B}dos.h +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h +${B}dos-joystick.o: ${B}dos-joystick.c ${B}dos.h ${B}dos-joystick.h +${B}dos-keyboard.o: ${B}dos-keyboard.c ${B}keyscan.h +${B}dos-mouse.o: ${B}dos-mouse.c +${B}dos-sound.o: ${B}dos-sound.c ${B}dos.h ${B}dos-sound.h ${B}dos-joystick.h +${B}dos-video.o: ${B}dos-video.c ${B}dos.h ${B}dos-video.h +include Makefile.common diff --git a/Makefile.linuxvga b/Makefile.linuxvga new file mode 100644 index 0000000..a123168 --- /dev/null +++ b/Makefile.linuxvga @@ -0,0 +1,22 @@ +CC = gcc +TFLAGS = -DFPS -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DC80x86 -DLSB_FIRST -DSVGALIB -DUNIX -DLINUX -DNETWORK -DPSS_STYLE=1 -DZLIB +RM = rm -f +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}svgalib.o ${B}main.o ${B}throttle.o ${B}svga-video.o ${B}unix-netplay.o ${B}lnx-joystick.o drivers/common/unixdsp.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o ${ZLIBOBJS} ${UNZIPOBJS} +LDRIVER = -lm -lvga + +include Makefile.base + +${B}lnx-joystick.o: ${B}lnx-joystick.c +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h +${B}svgalib.o: ${B}svgalib.c ${B}svgalib.h +${B}svga-video.o: ${B}svga-video.c ${B}svga-video.h ${B}vgatweak.c +${B}unix-netplay.o: ${B}unix-netplay.c + +include Makefile.common diff --git a/Makefile.unixsdl b/Makefile.unixsdl new file mode 100644 index 0000000..d8122d9 --- /dev/null +++ b/Makefile.unixsdl @@ -0,0 +1,25 @@ +CC = gcc +TFLAGS = -DNETWORK -DFPS `sdl-config --cflags` -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DC80x86 -DLSB_FIRST -DSDL -DUNIX -DPSS_STYLE=1 -DZLIB + +RM = rm -f +B = drivers/cli/ + +all: fceu + +include zlib/Makefile + +OBJDRIVER = ${B}sdl.o ${B}main.o ${B}throttle.o ${B}unix-netplay.o ${B}sdl-sound.o ${B}sdl-video.o ${B}sdl-joystick.o drivers/common/cheat.o drivers/common/config.o drivers/common/args.o drivers/common/vidblit.o ${UNZIPOBJS} +LDRIVER = -lm `sdl-config --libs` -lz + +include Makefile.base + +${B}sdl-joystick.o: ${B}sdl-joystick.c +${B}main.o: ${B}main.c ${B}main.h ${B}usage.h ${B}input.c ${B}keyscan.h +${B}sdl.o: ${B}sdl.c ${B}sdl.h +${B}sdl-video.o: ${B}sdl-video.c +${B}sdl-sound.o: ${B}sdl-sound.c +#${B}sdl-netplay.o: ${B}sdl-netplay.c +${B}unix-netplay.o: ${B}unix-netplay.c +${B}throttle.o: ${B}throttle.c ${B}main.h ${B}throttle.h + +include Makefile.common diff --git a/Makefile.win b/Makefile.win new file mode 100644 index 0000000..1767c71 --- /dev/null +++ b/Makefile.win @@ -0,0 +1,22 @@ +CC = gcc +TFLAGS = -mcpu=i686 -O2 -Izlib -fomit-frame-pointer -DNOSTDOUT -DC80x86 -DLSB_FIRST -DWINDOWS -DNETWORK -DPSS_STYLE=2 -DZLIB +RM = del +B = drivers/win/ +all: fceu + +include zlib/Makefile + +LDRIVER = -mwindows -lddraw -ldinput -ldsound -lgdi32 -ldxguid -lwinmm -lshell32 -lwsock32 -lcomdlg32 -lole32 +OBJDRIVER = ${B}main.o ${B}input.o ${B}joystick.o ${B}keyboard.o ${B}cheat.o ${B}res.o ${ZLIBOBJS} ${UNZIPOBJS} drivers/common/config.o + +include Makefile.base + +${B}main.o: ${B}main.c drivers/win/netplay.c ${B}config.c ${B}throttle.c ${B}video.c drivers/win/window.c drivers/win/sound.c drivers/win/wave.c +${B}cheat.o: ${B}common.h ${B}cheat.h +${B}input.o: ${B}common.h ${B}input.h ${B}joystick.h ${B}keyboard.h +${B}joystick.o: ${B}common.h ${B}joystick.h ${B}input.h +${B}keyboard.o: ${B}common.h ${B}keyboard.h ${B}input.h +drivers/win/res.o: drivers/win/res.res + windres -o drivers/win/res.o drivers/win/res.res + +include Makefile.common diff --git a/banksw.h b/banksw.h new file mode 100644 index 0000000..f45c489 --- /dev/null +++ b/banksw.h @@ -0,0 +1,101 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 Bero + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void FASTAPASS(2) VRAM_BANK1(uint32 A, uint8 V) +{ + V&=7; + PPUCHRRAM|=(1<<(A>>10)); + CHRBankList[(A)>>10]=V; + VPage[(A)>>10]=&CHRRAM[V<<10]-(A); +} + +void FASTAPASS(2) VRAM_BANK4(uint32 A, uint32 V) +{ + V&=1; + PPUCHRRAM|=(0xF<<(A>>10)); + CHRBankList[(A)>>10]=(V<<2); + CHRBankList[((A)>>10)+1]=(V<<2)+1; + CHRBankList[((A)>>10)+2]=(V<<2)+2; + CHRBankList[((A)>>10)+3]=(V<<2)+3; + VPage[(A)>>10]=&CHRRAM[V<<10]-(A); +} + +void FASTAPASS(2) VROM_BANK1(uint32 A,uint32 V) +{ + setchr1(A,V); + CHRBankList[(A)>>10]=V; +} + +void FASTAPASS(2) VROM_BANK2(uint32 A,uint32 V) +{ + setchr2(A,V); + CHRBankList[(A)>>10]=(V<<1); + CHRBankList[((A)>>10)+1]=(V<<1)+1; +} + +void FASTAPASS(2) VROM_BANK4(uint32 A, uint32 V) +{ + setchr4(A,V); + CHRBankList[(A)>>10]=(V<<2); + CHRBankList[((A)>>10)+1]=(V<<2)+1; + CHRBankList[((A)>>10)+2]=(V<<2)+2; + CHRBankList[((A)>>10)+3]=(V<<2)+3; +} + +void FASTAPASS(1) VROM_BANK8(uint32 V) +{ + setchr8(V); + CHRBankList[0]=(V<<3); + CHRBankList[1]=(V<<3)+1; + CHRBankList[2]=(V<<3)+2; + CHRBankList[3]=(V<<3)+3; + CHRBankList[4]=(V<<3)+4; + CHRBankList[5]=(V<<3)+5; + CHRBankList[6]=(V<<3)+6; + CHRBankList[7]=(V<<3)+7; +} + +void FASTAPASS(2) ROM_BANK8(uint32 A, uint32 V) +{ + setprg8(A,V); + if(A>=0x8000) + PRGBankList[((A-0x8000)>>13)]=V; +} + +void FASTAPASS(2) ROM_BANK16(uint32 A, uint32 V) +{ + setprg16(A,V); + if(A>=0x8000) + { + PRGBankList[((A-0x8000)>>13)]=V<<1; + PRGBankList[((A-0x8000)>>13)+1]=(V<<1)+1; + } +} + +void FASTAPASS(1) ROM_BANK32(uint32 V) +{ + setprg32(0x8000,V); + PRGBankList[0]=V<<2; + PRGBankList[1]=(V<<2)+1; + PRGBankList[2]=(V<<2)+2; + PRGBankList[3]=(V<<2)+3; +} + diff --git a/boards/Makefile b/boards/Makefile new file mode 100644 index 0000000..58dd4ef --- /dev/null +++ b/boards/Makefile @@ -0,0 +1,9 @@ +MUOBJS = boards/simple.o boards/malee.o boards/supervision.o boards/novel.o boards/super24.o boards/h2288.o boards/sachen.o + +boards/simple.o: boards/simple.c +boards/malee.o: boards/malee.c +boards/supervision.o: boards/supervision.c +boards/novel.o: boards/novel.c +boards/super24.o: boards/super24.c +boards/h2288.o: boards/h2288.c +boards/sachen.o: boards/sachen.c diff --git a/boards/h2288.c b/boards/h2288.c new file mode 100644 index 0000000..c8ad1e0 --- /dev/null +++ b/boards/h2288.c @@ -0,0 +1,99 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Not finished. Darn evil game... *Mumble*... */ + +#include "mapinc.h" + +static uint8 cmd; +static uint8 regs[8]; + +static void DoPRG(void) +{ + if(cmd&0x40) + { + setprg8(0xC000,regs[4]); + setprg8(0xA000,regs[5]); + setprg8(0x8000,~1); + setprg8(0xE000,~0); + } + else + { + setprg8(0x8000,regs[4]); + setprg8(0xA000,regs[5]); + setprg8(0xC000,~1); + setprg8(0xE000,~0); + } +} + +static void DoCHR(void) +{ + uint32 base=(cmd&0x80)<<5; + + setchr2(0x0000^base,regs[0]); + setchr2(0x0800^base,regs[2]); + + setchr1(0x1000^base,regs[6]); + setchr1(0x1400^base,regs[1]); + setchr1(0x1800^base,regs[7]); + setchr1(0x1c00^base,regs[3]); +} + +static DECLFW(H2288Write) +{ + //printf("$%04x:$%02x, $%04x\n",A,V,X.PC.W); + //RAM[0x7FB]=0x60; + switch(A&0xE001) + { + case 0xa000:setmirror((V&1)^1);break; + case 0x8000:// DumpMem("out",0x0000,0xFFFF); + cmd=V;DoPRG();DoCHR();break; + case 0x8001:regs[cmd&7]=V; + if((cmd&7)==4 || (cmd&7)==5) + DoPRG(); + else + DoCHR(); + break; + } +} + +static DECLFR(H2288Read) +{ + //printf("Rd: $%04x, $%04x\n",A,X.PC.W); + return((X.DB&0xFE)|(A&(A>>8))); +} + +static void H2288Reset(void) +{ + int x; + + SetReadHandler(0x5800,0x5FFF,H2288Read); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xFFFF,H2288Write); + for(x=0;x<8;x++) regs[x]=0; + cmd=0; + DoPRG(); + DoCHR(); +} + +void H2288_Init(void) +{ + BoardPower=H2288Reset; +} diff --git a/boards/malee.c b/boards/malee.c new file mode 100644 index 0000000..f575159 --- /dev/null +++ b/boards/malee.c @@ -0,0 +1,45 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +DECLFW(MWrite) +{ + (GameMemBlock-0x7000)[A]=V; +} + +static void MALEEReset(void) +{ + setprg2r(0x10,0x7000,0); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetReadHandler(0x6000,0x67ff,CartBR); + SetReadHandler(0x7000,0x77FF,CartBR); + SetWriteHandler(0x7000,0x77FF,MWrite); + setprg2r(1,0x6000,0); + setprg32(0x8000,0); + setchr8(0); +} + +void MALEE_Init(void) +{ + AddExState(GameMemBlock, 2048, 0,"RAM"); + SetupCartPRGMapping(0x10,GameMemBlock,2048,1); + BoardPower=MALEEReset; +} diff --git a/boards/mapinc.h b/boards/mapinc.h new file mode 100644 index 0000000..e6d4c41 --- /dev/null +++ b/boards/mapinc.h @@ -0,0 +1,11 @@ +#include "../types.h" +#include "../x6502.h" +#include "../fce.h" +#include "../version.h" +#include "../memory.h" +#include "../sound.h" +#include "../svga.h" +#include "../state.h" +#define UNIFPRIV +#include "../unif.h" +#include "../cart.h" diff --git a/boards/novel.c b/boards/novel.c new file mode 100644 index 0000000..7d620db --- /dev/null +++ b/boards/novel.c @@ -0,0 +1,53 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static void DoNovel(void) +{ + setprg32(0x8000,GameMemBlock[0]&3); + setchr8(GameMemBlock[0]&7); +} + +static DECLFW(NovelWrite) +{ + GameMemBlock[0]=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(void) +{ + AddExState(&GameMemBlock[0], 1, 0,"L1"); + BoardPower=NovelReset; + GameStateRestore=NovelRestore; +} diff --git a/boards/sachen.c b/boards/sachen.c new file mode 100644 index 0000000..d4d20c2 --- /dev/null +++ b/boards/sachen.c @@ -0,0 +1,289 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static uint8 cmd; +static uint8 latch[8]; +#define CHRRAM (GameMemBlock) + +static void S74LS374NSynco(void) +{ + setprg32(0x8000,latch[0]); + setchr8(latch[1]); + setmirror(latch[2]&1); +// setchr8(6); +} + +static DECLFW(S74LS374NWrite) +{ + //printf("$%04x:$%02x\n",A,V); + A&=0x4101; + if(A==0x4100) + cmd=V&7; + else + { + switch(cmd) + { + case 0:latch[0]=0;latch[1]=3;break; + case 4:latch[1]&=3;latch[1]|=(V<<2);break; + case 5:latch[0]=V&0x7;break; + case 6:latch[1]&=0x1C;latch[1]|=V&3;break; + case 7:latch[2]=V&1;break; + } + S74LS374NSynco(); + } +} + +static void S74LS374NReset(void) +{ + latch[0]=latch[2]=0; + latch[1]=3; + S74LS374NSynco(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0x7FFF,S74LS374NWrite); +} + +static void S74LS374NRestore(int version) +{ + S74LS374NSynco(); +} + +void S74LS374N_Init(void) +{ + BoardPower=S74LS374NReset; + GameStateRestore=S74LS374NRestore; + AddExState(latch, 3, 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. + { + if(!type) + { + for(x=0;x<4;x++) + setchr2(0x800*x,(x&1)|((latch[x]&7)<<1)|((latch[4]&7)<<4)); + } + else + { + for(x=0;x<4;x++) + setchr2(0x800*x,(latch[x]&0x7)|((latch[4]&7)<<3)); + } + } + switch((latch[7]>>1)&3) + { + case 0:setmirrorw(0,0,0,1);break; + case 1:setmirror(MI_H);break; + case 2:setmirror(MI_V);break; + case 3:setmirror(MI_0);break; + } +} + +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; + if(UNIFchrrama) setchr8(0); + + S8259Synco(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0x7FFF,S8259Write); +} + +static void S8259Restore(int version) +{ + S8259Synco(); +} + +void S8259A_Init(void) +{ + BoardPower=S8259Reset; + GameStateRestore=S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type=0; + + //if(!CHRsize[0]) + //{ + // SetupCartCHRMapping(0,CHRRAM,8192,1); + // AddExState(CHRRAM, 8192, 0, "CHRR"); + //} +} + +void S8259B_Init(void) +{ + BoardPower=S8259Reset; + GameStateRestore=S8259Restore; + AddExState(latch, 8, 0, "LATC"); + AddExState(&cmd, 1, 0, "CMD"); + type=1; +} + +static void(*WSync)(void); + +static void SA0161MSynco() +{ + setprg32(0x8000,(latch[0]>>3)&1); + setchr8(latch[0]&7); +} + +static DECLFW(SAWrite) +{ + if(A&0x100) + { + latch[0]=V; + WSync(); + } +} + +static void SAReset(void) +{ + latch[0]=0; + WSync(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0x5FFF,SAWrite); +} + +void SA0161M_Init(void) +{ + WSync=SA0161MSynco; + GameStateRestore=SA0161MSynco; + BoardPower=SAReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static void SA72007Synco() +{ + setprg32(0x8000,0); + setchr8(latch[0]>>7); +} + +void SA72007_Init(void) +{ + WSync=SA72007Synco; + GameStateRestore=SA72007Synco; + BoardPower=SAReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static void SA72008Synco() +{ + setprg32(0x8000,(latch[0]>>2)&1); + setchr8(latch[0]&3); +} + +void SA72008_Init(void) +{ + WSync=SA72008Synco; + GameStateRestore=SA72008Synco; + BoardPower=SAReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static DECLFW(SADWrite) +{ + latch[0]=V; + WSync(); +} + +static void SADReset(void) +{ + latch[0]=0; + WSync(); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xFFFF,SADWrite); +} + +static void SA0036Synco() +{ + setprg32(0x8000,0); + setchr8(latch[0]>>7); +} + +static void SA0037Synco() +{ + setprg32(0x8000,(latch[0]>>3)&1); + setchr8(latch[0]&7); +} + +void SA0036_Init(void) +{ + WSync=SA0036Synco; + GameStateRestore=SA0036Synco; + BoardPower=SADReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +void SA0037_Init(void) +{ + WSync=SA0037Synco; + GameStateRestore=SA0037Synco; + BoardPower=SADReset; + AddExState(&latch[0], 1, 0, "LATC"); +} + +static void TCU01Synco() +{ + setprg32(0x8000,(latch[0]>>2)&1); + setchr8((latch[0]>>3)&0xF); +} + +static DECLFW(TCWrite) +{ + if((A&0x103)==0x102) + latch[0]=V; + TCU01Synco(); +} + +static void TCU01Reset(void) +{ + latch[0]=0; + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x4100,0xFFFF,TCWrite); + TCU01Synco(); +} + +void TCU01_Init(void) +{ + GameStateRestore=TCU01Synco; + BoardPower=TCU01Reset; + AddExState(&latch[0], 1, 0, "LATC"); +} + diff --git a/boards/simple.c b/boards/simple.c new file mode 100644 index 0000000..ca4d1db --- /dev/null +++ b/boards/simple.c @@ -0,0 +1,162 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define CHRRAM (GameMemBlock) +static uint8 latche; + +DECLFW(CPROMWrite) +{ + latche=V&3; + setvram4(0x1000,CHRRAM+((V&3)<<12)); +} + +static void CPROMReset(void) +{ + setprg32(0x8000,0); + setvram8(0); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xffff,CPROMWrite); +} + +static void CPROMRestore(int version) +{ + setvram4(0x1000,CHRRAM+((latche)<<12)); +} + +void CPROM_Init(void) +{ + BoardPower=CPROMReset; + GameStateRestore=CPROMRestore; + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(CNROMWrite) +{ + latche=V&3; + setchr8(V&3); +} + +static void CNROMReset(void) +{ + setprg16(0x8000,0); + setprg16(0xC000,1); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0x8000,0xffff,CNROMWrite); +} + +static void CNROMRestore(int version) +{ + setchr8(latche); +} + +void CNROM_Init(void) +{ + BoardPower=CNROMReset; + GameStateRestore=CNROMRestore; + AddExState(&latche, 1, 0, "LATC"); +} + +static void NROM128Reset(void) +{ + setprg16(0x8000,0); + setprg16(0xC000,0); + setchr8(0); + SetReadHandler(0x8000,0xFFFF,CartBR); +} + +static void NROM256Reset(void) +{ + setprg16(0x8000,0); + setprg16(0xC000,1); + setchr8(0); + SetReadHandler(0x8000,0xFFFF,CartBR); +} +void NROM128_Init(void) +{ + BoardPower=NROM128Reset; +} + +void NROM256_Init(void) +{ + BoardPower=NROM256Reset; +} + +static DECLFW(MHROMWrite) +{ + setprg32(0x8000,V>>4); + setchr8(V); + latche=V; +} + +static void MHROMReset(void) +{ + setprg32(0x8000,0); + setchr8(0); + latche=0; + SetReadHandler(0x8000,0xFFFF,CartBR); +} + +static void MHROMRestore(int version) +{ + setprg32(0x8000,latche); + setchr8(latche); + SetWriteHandler(0x8000,0xffff,MHROMWrite); +} + +void MHROM_Init(void) +{ + BoardPower=MHROMReset; + AddExState(&latche, 1, 0,"LATC"); + PRGmask32[0]&=1; + CHRmask8[0]&=1; + GameStateRestore=MHROMRestore; +} + +static void UNROMRestore(int version) +{ + setprg16(0x8000,latche); +} + +static DECLFW(UNROMWrite) +{ + setprg16(0x8000,V); + latche=V; +} + +static void UNROMReset(void) +{ + setprg16(0x8000,0); + setprg16(0xc000,~0); + setvram8(CHRRAM); + SetWriteHandler(0x8000,0xffff,UNROMWrite); + SetReadHandler(0x8000,0xFFFF,CartBR); + latche=0; +} + +void UNROM_Init(void) +{ + BoardPower=UNROMReset; + PRGmask16[0]&=7; + AddExState(&latche, 1, 0, "LATC"); + AddExState(CHRRAM, 8192, 0, "CHRR"); + GameStateRestore=UNROMRestore; +} diff --git a/boards/super24.c b/boards/super24.c new file mode 100644 index 0000000..64af337 --- /dev/null +++ b/boards/super24.c @@ -0,0 +1,236 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" +//undef printf + +static int32 IRQCount,IRQLatch; +static uint8 IRQa,resetmode,mbia; +static uint8 sizer,bigbank,bigbank2; + +static uint8 DRegBuf[8],MMC3_cmd; + +static int masko8[8]={63,31,15,1,3,0,0,0}; +//static int masko1[8]={511,255,127,7,7,0,0,0}; + +static void swsetprg8(uint32 A, uint32 V) +{ + V&=masko8[sizer&7]; + V|=(bigbank*2); + setprg8r((V/64)&15,A,V); +} + +static void swsetchr1(uint32 A, uint32 V) +{ + if(sizer&0x20) + setchr1r(0x10,A,V); + else + { +// V&=masko1[sizer&7]; + V|=bigbank2*8; + setchr1r((V/512)&15,A,V); + } +} + +static void swsetchr2(uint32 A, uint32 V) +{ + if(sizer&0x20) + setchr2r(0x10,A,V); + else + { + //V&=masko1[sizer&7]>>1; + V|=bigbank2*4; + setchr2r((V/256)&15,A,V); + } +} + +static void Sup24_hb(void) +{ + if(ScreenON || SpriteON) + { + resetmode=0; + if(IRQCount>=0) + { + IRQCount--; + if(IRQCount<0) + { + if(IRQa) + { + resetmode = 1; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } + } +} + +static DECLFW(Sup24IRQWrite) +{ + switch(A&0xE001) + { + case 0xc000:IRQLatch=V; + if(resetmode==1) + IRQCount=IRQLatch; + break; + case 0xc001:resetmode=1; + IRQCount=IRQLatch; + break; + case 0xE000:IRQa=0;X6502_IRQEnd(FCEU_IQEXT); + if(resetmode==1) + {IRQCount=IRQLatch;} + break; + case 0xE001:IRQa=1; + if(resetmode==1) + {IRQCount=IRQLatch;} + break; + } +} + +static INLINE void FixMMC3PRG(int V) +{ + swsetprg8(0xA000,DRegBuf[7]); + swsetprg8(0xE000,~0); + if(V&0x40) + { + swsetprg8(0xC000,DRegBuf[6]); + swsetprg8(0x8000,~1); + } + else + { + swsetprg8(0x8000,DRegBuf[6]); + swsetprg8(0xC000,~1); + } +} + +static INLINE void FixMMC3CHR(int V) +{ + int cbase=(V&0x80)<<5; + swsetchr2((cbase^0x000),DRegBuf[0]>>1); + swsetchr2((cbase^0x800),DRegBuf[1]>>1); + swsetchr1(cbase^0x1000,DRegBuf[2]); + swsetchr1(cbase^0x1400,DRegBuf[3]); + swsetchr1(cbase^0x1800,DRegBuf[4]); + swsetchr1(cbase^0x1c00,DRegBuf[5]); +} + +static DECLFW(Super24hiwrite) +{ + //printf("$%04x:$%02x, %d\n",A,V,scanline); + 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: V>>=1;swsetchr2((cbase^0x000),V);break; + case 1: V>>=1;swsetchr2((cbase^0x800),V);break; + case 2: swsetchr1(cbase^0x1000,V); break; + case 3: swsetchr1(cbase^0x1400,V); break; + case 4: swsetchr1(cbase^0x1800,V); break; + case 5: swsetchr1(cbase^0x1C00,V); break; + case 6: if (MMC3_cmd&0x40) swsetprg8(0xC000,V); + else swsetprg8(0x8000,V); + break; + case 7: swsetprg8(0xA000,V); + break; + } + } + break; + + case 0xA000: + mbia=V; + setmirror((V&1)^1); + break; + } +} + + +DECLFW(Super24Write) +{ + //printf("$%04x:$%02x\n",A,V); + switch(A) + { + case 0x5ff0:sizer=V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + break; + case 0x5FF1: + bigbank=V; + FixMMC3PRG(MMC3_cmd); + break; + case 0x5FF2: + bigbank2=V; + FixMMC3CHR(MMC3_cmd); + break; + } +} + +static void Super24Reset(void) +{ + SetWriteHandler(0x8000,0xBFFF,Super24hiwrite); + SetWriteHandler(0x5000,0x7FFF,Super24Write); + SetWriteHandler(0xC000,0xFFFF,Sup24IRQWrite); + SetReadHandler(0x8000,0xFFFF,CartBR); + GameHBIRQHook=Sup24_hb; + IRQCount=IRQLatch=IRQa=resetmode=0; + sizer=0x24; + bigbank=159; + bigbank2=0; + + MMC3_cmd=0; + DRegBuf[6]=0; + DRegBuf[7]=1; + + FixMMC3PRG(0); + FixMMC3CHR(0); +} + +static void MrRestore(int version) +{ + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + setmirror((mbia&1)^1); +} + +void Super24_Init(void) +{ + BoardPower=Super24Reset; + SetupCartCHRMapping(0x10, GameMemBlock, 8192, 1); + GameStateRestore=MrRestore; + + AddExState(GameMemBlock, 8192, 0, "CHRR"); + AddExState(DRegBuf, 8, 0, "DREG"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQLatch, 4, 1, "IQL1"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(&sizer, 1, 0, "SIZA"); + AddExState(&bigbank, 1, 0, "BIG1"); + AddExState(&bigbank2, 1, 0, "BIG2"); +} diff --git a/boards/supervision.c b/boards/supervision.c new file mode 100644 index 0000000..fc22599 --- /dev/null +++ b/boards/supervision.c @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define CHRRAM (GameMemBlock+16) + +static void DoSuper(void) +{ + setprg8r((GameMemBlock[0]&0xC)>>2,0x6000,((GameMemBlock[0]&0x3)<<4)|0xF); + if(GameMemBlock[0]&0x10) + { + setprg16r((GameMemBlock[0]&0xC)>>2,0x8000,((GameMemBlock[0]&0x3)<<3)|(GameMemBlock[1]&7)); + setprg16r((GameMemBlock[0]&0xC)>>2,0xc000,((GameMemBlock[0]&0x3)<<3)|7); + } + else + setprg32r(4,0x8000,0); + + setmirror(((GameMemBlock[0]&0x20)>>5)^1); +} + +static DECLFW(SuperWrite) +{ + if(!(GameMemBlock[0]&0x10)) + { + GameMemBlock[0]=V; + DoSuper(); + } +} + +static DECLFW(SuperHi) +{ + GameMemBlock[1]=V; + DoSuper(); +} + +static void SuperReset(void) +{ + SetWriteHandler(0x6000,0x7FFF,SuperWrite); + SetWriteHandler(0x8000,0xFFFF,SuperHi); + SetReadHandler(0x6000,0xFFFF,CartBR); + GameMemBlock[0]=GameMemBlock[1]=0; + setprg32r(4,0x8000,0); + setvram8(CHRRAM); +} + +static void SuperRestore(int version) +{ + DoSuper(); +} + +void Supervision16_Init(void) +{ + AddExState(&GameMemBlock[0], 1, 0,"L1"); + AddExState(&GameMemBlock[1], 1, 0,"L2"); + AddExState(CHRRAM, 8192, 0, "CHRR"); + BoardPower=SuperReset; + GameStateRestore=SuperRestore; +} diff --git a/cart.c b/cart.c new file mode 100644 index 0000000..030e764 --- /dev/null +++ b/cart.c @@ -0,0 +1,590 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 /* For GG loading code. */ + +#include "types.h" +#include "version.h" +#include "fce.h" +#include "cart.h" +#include "memory.h" + +#include "general.h" +#include "svga.h" + +/* + This file contains all code for coordinating the mapping in of the + address space external to the NES. + It's also (ab)used by the NSF code. +*/ + +uint8 *Page[32],*VPage[8]; +uint8 **VPageR=VPage; +uint8 *VPageG[8]; +uint8 *MMC5SPRVPage[8]; +uint8 *MMC5BGVPage[8]; + +/* 16 are (sort of) reserved for UNIF/iNES and 16 to map other stuff. */ + +static int CHRram[32]; +static int 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]; + +static INLINE void setpageptr(int s, uint32 A, uint8 *p) +{ + uint32 AB=A>>11; + int x; + + for(x=(s>>1)-1;x>=0;x--) + Page[AB+x]=p-A; +} + +static char nothing[8192]; +void ResetCartMapping(void) +{ + int x; + + 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]; +} + +void FASTAPASS(3) GINLINE setprg2r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + V&=PRGmask2[r]; + + setpageptr(2,A,(&PRGptr[r][V<<11])); +} + +void FASTAPASS(2) setprg2(uint32 A, uint32 V) +{ + setprg2r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg4r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + V&=PRGmask4[r]; + setpageptr(4,A,(&PRGptr[r][V<<12])); +} + +void FASTAPASS(2) setprg4(uint32 A, uint32 V) +{ + setprg4r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg8r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + + if(PRGsize[r]>=8192) + { + V&=PRGmask8[r]; + setpageptr(8,A,(&PRGptr[r][V<<13])); + } + else + { + uint32 VA=V<<2; + int x; + for(x=0;x<4;x++) + setpageptr(2,A+(x<<11),(&PRGptr[r][((VA+x)&PRGmask2[r])<<11])); + } +} + +void FASTAPASS(2) setprg8(uint32 A, uint32 V) +{ + setprg8r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg16r(int r, unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + + if(PRGsize[r]>=16384) + { + V&=PRGmask16[r]; + setpageptr(16,A,(&PRGptr[r][V<<14])); + } + else + { + uint32 VA=V<<3; + int x; + + for(x=0;x<8;x++) + setpageptr(2,A+(x<<11),(&PRGptr[r][((VA+x)&PRGmask2[r])<<11])); + } +} + +void FASTAPASS(2) setprg16(uint32 A, uint32 V) +{ + setprg16r(0,A,V); +} + +void FASTAPASS(3) GINLINE setprg32r(int r,unsigned int A, unsigned int V) +{ + if(!PRGptr[r]) return; + if(PRGsize[r]>=32768) + { + V&=PRGmask32[r]; + setpageptr(32,A,(&PRGptr[r][V<<15])); + } + else + { + uint32 VA=V<<4; + int x; + + for(x=0;x<16;x++) + setpageptr(2,A+(x<<11),(&PRGptr[r][((VA+x)&PRGmask2[r])<<11])); + } +} + +void FASTAPASS(2) setprg32(uint32 A, uint32 V) +{ + setprg32r(0,A,V); +} + +void GINLINE FASTAPASS(3) setchr1r(int r, unsigned int A, unsigned int V) +{ + if(!CHRptr[r]) return; + V&=CHRmask1[r]; + if(CHRram[r]) + PPUCHRRAM|=(1<<(A>>10)); + else + PPUCHRRAM&=~(1<<(A>>10)); + VPageR[(A)>>10]=&CHRptr[r][(V)<<10]-(A); +} + +void GINLINE FASTAPASS(3) setchr2r(int r, unsigned int A, unsigned int V) +{ + if(!CHRptr[r]) return; + 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 GINLINE FASTAPASS(3) setchr4r(int r, unsigned int A, unsigned int V) +{ + if(!CHRptr[r]) return; + 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 GINLINE FASTAPASS(2) setchr8r(int r, unsigned int V) +{ + int x; + + if(!CHRptr[r]) return; + V&=CHRmask8[r]; + for(x=7;x>=0;x--) + VPageR[x]=&CHRptr[r][V<<13]; + if(CHRram[r]) + PPUCHRRAM|=(255); + else + PPUCHRRAM&=~(255); +} + +void FASTAPASS(2) setchr1(unsigned int A, unsigned int V) +{ + setchr1r(0,A,V); +} + +void FASTAPASS(2) setchr2(unsigned int A, unsigned int V) +{ + setchr2r(0,A,V); +} + +void FASTAPASS(2) setchr4(unsigned int A, unsigned int V) +{ + setchr4r(0,A,V); +} + +void FASTAPASS(1) setchr8(unsigned int V) +{ + setchr8r(0,V); +} + +void FASTAPASS(1) setvram8(uint8 *p) +{ + int x; + for(x=7;x>=0;x--) + VPageR[x]=p; + PPUCHRRAM|=255; +} + +void FASTAPASS(2) setvram4(uint32 A, uint8 *p) +{ + int x; + for(x=3;x>=0;x--) + VPageR[(A>>10)+x]=p-A; + PPUCHRRAM|=(15<<(A>>10)); +} + +void FASTAPASS(3) setvramb1(uint8 *p, uint32 A, uint32 b) +{ + VPageR[A>>10]=p-A+(b<<10); + PPUCHRRAM|=(1<<(A>>10)); +} + +void FASTAPASS(3) setvramb2(uint8 *p, uint32 A, uint32 b) +{ + VPageR[(A>>10)]=VPageR[(A>>10)+1]=p-A+(b<<11); + PPUCHRRAM|=(3<<(A>>10)); +} + +void FASTAPASS(3) setvramb4(uint8 *p, uint32 A, uint32 b) +{ + int x; + + for(x=3;x>=0;x--) + VPageR[(A>>10)+x]=p-A+(b<<12); + PPUCHRRAM|=(15<<(A>>10)); +} + +void FASTAPASS(2) setvramb8(uint8 *p, uint32 b) +{ + int x; + + for(x=7;x>=0;x--) + VPageR[x]=p+(b<<13); + PPUCHRRAM|=255; +} + +/* This function can be called without calling SetupCartMirroring(). */ + +void FASTAPASS(3) setntamem(uint8 *p, int ram, uint32 b) +{ + vnapage[b]=p; + PPUNTARAM&=~(1<>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(); + + 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 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; +} + + diff --git a/cart.h b/cart.h new file mode 100644 index 0000000..b066771 --- /dev/null +++ b/cart.h @@ -0,0 +1,70 @@ +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(CartBR); +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 FASTAPASS(2) setprg2(uint32 A, uint32 V); +void FASTAPASS(2) setprg4(uint32 A, uint32 V); +void FASTAPASS(2) setprg8(uint32 A, uint32 V); +void FASTAPASS(2) setprg16(uint32 A, uint32 V); +void FASTAPASS(2) setprg32(uint32 A, uint32 V); + +void FASTAPASS(3) setprg2r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg4r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg8r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg16r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setprg32r(int r, unsigned int A, unsigned int V); + +void FASTAPASS(3) setchr1r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setchr2r(int r, unsigned int A, unsigned int V); +void FASTAPASS(3) setchr4r(int r, unsigned int A, unsigned int V); +void FASTAPASS(2) setchr8r(int r, unsigned int V); + +void FASTAPASS(2) setchr1(unsigned int A, unsigned int V); +void FASTAPASS(2) setchr2(unsigned int A, unsigned int V); +void FASTAPASS(2) setchr4(unsigned int A, unsigned int V); +void FASTAPASS(2) setchr8(unsigned int V); + +void FASTAPASS(2) setvram4(uint32 A, uint8 *p); +void FASTAPASS(1) setvram8(uint8 *p); + +void FASTAPASS(3) setvramb1(uint8 *p, uint32 A, uint32 b); +void FASTAPASS(3) setvramb2(uint8 *p, uint32 A, uint32 b); +void FASTAPASS(3) setvramb4(uint8 *p, uint32 A, uint32 b); +void FASTAPASS(2) setvramb8(uint8 *p, uint32 b); + +void FASTAPASS(1) setmirror(int t); +void setmirrorw(int a, int b, int c, int d); +void FASTAPASS(3) 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 GeniePower(void); + +void OpenGenie(void); +void CloseGenie(void); diff --git a/cheat.c b/cheat.c new file mode 100644 index 0000000..d789d74 --- /dev/null +++ b/cheat.c @@ -0,0 +1,565 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "x6502.h" +#include "cheat.h" +#include "fce.h" +#include "svga.h" +#include "general.h" +#include "cart.h" +#include "memory.h" + +static uint8 *CheatRPtrs[64]; + +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; +} + + +struct CHEATF { + struct CHEATF *next; + char *name; + uint16 addr; + uint8 val; + int status; +}; + +struct CHEATF *cheats=0,*cheatsl=0; + + +#define CHEATC_NONE 0x8000 +#define CHEATC_EXCLUDED 0x4000 +#define CHEATC_NOSHOW 0xC000 + +static uint16 *CheatComp=0; +static int savecheats; + +static int AddCheatEntry(char *name, uint32 addr, uint8 val, int status); + +static void CheatMemErr(void) +{ + FCEUD_PrintError("Error allocating memory for cheat data."); +} + +/* This function doesn't allocate any memory for "name" */ +static int AddCheatEntry(char *name, uint32 addr, uint8 val, int status) +{ + struct CHEATF *temp; + if(!(temp=malloc(sizeof(struct CHEATF)))) + { + CheatMemErr(); + return(0); + } + temp->name=name; + temp->addr=addr; + temp->val=val; + temp->status=status; + + temp->next=0; + + if(cheats) + { + cheatsl->next=temp; + cheatsl=temp; + } + else + cheats=cheatsl=temp; + + return(1); +} + +void LoadGameCheats(void) +{ + FILE *fp; + char *name=0; + unsigned int addr; + unsigned int val; + unsigned int status; + int x; + + char linebuf[256+4+2+2+1+1]; /* 256 for name, 4 for address, 2 for value, 2 for semicolons, 1 for status, 1 for null */ + char namebuf[257]; + int tc=0; + + savecheats=0; + if(!(fp=fopen(FCEU_MakeFName(FCEUMKF_CHEAT,0,0),"rb"))) + return; + + while(fgets(linebuf,256+4+2+2+1+1,fp)>0) + { + addr=val=status=0; + namebuf[0]=0; // If the cheat doesn't have a name... + if(linebuf[0]==':') + { + strncpy(namebuf,&linebuf[1+4+2+2],257); + if(sscanf(&linebuf[1],"%04x%*[:]%02x",&addr,&val)!=2) + continue; + status=0; + } + else + { + strncpy(namebuf,&linebuf[4+2+2],257); + if(sscanf(linebuf,"%04x%*[:]%02x",&addr,&val)!=2) continue; + status=1; + } + for(x=0;x<257;x++) + { + if(namebuf[x]==10 || namebuf[x]==13) + { + namebuf[x]=0; + break; + } + } + namebuf[256]=0; + if(!(name=malloc(strlen(namebuf)+1))) + CheatMemErr(); + else + { + strcpy(name,namebuf); + AddCheatEntry(name,addr,val,status); + tc++; + } + } + fclose(fp); +} + + +void FlushGameCheats(void) +{ + if(CheatComp) + { + free(CheatComp); + CheatComp=0; + } + + if(!savecheats) + { + if(cheats) + { + struct CHEATF *next=cheats; + for(;;) + { + next=next->next; + free(cheats->name); + free(cheats); + if(!next) break; + } + cheats=cheatsl=0; + } + } + else + { + if(cheats) + { + struct CHEATF *next=cheats; + FILE *fp; + if((fp=fopen(FCEU_MakeFName(FCEUMKF_CHEAT,0,0),"wb"))) + { + for(;;) + { + struct CHEATF *t; + if(next->status) + fprintf(fp,"%04x:%02x:%s\n",next->addr,next->val,next->name); + else + fprintf(fp,":%04x:%02x:%s\n",next->addr,next->val,next->name); + free(next->name); + t=next; + next=next->next; + free(t); + if(!next) break; + } + fclose(fp); + } + else + FCEUD_PrintError("Error saving cheats."); + cheats=cheatsl=0; + } + else + remove(FCEU_MakeFName(FCEUMKF_CHEAT,0,0)); + } +} + + +int FCEUI_AddCheat(char *name, uint32 addr, uint8 val) +{ + char *t; + + if(!(t=malloc(strlen(name)+1))) + { + CheatMemErr(); + return(0); + } + strcpy(t,name); + if(!AddCheatEntry(t,addr,val,1)) + { + free(t); + return(0); + } + savecheats=1; + 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; + return(1); +} + +void ApplyPeriodicCheats(void) +{ + struct CHEATF *cur=cheats; + if(!cur) return; + + for(;;) + { + if(cur->status) + 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 s)) +{ + struct CHEATF *next=cheats; + + while(next) + { + if(!callb(next->name,next->addr,next->val,next->status)) break; + next=next->next; + } +} + +int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *s) +{ + 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; + + return(1); + } + next=next->next; + x++; + } + return(0); +} + +/* 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, char *name, int32 a, int32 v, int s) +{ + struct CHEATF *next=cheats; + uint32 x=0; + + while(next) + { + if(x==which) + { + if(name) + { + char *t; + + if((t=realloc(next->name,strlen(name+1)))) + { + next->name=t; + strcpy(next->name,name); + } + else + return(0); + } + if(a>=0) + next->addr=a; + if(v>=0) + next->val=v; + if(s>=0) + next->status=s; + savecheats=1; + return(1); + } + next=next->next; + x++; + } + return(0); +} + + +static int InitCheatComp(void) +{ + uint32 x; + + CheatComp=malloc(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; + 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)) +{ + 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])) + 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; + } + } + + + if(!type) // Change to a specific value. + { + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatComp[x]==v1 && CheatRPtrs[x>>10][x]==v2) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + } + else if(type==1) // Search for relative change(between values). + { + for(x=0;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatComp[x]==v1 && CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + } + else if(type==2) // Purely relative change. + { + for(x=0x000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CAbs(CheatComp[x]-CheatRPtrs[x>>10][x])==v2) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + } + else if(type==3) // Any change. + { + for(x=0x000;x<0x10000;x++) + if(!(CheatComp[x]&CHEATC_NOSHOW)) + { + if(CheatComp[x]!=CheatRPtrs[x>>10][x]) + { + + } + else + CheatComp[x]|=CHEATC_EXCLUDED; + } + + } +} diff --git a/cheat.h b/cheat.h new file mode 100644 index 0000000..e930f52 --- /dev/null +++ b/cheat.h @@ -0,0 +1,6 @@ +void FCEU_CheatResetRAM(void); +void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p); + +void LoadGameCheats(void); +void FlushGameCheats(void); +void ApplyPeriodicCheats(void); diff --git a/crc32.c b/crc32.c new file mode 100644 index 0000000..c4d272b --- /dev/null +++ b/crc32.c @@ -0,0 +1,100 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 ZLIB +#include "types.h" +#include "crc32.h" + +static uint32 CRC32Table[256] = { + 0x00000000,0x77073096,0xee0e612c,0x990951ba, + 0x076dc419,0x706af48f,0xe963a535,0x9e6495a3, + 0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988, + 0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91, + 0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de, + 0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7, + 0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, + 0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5, + 0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172, + 0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b, + 0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940, + 0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59, + 0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116, + 0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, + 0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924, + 0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d, + 0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a, + 0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433, + 0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818, + 0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01, + 0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, + 0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457, + 0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c, + 0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65, + 0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2, + 0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb, + 0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0, + 0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, + 0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086, + 0x5768b525,0x206f85b3,0xb966d409,0xce61e49f, + 0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4, + 0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad, + 0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a, + 0xead54739,0x9dd277af,0x04db2615,0x73dc1683, + 0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, + 0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1, + 0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe, + 0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7, + 0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc, + 0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5, + 0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252, + 0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, + 0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60, + 0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79, + 0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236, + 0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f, + 0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04, + 0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d, + 0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, + 0x9c0906a9,0xeb0e363f,0x72076785,0x05005713, + 0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38, + 0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21, + 0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e, + 0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777, + 0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c, + 0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, + 0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2, + 0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db, + 0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0, + 0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9, + 0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6, + 0xbad03605,0xcdd70693,0x54de5729,0x23d967bf, + 0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, + 0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d +}; + +uint32 CalcCRC32(uint32 crc, uint8 *buf, uint32 len) +{ + crc=~crc; + + while(len--) + crc=(crc>>8)^CRC32Table[(crc&0xFF)^(*buf++)]; + return(~crc); +} +#endif diff --git a/crc32.h b/crc32.h new file mode 100644 index 0000000..fa1291c --- /dev/null +++ b/crc32.h @@ -0,0 +1,6 @@ +#ifdef ZLIB +#include +#define CalcCRC32 crc32 +#else +uint32 CalcCRC32(uint32 crc, uint8 *buf, uint32 len); +#endif diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..206a58d --- /dev/null +++ b/debug.c @@ -0,0 +1,35 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "fce.h" +#include "debug.h" + + + +void DumpMem(char *fname, uint32 start, uint32 end) +{ + FILE *fp=fopen(fname,"wb"); + for(;start<=end;start++) + fputc(ARead[start](start),fp); + fclose(fp); + +} diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..85afa34 --- /dev/null +++ b/debug.h @@ -0,0 +1,2 @@ +void DumpMem(char *fname, uint32 start, uint32 end); + diff --git a/drawing.h b/drawing.h new file mode 100644 index 0000000..4e7bfec --- /dev/null +++ b/drawing.h @@ -0,0 +1,154 @@ +static void DrawDips(void) +{ + uint32 *dest; + int y,x; + + dest=(uint32 *)(XBuf+272*12+164); + for(y=24;y;y--,dest+=(272-72)>>2) + { + for(x=72>>2;x;x--,dest++) + *dest=0x80808080; + } + + dest=(uint32 *)(XBuf+272*(12+4)+164+6 ); + for(y=16;y;y--,dest+=(272>>2)-16) + for(x=8;x;x--) + { + *dest=0x81818181; + dest+=2; + } + + dest=(uint32 *)(XBuf+272*(12+4)+164+6 ); + for(x=0;x<8;x++,dest+=2) + { + uint32 *da=dest+(272>>2); + + if(!((vsdip>>x)&1)) + da+=(272>>2)*10; + + for(y=4;y;y--,da+=272>>2) + *da=0x80808080; + + } +} + +static void DrawMessage(void) +{ + if(howlong) + { + uint8 *t; + howlong--; + t=XBuf+(FSettings.LastSLine-29)*272+32; + if(t>=XBuf) + DrawTextTrans(t,272,errmsg,132); + } +} + +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 +}; + +uint8 fontdata2[2048] = +{ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e,0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e,0x36,0x7f,0x7f,0x7f,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,0x1c,0x3e,0x1c,0x7f,0x7f,0x3e,0x1c,0x3e,0x08,0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x3e,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xf0,0xe0,0xf0,0xbe,0x33,0x33,0x33,0x1e,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0xfc,0xcc,0xfc,0x0c,0x0c,0x0e,0x0f,0x07,0xfe,0xc6,0xfe,0xc6,0xc6,0xe6,0x67,0x03,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,0x01,0x07,0x1f,0x7f,0x1f,0x07,0x01,0x00,0x40,0x70,0x7c,0x7f,0x7c,0x70,0x40,0x00,0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00,0xfe,0xdb,0xdb,0xde,0xd8,0xd8,0xd8,0x00,0x7c,0xc6,0x1c,0x36,0x36,0x1c,0x33,0x1e,0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00,0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x18,0x30,0x7f,0x30,0x18,0x00,0x00,0x00,0x0c,0x06,0x7f,0x06,0x0c,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x7f,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x1e,0x1e,0x0c,0x0c,0x00,0x0c,0x00,0x36,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x7f,0x36,0x7f,0x36,0x36,0x00,0x0c,0x3e,0x03,0x1e,0x30,0x1f,0x0c,0x00,0x00,0x63,0x33,0x18,0x0c,0x66,0x63,0x00,0x1c,0x36,0x1c,0x6e,0x3b,0x33,0x6e,0x00,0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0x06,0x06,0x06,0x0c,0x18,0x00,0x06,0x0c,0x18,0x18,0x18,0x0c,0x06,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,0x3e,0x63,0x73,0x7b,0x6f,0x67,0x3e,0x00,0x0c,0x0e,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x06,0x33,0x3f,0x00,0x1e,0x33,0x30,0x1c,0x30,0x33,0x1e,0x00,0x38,0x3c,0x36,0x33,0x7f,0x30,0x78,0x00,0x3f,0x03,0x1f,0x30,0x30,0x33,0x1e,0x00,0x1c,0x06,0x03,0x1f,0x33,0x33,0x1e,0x00,0x3f,0x33,0x30,0x18,0x0c,0x0c,0x0c,0x00,0x1e,0x33,0x33,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x33,0x3e,0x30,0x18,0x0e,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x06,0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x00,0x00,0x00,0x3f,0x00,0x00,0x3f,0x00,0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x1e,0x33,0x30,0x18,0x0c,0x00,0x0c,0x00, +0x3e,0x63,0x7b,0x7b,0x7b,0x03,0x1e,0x00,0x0c,0x1e,0x33,0x33,0x3f,0x33,0x33,0x00,0x3f,0x66,0x66,0x3e,0x66,0x66,0x3f,0x00,0x3c,0x66,0x03,0x03,0x03,0x66,0x3c,0x00,0x1f,0x36,0x66,0x66,0x66,0x36,0x1f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x46,0x7f,0x00,0x7f,0x46,0x16,0x1e,0x16,0x06,0x0f,0x00,0x3c,0x66,0x03,0x03,0x73,0x66,0x7c,0x00,0x33,0x33,0x33,0x3f,0x33,0x33,0x33,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x78,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x67,0x66,0x36,0x1e,0x36,0x66,0x67,0x00,0x0f,0x06,0x06,0x06,0x46,0x66,0x7f,0x00,0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,0x63,0x67,0x6f,0x7b,0x73,0x63,0x63,0x00,0x1c,0x36,0x63,0x63,0x63,0x36,0x1c,0x00,0x3f,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00,0x1e,0x33,0x33,0x33,0x3b,0x1e,0x38,0x00,0x3f,0x66,0x66,0x3e,0x36,0x66,0x67,0x00,0x1e,0x33,0x07,0x0e,0x38,0x33,0x1e,0x00,0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x3f,0x00,0x33,0x33,0x33,0x33,0x33,0x1e,0x0c,0x00,0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x1e,0x00,0x7f,0x63,0x31,0x18,0x4c,0x66,0x7f,0x00,0x1e,0x06,0x06,0x06,0x06,0x06,0x1e,0x00,0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00,0x1e,0x18,0x18,0x18,0x18,0x18,0x1e,0x00,0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, +0x0c,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0x30,0x3e,0x33,0x6e,0x00,0x07,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00,0x00,0x00,0x1e,0x33,0x03,0x33,0x1e,0x00,0x38,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00,0x00,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x1c,0x36,0x06,0x0f,0x06,0x06,0x0f,0x00,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x1f,0x07,0x06,0x36,0x6e,0x66,0x66,0x67,0x00,0x0c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1e,0x07,0x06,0x66,0x36,0x1e,0x36,0x67,0x00,0x0e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x33,0x7f,0x7f,0x6b,0x63,0x00,0x00,0x00,0x1f,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x1e,0x33,0x33,0x33,0x1e,0x00,0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,0x00,0x00,0x3b,0x6e,0x66,0x06,0x0f,0x00,0x00,0x00,0x3e,0x03,0x1e,0x30,0x1f,0x00,0x08,0x0c,0x3e,0x0c,0x0c,0x2c,0x18,0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,0x00,0x00,0x33,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x3f,0x19,0x0c,0x26,0x3f,0x00,0x38,0x0c,0x0c,0x07,0x0c,0x0c,0x38,0x00,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x07,0x0c,0x0c,0x38,0x0c,0x0c,0x07,0x00,0x6e,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00, +0x1e,0x33,0x03,0x33,0x1e,0x18,0x30,0x1e,0x00,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x38,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x7e,0xc3,0x3c,0x60,0x7c,0x66,0xfc,0x00,0x33,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x07,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x0c,0x0c,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x00,0x00,0x1e,0x03,0x03,0x1e,0x30,0x1c,0x7e,0xc3,0x3c,0x66,0x7e,0x06,0x3c,0x00,0x33,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x07,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x33,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x3e,0x63,0x1c,0x18,0x18,0x18,0x3c,0x00,0x07,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00,0x0c,0x0c,0x00,0x1e,0x33,0x3f,0x33,0x00,0x38,0x00,0x3f,0x06,0x1e,0x06,0x3f,0x00,0x00,0x00,0xfe,0x30,0xfe,0x33,0xfe,0x00,0x7c,0x36,0x33,0x7f,0x33,0x33,0x73,0x00,0x1e,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x33,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x07,0x00,0x1e,0x33,0x33,0x1e,0x00,0x1e,0x33,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x07,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x33,0x00,0x33,0x33,0x3e,0x30,0x1f,0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00,0x33,0x00,0x33,0x33,0x33,0x33,0x1e,0x00,0x18,0x18,0x7e,0x03,0x03,0x7e,0x18,0x18,0x1c,0x36,0x26,0x0f,0x06,0x67,0x3f,0x00,0x33,0x33,0x1e,0x3f,0x0c,0x3f,0x0c,0x0c,0x1f,0x33,0x33,0x5f,0x63,0xf3,0x63,0xe3,0x70,0xd8,0x18,0x3c,0x18,0x18,0x1b,0x0e, +0x38,0x00,0x1e,0x30,0x3e,0x33,0x7e,0x00,0x1c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x38,0x00,0x1e,0x33,0x33,0x1e,0x00,0x00,0x38,0x00,0x33,0x33,0x33,0x7e,0x00,0x00,0x1f,0x00,0x1f,0x33,0x33,0x33,0x00,0x3f,0x00,0x33,0x37,0x3f,0x3b,0x33,0x00,0x3c,0x36,0x36,0x7c,0x00,0x7e,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00,0x0c,0x00,0x0c,0x06,0x03,0x33,0x1e,0x00,0x00,0x00,0x00,0x3f,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x30,0x00,0x00,0xc3,0x63,0x33,0x7b,0xcc,0x66,0x33,0xf0,0xc3,0x63,0x33,0xdb,0xec,0xf6,0xf3,0xc0,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00,0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00,0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xdb,0xee,0xdb,0x77,0xdb,0xee,0xdb,0x77,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0x6f,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x7f,0x6c,0x6c,0x6c,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x6c,0x6c,0x6f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x7f,0x60,0x6f,0x6c,0x6c,0x6c,0x6c,0x6c,0x6f,0x60,0x7f,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0x7f,0x00,0x00,0x00,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x6c,0x6c,0x6c,0x6c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xfc,0x00,0x00,0x00,0x00,0x00,0xfc,0x0c,0xec,0x6c,0x6c,0x6c,0x6c,0x6c,0xef,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xef,0x6c,0x6c,0x6c,0x6c,0x6c,0xec,0x0c,0xec,0x6c,0x6c,0x6c,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0xef,0x00,0xef,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x6c,0x6c,0x6c,0x6c,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xff,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xfc,0x00,0x00,0x00,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0xfc,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0xff,0x6c,0x6c,0x6c,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, +0x00,0x00,0x6e,0x3b,0x13,0x3b,0x6e,0x00,0x00,0x1e,0x33,0x1f,0x33,0x1f,0x03,0x03,0x00,0x3f,0x33,0x03,0x03,0x03,0x03,0x00,0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00,0x3f,0x33,0x06,0x0c,0x06,0x33,0x3f,0x00,0x00,0x00,0x7e,0x1b,0x1b,0x1b,0x0e,0x00,0x00,0x66,0x66,0x66,0x66,0x3e,0x06,0x03,0x00,0x6e,0x3b,0x18,0x18,0x18,0x18,0x00,0x3f,0x0c,0x1e,0x33,0x33,0x1e,0x0c,0x3f,0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00,0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00,0x38,0x0c,0x18,0x3e,0x33,0x33,0x1e,0x00,0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00,0x60,0x30,0x7e,0xdb,0xdb,0x7e,0x06,0x03,0x1c,0x06,0x03,0x1f,0x03,0x06,0x1c,0x00,0x1e,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x3f,0x00,0x3f,0x00,0x3f,0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x3f,0x00,0x06,0x0c,0x18,0x0c,0x06,0x00,0x3f,0x00,0x18,0x0c,0x06,0x0c,0x18,0x00,0x3f,0x00,0x70,0xd8,0xd8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1b,0x1b,0x0e,0x0c,0x0c,0x00,0x3f,0x00,0x0c,0x0c,0x00,0x00,0x6e,0x3b,0x00,0x6e,0x3b,0x00,0x00,0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xf0,0x30,0x30,0x30,0x37,0x36,0x3c,0x38,0x1e,0x36,0x36,0x36,0x36,0x00,0x00,0x00,0x0e,0x18,0x0c,0x06,0x1e,0x00,0x00,0x00,0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static void DrawState(void) +{ + uint8 *XBaf; + int x,y,z; + + XBaf=XBuf+4+(FSettings.LastSLine-44)*272; + + if(XBaf>=XBuf) + for(z=1;z<11;z++) + { + if(SaveStateStatus[z%10]) + { + for(y=0;y<13;y++) + for(x=0;x<21;x++) + XBaf[y*272+x+z*21+z]=sstat[y*21+x+(z-1)*21*12]; + } 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*272+x+z*21+z]=sstat[y*21+x+(z-1)*21*12]; + } + if(CurrentState==z%10) + { + for(x=0;x<21;x++) + XBaf[x+z*21+z*1]=132; + for(x=1;x<12;x++) + { + XBaf[272*x+z*21+z*1]= + XBaf[272*x+z*21+z*1+20]=132; + } + for(x=0;x<21;x++) + XBaf[3264+x+z*21+z*1]=132; + } + } + StateShow--; +} + +void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor) +{ + uint8 length=strlen(textmsg); + uint8 x; + uint8 y; + uint8 z; + + for(x=0;x>z)&1) dest[y*width+(x<<3)+z]=fgcolor; +} + +void DrawBars(void) +{ + uint8 *XBaf; + short which=0; + int x,x2; + + if(controlselect==1) + { + DrawTextTrans(XBuf+128-12+180*272, 272, "Hue", 0x85); + which=ntschue<<1; + } + else if(controlselect==2) + { + DrawTextTrans(XBuf+128-16+180*272, 272, "Tint", 0x85); + which=ntsctint<<1; + } + + XBaf=XBuf+200*272; + for(x=0;x=-6;x2--) + { + XBaf[x-272*x2]=0x85; + } + } + for(;x<256;x+=2) + { + for(x2=2;x2>=-2;x2--) + XBaf[x-272*x2]=0x85; + } + +} diff --git a/driver.h b/driver.h new file mode 100644 index 0000000..9a298c6 --- /dev/null +++ b/driver.h @@ -0,0 +1,174 @@ +#include "types.h" +#include "git.h" + +/* Prototypes for platform interface functions follow: */ + +void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count); + +/* Video interface */ +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b); +void FCEUD_GetPalette(uint8 i,uint8 *r, unsigned char *g, unsigned char *b); + +/* Sound interface */ +void FCEUD_WriteSoundData(int32 *Buffer, int Count); + +/* Displays an error. Can block or not. */ +void FCEUD_PrintError(char *s); + +#ifdef NETWORK +/* Network interface */ + +int FCEUD_NetworkConnect(void); +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block); +int FCEUD_NetworkSendData(uint8 *data, uint32 len); +void FCEUD_NetworkClose(void); + +#endif + +#define DES_NTSCCOL 2 + +#define DES_RESET 0x10 +#define DES_POWER 0x11 + +#define DES_GETNTSCHUE 0x20 +#define DES_GETNTSCTINT 0x21 +#define DES_SETNTSCHUE 0x22 +#define DES_SETNTSCTINT 0x23 + +#define DES_NSFINC 0x50 +#define DES_NSFDEC 0x51 +#define DES_NSFRES 0x52 + +#define DES_VSUNIDIPSET 0x70 +#define DES_VSUNITOGGLEDIPVIEW 0x71 +#define DES_VSUNICOIN 0x72 +#define DES_FDSINSERT 0x80 +#define DES_FDSEJECT 0x81 +#define DES_FDSSELECT 0x82 + +#define DES_NTSCSELHUE 0x90 +#define DES_NTSCSELTINT 0x91 +#define DES_NTSCDEC 0x92 +#define DES_NTSCINC 0x93 + +void DriverInterface(int w, void *d); + +void FCEUI_SetInput(int port, int type, void *ptr, int attrib); +void FCEUI_SetInputFC(int type, void *ptr, int attrib); +void FCEUI_DisableFourScore(int s); + +#include "version.h" + +#define SI_NONE 0 +#define SI_GAMEPAD 1 +#define SI_ZAPPER 2 +#define SI_POWERPAD 3 +#define SI_ARKANOID 4 + +#define SIFC_NONE 0 +#define SIFC_ARKANOID 1 +#define SIFC_SHADOW 2 +#define SIFC_4PLAYER 3 +#define SIFC_FKB 4 +#define SIFC_OEKA 5 + +/* New interface functions */ + +/* 0 to order screen snapshots numerically(0.png), 1 to order them file base-numerically(smb3-0.png). */ +void FCEUI_SetSnapName(int a); + +/* 0 to keep 8-sprites limitation, 1 to remove it */ +void FCEUI_DisableSpriteLimitation(int a); + +/* 0 to save extra game data(*.sav) in same dir as game, 1 to save under base dir */ +void FCEUI_SaveExtraDataUnderBase(int a); + +/* name=path and file to load. returns 0 on failure, 1 on success */ +FCEUGI *FCEUI_LoadGame(char *name); + +/* allocates memory. 0 on failure, 1 on success. */ +int FCEUI_Initialize(void); + +/* begins emulation. Returns after FCEUI_CloseGame() is called */ +void FCEUI_Emulate(void); + +/* Closes currently loaded game, causes FCEUI_Emulate to return */ +void FCEUI_CloseGame(void); + +/* Enable/Disable game genie. a=0 disable, a=1 enable */ +void FCEUI_SetGameGenie(int a); + +/* Set video system a=0 NTSC, a=1 PAL */ +void FCEUI_SetVidSystem(int a); + +/* 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(char *dir); + +/* Tells FCE Ultra to copy the palette data pointed to by pal and use it. + Data pointed to by pal needs to be 64*3 bytes in length. +*/ +void FCEUI_SetPaletteArray(uint8 *pal); + +/* Sets up sound code to render sound at the specified rate, in samples + per second. The sample rate should be as close to 44100 hz as possible. + Sample rates from 8192 through 65535 are ok, though if the sample rate + is too low, some sound channels(noise) won't sound right. + If "Rate" equals 0, sound is disabled. +*/ +void FCEUI_Sound(int Rate); +void FCEUI_SetSoundVolume(uint32 volume); + +#ifdef NETWORK +/* Set network play stuff. type=1 for server, type=2 for client. + skip is only used for server */ +void FCEUI_SetNetworkPlay(int type); +#endif + +void FCEUI_SelectState(int w); +void FCEUI_SaveState(void); +void FCEUI_LoadState(void); +int32 FCEUI_GetDesiredFPS(void); +void FCEUI_SaveSnapshot(void); +void FCEU_DispMessage(char *format, ...); +#define FCEUI_DispMessage FCEU_DispMessage + +int FCEUI_AddCheat(char *name, uint32 addr, uint8 val); +int FCEUI_DelCheat(uint32 which); + +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 FCEUI_CheatSearchBegin(void); +void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2); +void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int s)); + +int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *s); +int FCEUI_SetCheat(uint32 which, char *name, int32 a, int32 v, int s); +void FCEUI_CheatSearchShowExcluded(void); +void FCEUI_CheatSearchSetCurrentAsOriginal(void); + +#define FCEUIOD_STATE 0 +#define FCEUIOD_SNAPS 1 +#define FCEUIOD_NV 2 +#define FCEUIOD_CHEATS 3 +#define FCEUIOD_MISC 4 + +#define FCEUIOD__COUNT 5 + +void FCEUI_SetDirOverride(int which, char *n); + diff --git a/drivers/cli/dface.h b/drivers/cli/dface.h new file mode 100644 index 0000000..92df082 --- /dev/null +++ b/drivers/cli/dface.h @@ -0,0 +1,32 @@ +extern CFGSTRUCT DriverConfig[]; +extern ARGPSTRUCT DriverArgs[]; +extern char *DriverUsage; + +void DoDriverArgs(void); +void GetBaseDirectory(char *BaseDirectory); + +int InitSound(void); +void WriteSound(int32 *Buffer, int Count, int NoWaiting); +void KillSound(void); +void SilenceSound(int s); /* DOS and SDL */ + + +int InitMouse(void); +void KillMouse(void); +void GetMouseData(uint32 *MouseData); + +int InitJoysticks(void); +void KillJoysticks(void); +uint32 *GetJSOr(void); + +int InitKeyboard(void); +int UpdateKeyboard(void); +char *GetKeyboard(void); +void KillKeyboard(void); + +int InitVideo(void); +void KillVideo(void); +void BlitScreen(uint8 *XBuf); +void LockConsole(void); +void UnlockConsole(void); +void ToggleFS(); /* SDL */ diff --git a/drivers/cli/dos-joystick.c b/drivers/cli/dos-joystick.c new file mode 100644 index 0000000..3729187 --- /dev/null +++ b/drivers/cli/dos-joystick.c @@ -0,0 +1,190 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +#include +#include +#include +#include + +#include "dos.h" +#include "dos-joystick.h" + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +int joy=0; +int joyBMap[4]; + +static int32 joybuttons=0; +static uint32 joyx=0; +static uint32 joyy=0; +static uint32 joyxcenter; +static uint32 joyycenter; + +static void ConfigJoystick(void); +volatile int soundjoyer=0; +volatile int soundjoyeron=0; + +/* Crude method to detect joystick. */ +static int DetectJoystick(void) +{ + uint8 b; + + outportb(0x201,0); + b=inportb(0x201); + sleep(1); + if((inportb(0x201)&3)==(b&3)) + return 0; + else + return 1; +} + +void UpdateJoyData(void) +{ + uint32 xc,yc; + + + joybuttons=((inportb(0x201)&0xF0)^0xF0)>>4; + + xc=yc=0; + + { + outportb(0x201,0); + + for(;;) + { + uint8 b; + + b=inportb(0x201); + if(!(b&3)) + break; + if(b&1) xc++; + if(b&2) yc++; + } + } + + joyx=xc; + joyy=yc; +} + +uint32 GetJSOr(void) +{ + int y; + unsigned long ret; + ret=0; + + if(!soundo) + UpdateJoyData(); + for(y=0;y<4;y++) + if(joybuttons&joyBMap[y]) ret|=(1<=joyxcenter*1.75) ret|=JOY_RIGHT<<((joy-1)<<3); + if(joyy<=joyycenter*.25) ret|=JOY_UP<<((joy-1)<<3); + else if(joyy>=joyycenter*1.75) ret|=JOY_DOWN<<((joy-1)<<3); + + return ret; +} + +int InitJoysticks(void) +{ + if(!joy) return(0); + if(!DetectJoystick()) + { + printf("Joystick not detected!\n"); + joy=0; + return 0; + } + if(soundo) + { + soundjoyeron=1; + while(!soundjoyer); + } + else + UpdateJoyData(); + + joyxcenter=joyx; + joyycenter=joyy; + + if(!(joyBMap[0]|joyBMap[1]|joyBMap[2]|joyBMap[3])) + ConfigJoystick(); + return(1); +} + +static void BConfig(int b) +{ + int c=0; + uint32 st=time(0); + + while(time(0)< (st+4) ) + { + if(!soundo) + UpdateJoyData(); + if(joybuttons) c=joybuttons; + else if(c && !joybuttons) + { + joyBMap[b]=c; + break; + } + + } +} + +void KillJoysticks(void) +{ + +} + +static void ConfigJoystick(void) +{ + static char *genb="** Press button for "; + + printf("\n\n Joystick button configuration:\n\n"); + printf(" Push and release the button to map to the virtual joystick.\n"); + printf(" If you do not wish to assign a button, wait a few seconds\n"); + printf(" and the configuration will continue.\n\n"); + printf(" Press enter to continue...\n"); + getchar(); + + printf("%s\"Select\".\n",genb); + BConfig(2); + + printf("%s\"Start\".\n",genb); + BConfig(3); + + printf("%s\"B\".\n",genb); + BConfig(1); + + printf("%s\"A\".\n",genb); + BConfig(0); +} + diff --git a/drivers/cli/dos-joystick.h b/drivers/cli/dos-joystick.h new file mode 100644 index 0000000..6cfaee5 --- /dev/null +++ b/drivers/cli/dos-joystick.h @@ -0,0 +1,27 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void UpdateJoyData(void); +uint32 GetJSOr(void); +int InitJoysticks(void); + +/* Variables to save in config file. */ +extern int joy; +extern int joyBMap[4]; diff --git a/drivers/cli/dos-keyboard.c b/drivers/cli/dos-keyboard.c new file mode 100644 index 0000000..22ca9e8 --- /dev/null +++ b/drivers/cli/dos-keyboard.c @@ -0,0 +1,131 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include "keyscan.h" + +static unsigned char lastsc; +static char keybuf[256]; +int newk; + +/* Read scan code from port $60 */ +/* Acknowledge interrupt( output $20 to port $20) */ + +static void ihandler(_go32_dpmi_registers *r) +{ + unsigned char scode=inp(0x60); /* Get scan code. */ + + + if(scode!=0xE0) + { + int offs=0; + + /* I'm only interested in preserving the independent status of the + right ALT and CONTROL keys. + */ + if(lastsc==0xE0) + if((scode&0x7F)==SCAN_LEFTALT || (scode&0x7F)==SCAN_LEFTCONTROL) + offs=0x80; + + + keybuf[(scode&0x7f)|offs]=((scode&0x80)^0x80); + newk++; + } + lastsc=scode; + + outp(0x20,0x20); /* Acknowledge interrupt. */ +} + +static _go32_dpmi_seginfo KBIBack,KBIBackRM; +static _go32_dpmi_seginfo KBI,KBIRM; +static _go32_dpmi_registers KBIRMRegs; +static int initdone=0; + +int InitKeyboard(void) +{ + /* I'll assume that the keyboard is in the correct scancode mode(translated + mode 2, I think). + */ + newk=0; + memset(keybuf,0,sizeof(keybuf)); + KBIRM.pm_offset=KBI.pm_offset=(int)ihandler; + KBIRM.pm_selector=KBI.pm_selector=_my_cs(); + + _go32_dpmi_get_real_mode_interrupt_vector(9,&KBIBackRM); + _go32_dpmi_allocate_real_mode_callback_iret(&KBIRM, &KBIRMRegs); + _go32_dpmi_set_real_mode_interrupt_vector(9,&KBIRM); + + _go32_dpmi_get_protected_mode_interrupt_vector(9,&KBIBack); + _go32_dpmi_allocate_iret_wrapper(&KBI); + _go32_dpmi_set_protected_mode_interrupt_vector(9,&KBI); + lastsc=0; + initdone=1; + return(1); +} + +void KillKeyboard(void) +{ + if(initdone) + { + _go32_dpmi_set_protected_mode_interrupt_vector(9,&KBIBack); + _go32_dpmi_free_iret_wrapper(&KBI); + + _go32_dpmi_set_real_mode_interrupt_vector(9,&KBIBackRM); + _go32_dpmi_free_real_mode_callback(&KBIRM); + initdone=0; + } +} + +/* In FCE Ultra, it doesn't matter if the key states change + in the middle of the keyboard handling code. If you want + to use this code elsewhere, you may want to memcpy() keybuf + to another buffer and return that when GetKeyboard() is + called. +*/ + +char *GetKeyboard(void) +{ + return keybuf; +} + +/* Returns 1 on new scan codes generated, 0 on no new scan codes. */ +int UpdateKeyboard(void) +{ + int t=newk; + + if(t) + { + asm volatile( + "subl %%eax,_newk\n\t" + : + : "a" (t) + ); + + if(keybuf[SCAN_LEFTCONTROL] && keybuf[SCAN_C]) + raise(SIGINT); + return(1); + } + return(0); +} diff --git a/drivers/cli/dos-mouse.c b/drivers/cli/dos-mouse.c new file mode 100644 index 0000000..ae3e341 --- /dev/null +++ b/drivers/cli/dos-mouse.c @@ -0,0 +1,77 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "dos.h" + +int InitMouse(void) +{ + __dpmi_regs regs; + + memset(®s,0,sizeof(regs)); + regs.x.ax=0; + __dpmi_int(0x33,®s); + if(regs.x.ax!=0xFFFF) + return(0); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x7; + regs.x.cx=0; // Min X + regs.x.dx=260; // Max X + __dpmi_int(0x33,®s); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x8; + regs.x.cx=0; // Min Y + regs.x.dx=260; // Max Y + __dpmi_int(0x33,®s); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0xF; + regs.x.cx=8; // Mickey X + regs.x.dx=8; // Mickey Y + __dpmi_int(0x33,®s); + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x2; + __dpmi_int(0x33,®s); + + return(1); +} + +uint32 GetMouseData(uint32 *x, uint32 *y) +{ + __dpmi_regs regs; + + memset(®s,0,sizeof(regs)); + regs.x.ax=0x3; + __dpmi_int(0x33,®s); + + *x=regs.x.cx; + *y=regs.x.dx; + return(regs.x.bx&3); +} + +void KillMouse(void) +{ + +} diff --git a/drivers/cli/dos-sound.c b/drivers/cli/dos-sound.c new file mode 100644 index 0000000..19ef271 --- /dev/null +++ b/drivers/cli/dos-sound.c @@ -0,0 +1,567 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include +#include + +#include "dos.h" +#include "dos-sound.h" +#include "dos-joystick.h" + + +static void SBIRQHandler(_go32_dpmi_registers *r); +static uint32 LMBuffer; /* Address of low memory DMA playback buffer. */ +static int LMSelector; + +static uint8 *WaveBuffer; +static unsigned int IVector, SBIRQ, SBDMA, SBDMA16, SBPort; +static int DSPV,hsmode; +static int format; +static int frags, fragsize, fragtotal; +static volatile int WritePtr, ReadPtr; +static volatile int hbusy; +static volatile int whichbuf; + + +static uint8 PICMask; +/* Protected mode interrupt vector info. */ +static _go32_dpmi_seginfo SBIH,SBIHOld; + +/* Real mode interrupt vector info. */ +static _go32_dpmi_seginfo SBIHRM,SBIHRMOld; +static _go32_dpmi_registers SBIHRMRegs; + +static int WriteDSP(uint8 V) +{ + int x; + + for(x=65536;x;x--) + { + if(!(inportb(SBPort+0xC)&0x80)) + { + outportb(SBPort+0xC,V); + return(1); + } + } + return(0); +} + +static int ReadDSP(uint8 *V) +{ + int x; + + for(x=65536;x;x--) /* Should be more than enough time... */ + { + if(inportb(SBPort+0xE)&0x80) + { + *V=inportb(SBPort+0xA); + return(1); + } + } + return(0); +} + + +static int SetVectors(void) +{ + SBIH.pm_offset=SBIHRM.pm_offset=(int)SBIRQHandler; + SBIH.pm_selector=SBIHRM.pm_selector=_my_cs(); + + /* Get and set real mode interrupt vector. */ + _go32_dpmi_get_real_mode_interrupt_vector(IVector,&SBIHRMOld); + _go32_dpmi_allocate_real_mode_callback_iret(&SBIHRM, &SBIHRMRegs); + _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRM); + + /* Get and set protected mode interrupt vector. */ + _go32_dpmi_get_protected_mode_interrupt_vector(IVector,&SBIHOld); + _go32_dpmi_allocate_iret_wrapper(&SBIH); + _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIH); + + return(1); +} + +static void ResetVectors(void) +{ + _go32_dpmi_set_protected_mode_interrupt_vector(IVector,&SBIHOld); + _go32_dpmi_free_iret_wrapper(&SBIH); + _go32_dpmi_set_real_mode_interrupt_vector(IVector,&SBIHRMOld); + _go32_dpmi_free_real_mode_callback(&SBIHRM); +} + +int GetBLASTER(void) +{ + int check=0; + char *s; + + if(!(s=getenv("BLASTER"))) + { + puts(" Error getting BLASTER environment variable."); + return(0); + } + + while(*s) + { + switch(toupper(*s)) + { + case 'A': check|=(sscanf(s+1,"%x",&SBPort)==1)?1:0;break; + case 'I': check|=(sscanf(s+1,"%d",&SBIRQ)==1)?2:0;break; + case 'D': check|=(sscanf(s+1,"%d",&SBDMA)==1)?4:0;break; + case 'H': check|=(sscanf(s+1,"%d",&SBDMA16)==1)?8:0;break; + } + s++; + } + + if((check^7)&7 || SBDMA>=4 || (SBDMA16<=4 && check&8) || SBIRQ>15) + { + puts(" Invalid or incomplete BLASTER environment variable."); + return(0); + } + if(!(check&8)) + format=0; + return(1); +} + +static int ResetDSP(void) +{ + uint8 b; + + outportb(SBPort+0x6,0x1); + delay(10); + outportb(SBPort+0x6,0x0); + delay(10); + + if(ReadDSP(&b)) + if(b==0xAA) + return(1); + return(0); +} + +static int GetDSPVersion(void) +{ + int ret; + uint8 t; + + if(!WriteDSP(0xE1)) + return(0); + if(!ReadDSP(&t)) + return(0); + ret=t<<8; + if(!ReadDSP(&t)) + return(0); + ret|=t; + + return(ret); +} + +static void KillDMABuffer(void) +{ + __dpmi_free_dos_memory(LMSelector); +} + +static int MakeDMABuffer(void) +{ + uint32 size; + int32 tmp; + + size=fragsize*2; /* Two buffers in the DMA buffer. */ + size<<=format; /* Twice the size for 16-bit than for 8-bit. */ + + size<<=1; /* Double the size in case the first 2 buffers + cross a 64KB or 128KB page boundary. + */ + size=(size+15)>>4; /* Convert to paragraphs */ + + if((tmp=__dpmi_allocate_dos_memory(size,&LMSelector))<0) + return(0); + + LMBuffer=tmp<<=4; + + if(format) /* Check for and fix 128KB page boundary crossing. */ + { + if((LMBuffer&0x20000) != ((LMBuffer+fragsize*2*2-1)&0x20000)) + LMBuffer+=fragsize*2*2; + } + else /* Check for and fix 64KB page boundary crossing. */ + { + if((LMBuffer&0x10000) != ((LMBuffer+fragsize*2-1)&0x10000)) + LMBuffer+=fragsize*2; + } + + DOSMemSet(LMBuffer, format?0:128, (fragsize*2)<>8); + + /* Page of buffer. */ + outportb(PPorts[format?SBDMA16:SBDMA],LMBuffer>>16); + + /* Offset of buffer within page. */ + if(format) + tmp=((SBDMA16&3)<<2)+0xc0; + else + tmp=SBDMA<<1; + + outportb(tmp,(LMBuffer>>format)); + outportb(tmp,(LMBuffer>>(8+format))); +} + +int InitSB(int Rate, int bittage) +{ + hsmode=hbusy=0; + whichbuf=1; + puts("Initializing Sound Blaster..."); + + format=bittage?1:0; + frags=8; + + if(Rate<=11025) + fragsize=1<<5; + else if(Rate<=22050) + fragsize=1<<6; + else + fragsize=1<<7; + + fragtotal=frags*fragsize; + WaveBuffer=malloc(fragtotal<65535)) + { + printf(" Unsupported playback rate: %d samples per second\n",Rate); + return(0); + } + + if(!GetBLASTER()) + return(0); + + /* Disable IRQ line in PIC0 or PIC1 */ + if(SBIRQ>7) + { + PICMask=inportb(0xA1); + outportb(0xA1,PICMask|(1<<(SBIRQ&7))); + } + else + { + PICMask=inportb(0x21); + outportb(0x21,PICMask|(1<>8,DSPV&0xFF); + if(DSPV<0x201) + { + printf(" DSP version number is too low.\n"); + return(0); + } + + if(DSPV<0x400) + format=0; + if(!MakeDMABuffer()) + { + puts(" Error creating low-memory DMA buffer."); + return(0); + } + + if(SBIRQ>7) IVector=SBIRQ+0x68; + else IVector=SBIRQ+0x8; + + if(!SetVectors()) + { + puts(" Error setting interrupt vectors."); + KillDMABuffer(); + return(0); + } + + /* Reenable IRQ line. */ + if(SBIRQ>7) + outportb(0xA1,PICMask&(~(1<<(SBIRQ&7)))); + else + outportb(0x21,PICMask&(~(1<=0x400) + { + WriteDSP(0x41); // Set sampling rate + WriteDSP(Rate>>8); // High byte + WriteDSP(Rate&0xFF); // Low byte + if(!format) + { + WriteDSP(0xC6); // 8-bit output + WriteDSP(0x00); // 8-bit mono unsigned PCM + } + else + { + WriteDSP(0xB6); // 16-bit output + WriteDSP(0x10); // 16-bit mono signed PCM + } + WriteDSP((fragsize-1)&0xFF);// Low byte of size + WriteDSP((fragsize-1)>>8); // High byte of size + } + else + { + int tc,command; + if(Rate>22050) + { + tc=(65536-(256000000/Rate))>>8; + Rate=256000000/(65536-(tc<<8)); + command=0x90; // High-speed auto-initialize DMA mode transfer + hsmode=1; + } + else + { + tc=256-(1000000/Rate); + Rate=1000000/(256-tc); + command=0x1c; // Auto-initialize DMA mode transfer + } + WriteDSP(0x40); // Set DSP time constant + WriteDSP(tc); // time constant + WriteDSP(0x48); // Set DSP block transfer size + WriteDSP((fragsize-1)&0xFF); + WriteDSP((fragsize-1)>>8); + + WriteDSP(command); + } + + /* Enable DMA */ + if(format) + outportb(0xd4,SBDMA16&3); + else + outportb(0xa,SBDMA); + + printf(" %d hz, %d-bit\n",Rate,8<=8) + outportb(0xA0,0x20); + whichbuf^=1; + return; + } + hbusy=1; + + { + /* This code seems to fail on many SB emulators. Bah. + SCREW SB EMULATORS. ^_^ */ + uint32 count; + uint32 block; + uint32 port; + + if(format) + port=((SBDMA16&3)*4)+0xc2; + else + port=(SBDMA*2)+1; + + count=inportb(port); + count|=inportb(port)<<8; + + if(count>=fragsize) + block=1; + else + block=0; + dest=LMBuffer+((block*fragsize)<>2;x;x--,dest+=4) + { + _farnspokel(dest,sby); + } + } + else + { + for(x=(fragsize<>2;x;x--,dest+=4,src++) + { + _farnspokel(dest,*src); + } + ReadPtr=(ReadPtr+fragsize)&(fragtotal-1); + } + + if(soundjoyeron) + { + static int coot=0; + if(!coot) + { + UpdateJoyData(); + soundjoyer=1; + } + coot=(coot+1)&3; + } + hbusy=0; + outportb(0x20,0x20); + if(SBIRQ>=8) + outportb(0xA0,0x20); +} + +void SilenceSound(int s) +{ + ssilence=s; +} + +void WriteSBSound(int32 *Buffer, int Count, int NoBlocking) +{ + int x; + + if(!format) + { + for(x=0;x>8)^128; + WritePtr=(WritePtr+1)&(fragtotal-1); + } + } + else // 16 bit + { + for(x=0;x7)?0xA1:0x21,PICMask|(1<<(SBIRQ&7))); + ResetVectors(); + outportb((SBIRQ>7)?0xA1:0x21,PICMask); + KillDMABuffer(); +} diff --git a/drivers/cli/dos-sound.h b/drivers/cli/dos-sound.h new file mode 100644 index 0000000..7f5185a --- /dev/null +++ b/drivers/cli/dos-sound.h @@ -0,0 +1,26 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int InitSB(int Rate, int bittage); +void KillSB(void); + +void WriteSBSound(int32 *Buffer, int Count, int NoBlocking); +void SilenceSound(int s); + diff --git a/drivers/cli/dos-video.c b/drivers/cli/dos-video.c new file mode 100644 index 0000000..574c824 --- /dev/null +++ b/drivers/cli/dos-video.c @@ -0,0 +1,246 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 \Firebug\ + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include + +#include "dos.h" +#include "dos-video.h" + +#define TEXT 3 +#define G320x200x256 0x13 + +static void vga_waitretrace(void) +{ + while(inp(0x3da)&0x8); + while(!(inp(0x3da)&0x8)); +} + +static void vga_setmode(int mode) +{ + __dpmi_regs regs; + + memset(®s,0,sizeof(regs)); + regs.x.ax=mode; + + __dpmi_int(0x10,®s); +} + +void vga_setpalette(int i, int r, int g, int b) +{ + outp(0x3c8,i); + outp(0x3c9,r); + outp(0x3c9,g); + outp(0x3c9,b); +} + +int FCEUDvmode=1; + +static int vidready=0; + +/* Part of the VGA low-level mass register setting code derived from + code by \Firebug\. +*/ + +#include "vgatweak.c" + +void SetBorder(void) +{ + inportb(0x3da); + outportb(0x3c0,(0x11|0x20)); + outportb(0x3c0,0x80); +} + +void TweakVGA(int VGAMode) +{ + int I; + + vga_waitretrace(); + + outportb(0x3C8,0x00); + for(I=0;I<768;I++) outportb(0x3C9,0x00); + + outportb(0x3D4,0x11); + I=inportb(0x3D5)&0x7F; + outportb(0x3D4,0x11); + outportb(0x3D5,I); + + switch(VGAMode) + { + case 1: for(I=0;I<25;I++) VGAPortSet(v256x240[I]);break; + case 2: for(I=0;I<25;I++) VGAPortSet(v256x256[I]);break; + case 3: for(I=0;I<25;I++) VGAPortSet(v256x256S[I]);break; + case 6: for(I=0;I<25;I++) VGAPortSet(v256x224S[I]);break; + case 8: for(I=0;I<25;I++) VGAPortSet(v256x224_103[I]);break; + default: break; + } + + outportb(0x3da,0); +} + + +static uint8 palettedbr[256],palettedbg[256],palettedbb[256]; + +static void FlushPalette(void) +{ + int x; + for(x=0;x<256;x++) + { + int z=x; + vga_setpalette(z,palettedbr[x]>>2,palettedbg[x]>>2,palettedbb[x]>>2); + } +} + +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b) +{ + palettedbr[index]=r; + palettedbg[index]=g; + palettedbb[index]=b; + if(vidready) + { + vga_setpalette(index,r>>2,g>>2,b>>2); + } +} + + +void FCEUD_GetPalette(uint8 i, uint8 *r, uint8 *g, uint8 *b) +{ + *r=palettedbr[i]; + *g=palettedbg[i]; + *b=palettedbb[i]; +} + +static uint32 ScreenLoc; + +int InitVideo(void) +{ + vidready=0; + switch(FCEUDvmode) + { + default: + case 1: + case 2: + case 3: + case 6: + case 8: + vga_setmode(G320x200x256); + vidready|=1; + ScreenLoc=0xa0000; + TweakVGA(FCEUDvmode); + SetBorder(); + DOSMemSet(ScreenLoc, 128, 256*256); + break; + } + vidready|=2; + FlushPalette(); + return 1; +} + +void KillVideo(void) +{ + if(vidready) + { + vga_setmode(TEXT); + vidready=0; + } +} +void LockConsole(void){} +void UnlockConsole(void){} +void BlitScreen(uint8 *XBuf) +{ + uint32 dest; + int tlines; + + if(eoptions&4 && !NoWaiting) + vga_waitretrace(); + + tlines=erendline-srendline+1; + + dest=ScreenLoc; + + switch(FCEUDvmode) + { + case 1:dest+=(((240-tlines)>>1)<<8);break; + case 2: + case 3:dest+=(((256-tlines)>>1)<<8);break; + case 4: + case 5:dest+=(((240-tlines)>>1)*640+((640-512)>>1));break; + case 8: + case 6:if(tlines>224) tlines=224;dest+=(((224-tlines)>>1)<<8);break; + } + + XBuf+=(srendline<<8)+(srendline<<4); + + _farsetsel(_dos_ds); + if(eoptions&DO_CLIPSIDES) + { + asm volatile( + "agoop1:\n\t" + "movl $30,%%eax\n\t" + "agoop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + ".byte 0x64 \n\t" + "movl %%edx,(%%edi)\n\t" + ".byte 0x64 \n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne agoop2\n\t" + "addl $32,%%esi\n\t" + "addl $16,%%edi\n\t" + "decb %%bl\n\t" + "jne agoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + else + { + asm volatile( + "goop1:\n\t" + "movl $32,%%eax\n\t" + "goop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + ".byte 0x64 \n\t" + "movl %%edx,(%%edi)\n\t" + ".byte 0x64 \n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne goop2\n\t" + "addl $16,%%esi\n\t" + "decb %%bl\n\t" + "jne goop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } +} + + diff --git a/drivers/cli/dos-video.h b/drivers/cli/dos-video.h new file mode 100644 index 0000000..ee09b51 --- /dev/null +++ b/drivers/cli/dos-video.h @@ -0,0 +1,22 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern int FCEUDvmode; + diff --git a/drivers/cli/dos.c b/drivers/cli/dos.c new file mode 100644 index 0000000..8ba1578 --- /dev/null +++ b/drivers/cli/dos.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include + +#include "dos.h" +#include "dos-joystick.h" +#include "dos-video.h" +#include "dos-sound.h" +#include "../common/args.h" +#include "../common/config.h" + +/* _CRT0_FLAG_LOCK_MEMORY might not always result in all memory being locked. + Bummer. I'll add code to explicitly lock the data touched by the sound + interrupt handler(and the handler itself), if necessary(though that might + be tricky...). I'll also to cover the data the keyboard + interrupt handler touches. +*/ + +int _crt0_startup_flags = _CRT0_FLAG_FILL_SBRK_MEMORY | _CRT0_FLAG_LOCK_MEMORY | _CRT0_FLAG_USE_DOS_SLASHES; + +static int f8bit=0; +int soundo=44100; +int doptions=0; + + +CFGSTRUCT DriverConfig[]={ + NAC("sound",soundo), + AC(doptions), + AC(f8bit), + AC(FCEUDvmode), + NACA("joybmap",joyBMap), + AC(joy), + ENDCFGSTRUCT +}; + +char *DriverUsage= +"-vmode x Select video mode(all are 8 bpp).\n\ + 1 = 256x240 6 = 256x224(with scanlines)\n\ + 2 = 256x256 8 = 256x224\n\ + 3 = 256x256(with scanlines)\n\ +-vsync x Wait for the screen's vertical retrace before updating the\n\ + screen. Refer to the documentation for caveats.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-sound x Sound.\n\ + 0 = Disabled.\n\ + Otherwise, x = playback rate.\n\ +-f8bit x Force 8-bit sound.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-joy x Joystick mapped to virtual joystick x(1-4).\n\ + 0 = Disabled, reset configuration.\n\ + 1 = Enabled."; + +ARGPSTRUCT DriverArgs[]={ + {"-vmode",0,&FCEUDvmode,0}, + {"-sound",0,&soundo,0}, + {"-f8bit",0,&f8bit,0}, + {"-joy",0,&joy,0}, + {"-vsync",0,&eoptions,0x8004}, + {0,0,0,0} +}; + +void DoDriverArgs(void) +{ + if(!joy) memset(joyBMap,0,4); +} + +int InitSound(void) +{ + if(soundo) + { + if(soundo==1) + soundo=44100; + soundo=InitSB(soundo,f8bit?0:1); + FCEUI_Sound(soundo); + } + return(soundo?1:0); +} + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + WriteSBSound(Buffer,Count,NoWaiting); +} + +void KillSound(void) +{ + if(soundo) + KillSB(); +} + +void DOSMemSet(uint32 A, uint8 V, uint32 count) +{ + uint32 x; + + _farsetsel(_dos_ds); + for(x=0;x=0;x--) + { + if(arg0[x]=='/' || arg0[x]=='\\') + { + strncpy(BaseDirectory,arg0,x); + break; + } + } + + BaseDirectory[x]=0; +} + +int main(int argc, char *argv[]) +{ + puts("\nStarting FCE Ultra "VERSION_STRING"...\n"); + arg0=argv[0]; + return(CLImain(argc,argv)); +} + diff --git a/drivers/cli/dos.h b/drivers/cli/dos.h new file mode 100644 index 0000000..05bc8e5 --- /dev/null +++ b/drivers/cli/dos.h @@ -0,0 +1,28 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "../../driver.h" +#include "main.h" + +extern int eoptions; +extern int soundo; +void DOSMemSet(uint32 A, uint8 V, uint32 count); +#define DO_CLIPSIDES 1 + diff --git a/drivers/cli/input.c b/drivers/cli/input.c new file mode 100644 index 0000000..b946c5b --- /dev/null +++ b/drivers/cli/input.c @@ -0,0 +1,340 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +static void UpdateFKB(void); + +/* UsrInputType[] is user-specified. InputType[] is current + (game loading can override user settings) +*/ +static int UsrInputType[2]={SI_GAMEPAD,SI_GAMEPAD}; +static int InputType[2]; + +static int UsrInputTypeFC={SI_NONE}; +static int InputTypeFC; + +static uint32 JSreturn; +int NoWaiting=0; + +static void DoCheatSeq(void) +{ + #if defined(DOS) || defined(SDL) + if(inited&1) + SilenceSound(1); + #endif + KillKeyboard(); + KillVideo(); + + DoConsoleCheatConfig(); + InitVideo(); + InitKeyboard(); + #if defined(DOS) || defined(SDL) + if(inited&1) + SilenceSound(0); + #endif +} + +#include "keyscan.h" +static char *keys; +static int DIPS=0; +static uint8 keyonce[MK_COUNT]; +#define KEY(__a) keys[MK(__a)] +#define keyonly(__a,__z) {if(KEY(__a)){if(!keyonce[MK(__a)]) {keyonce[MK(__a)]=1;__z}}else{keyonce[MK(__a)]=0;}} +static int JoySwap=0; +static int cidisabled=0; +static int KeyboardUpdate(void) +{ + + if(!UpdateKeyboard()) + if(keys) + return 0; + + keys=GetKeyboard(); + + if(InputTypeFC==SIFC_FKB) + { + keyonly(SCROLLLOCK,cidisabled^=1; + FCEUI_DispMessage("Family Keyboard %sabled.",cidisabled?"en":"dis");) + #ifdef SDL + SDL_WM_GrabInput(cidisabled?SDL_GRAB_ON:SDL_GRAB_OFF); + #endif + if(cidisabled) return(1); + } + #ifdef SVGALIB + keyonly(F3,LockConsole();) + keyonly(F4,UnlockConsole();) + #elif SDL + keyonly(F4,ToggleFS();) + #endif + NoWaiting&=~1; + if(KEY(GRAVE)) + NoWaiting|=1; + + if(gametype==GIT_FDS) + { + keyonly(S,DriverInterface(DES_FDSSELECT,0);) + keyonly(I,DriverInterface(DES_FDSINSERT,0);) + keyonly(E,DriverInterface(DES_FDSEJECT,0);) + } + + keyonly(F9,FCEUI_SaveSnapshot();) + if(gametype!=GIT_NSF) + { + keyonly(F2,DoCheatSeq();) + keyonly(F5,FCEUI_SaveState();) + keyonly(F7,FCEUI_LoadState();) + } + else + { + keyonly(CURSORLEFT,DriverInterface(DES_NSFDEC,0);) + keyonly(CURSORRIGHT,DriverInterface(DES_NSFINC,0);) + if( KEY(ENTER)) DriverInterface(DES_NSFRES,0); + if( KEY(CURSORUP)) DriverInterface(DES_NSFINC,0); + if( KEY(CURSORDOWN)) DriverInterface(DES_NSFDEC,0); + } + + keyonly(F10,DriverInterface(DES_RESET,0);) + keyonly(F11,DriverInterface(DES_POWER,0);) + if(KEY(F12) || KEY(ESCAPE)) FCEUI_CloseGame(); + + if(gametype==GIT_VSUNI) + { + keyonly(C,DriverInterface(DES_VSUNICOIN,0);) + keyonly(V,DIPS^=1;DriverInterface(DES_VSUNITOGGLEDIPVIEW,0);) + if(!(DIPS&1)) goto DIPSless; + keyonly(1,DriverInterface(DES_VSUNIDIPSET,(void *)1);) + keyonly(2,DriverInterface(DES_VSUNIDIPSET,(void *)2);) + keyonly(3,DriverInterface(DES_VSUNIDIPSET,(void *)3);) + keyonly(4,DriverInterface(DES_VSUNIDIPSET,(void *)4);) + keyonly(5,DriverInterface(DES_VSUNIDIPSET,(void *)5);) + keyonly(6,DriverInterface(DES_VSUNIDIPSET,(void *)6);) + keyonly(7,DriverInterface(DES_VSUNIDIPSET,(void *)7);) + keyonly(8,DriverInterface(DES_VSUNIDIPSET,(void *)8);) + } + else + { + keyonly(H,DriverInterface(DES_NTSCSELHUE,0);) + keyonly(T,DriverInterface(DES_NTSCSELTINT,0);) + if(KEY(KP_MINUS) || KEY(MINUS)) DriverInterface(DES_NTSCDEC,0); + if(KEY(KP_PLUS) || KEY(EQUAL)) DriverInterface(DES_NTSCINC,0); + + DIPSless: + keyonly(0,FCEUI_SelectState(0);) + keyonly(1,FCEUI_SelectState(1);) + keyonly(2,FCEUI_SelectState(2);) + keyonly(3,FCEUI_SelectState(3);) + keyonly(4,FCEUI_SelectState(4);) + keyonly(5,FCEUI_SelectState(5);) + keyonly(6,FCEUI_SelectState(6);) + keyonly(7,FCEUI_SelectState(7);) + keyonly(8,FCEUI_SelectState(8);) + keyonly(9,FCEUI_SelectState(9);) + } + return 1; +} + +static uint32 KeyboardDodo(void) +{ + uint32 JS=0; + + if(gametype!=GIT_NSF) + { + int x,y; + x=y=0; + keyonly(CAPSLOCK, + { + char tmp[64]; + JoySwap=(JoySwap+8)%32; + sprintf(tmp,"Joystick %d selected.",(JoySwap>>3)+1); + FCEUI_DispMessage(tmp); + }) + + if(KEY(LEFTALT) || KEY(X)) JS|=JOY_A<>8)|((JS&0xFF00)<<8); + } + if(t&2) + GetMouseData(MouseData); +} + +static void InitOtherInput(void) +{ + void *InputDPtr; + + int t; + int x; + int attrib; + + for(t=0,x=0;x<2;x++) + { + attrib=0; + InputDPtr=0; + switch(InputType[x]) + { + case SI_POWERPAD:InputDPtr=&powerpadbuf[x];break; + case SI_GAMEPAD:InputDPtr=((uint8 *)&JSreturn)+(x<<1);break; + case SI_ARKANOID:InputDPtr=MouseData;t|=1;break; + case SI_ZAPPER:InputDPtr=MouseData; + t|=1; + attrib=1; + break; + } + FCEUI_SetInput(x,InputType[x],InputDPtr,attrib); + } + + attrib=0; + InputDPtr=0; + switch(InputTypeFC) + { + case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=1;break; + case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break; + case SIFC_FKB:InputDPtr=fkbkeys;break; + } + + FCEUI_SetInputFC(InputTypeFC,InputDPtr,attrib); + FCEUI_DisableFourScore(eoptions&EO_NOFOURSCORE); + + if(t && !(inited&16)) + { + InitMouse(); + inited|=16; + } +} + +int fkbmap[0x48]= +{ + MK(F1),MK(F2),MK(F3),MK(F4),MK(F5),MK(F6),MK(F7),MK(F8), + MK(1),MK(2),MK(3),MK(4),MK(5),MK(6),MK(7),MK(8),MK(9),MK(0), + MK(MINUS),MK(EQUAL),MK(BACKSLASH),MK(BACKSPACE), + MK(ESCAPE),MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I),MK(O), + MK(P),MK(GRAVE),MK(BRACKET_LEFT),MK(ENTER), + MK(LEFTCONTROL),MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K), + MK(L),MK(SEMICOLON),MK(APOSTROPHE),MK(BRACKET_RIGHT),MK(INSERT), + MK(LEFTSHIFT),MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M),MK(COMMA), + MK(PERIOD),MK(SLASH),MK(RIGHTALT),MK(RIGHTSHIFT),MK(LEFTALT),MK(SPACE), + MK(DELETE),MK(END),MK(PAGEDOWN), + MK(CURSORUP),MK(CURSORLEFT),MK(CURSORRIGHT),MK(CURSORDOWN) +}; + +static void UpdateFKB(void) +{ + int x; + + for(x=0;x<0x48;x++) + { + fkbkeys[x]=0; + if(keys[fkbmap[x]]) + fkbkeys[x]=1; + } +} diff --git a/drivers/cli/keyscan.h b/drivers/cli/keyscan.h new file mode 100644 index 0000000..af82127 --- /dev/null +++ b/drivers/cli/keyscan.h @@ -0,0 +1,166 @@ +#ifdef SVGALIB + +#include +#define SCANCODE_DELETE SCANCODE_REMOVE +#define SCANCODE_KP_MINUS SCANCODE_KEYPADMINUS +#define SCANCODE_KP_PLUS SCANCODE_KEYPADPLUS +#define MK(k) SCANCODE_##k +#define MK_COUNT 256 +#elif SDL +#include +#define SDLK_A SDLK_a +#define SDLK_B SDLK_b +#define SDLK_C SDLK_c +#define SDLK_D SDLK_d +#define SDLK_E SDLK_e +#define SDLK_F SDLK_f +#define SDLK_G SDLK_g +#define SDLK_H SDLK_h +#define SDLK_I SDLK_i +#define SDLK_J SDLK_j +#define SDLK_K SDLK_k +#define SDLK_L SDLK_l +#define SDLK_M SDLK_m +#define SDLK_N SDLK_n +#define SDLK_O SDLK_o +#define SDLK_P SDLK_p +#define SDLK_Q SDLK_q +#define SDLK_R SDLK_r +#define SDLK_S SDLK_s +#define SDLK_T SDLK_t +#define SDLK_U SDLK_u +#define SDLK_V SDLK_v +#define SDLK_W SDLK_w +#define SDLK_X SDLK_x +#define SDLK_Y SDLK_y +#define SDLK_Z SDLK_z +#define SDLK_LEFTCONTROL SDLK_LCTRL +#define SDLK_RIGHTCONTROL SDLK_RCTRL +#define SDLK_LEFTALT SDLK_LALT +#define SDLK_RIGHTALT SDLK_RALT +#define SDLK_LEFTSHIFT SDLK_LSHIFT +#define SDLK_RIGHTSHIFT SDLK_RSHIFT +#define SDLK_CURSORDOWN SDLK_DOWN +#define SDLK_CURSORUP SDLK_UP +#define SDLK_CURSORLEFT SDLK_LEFT +#define SDLK_CURSORRIGHT SDLK_RIGHT +#define SDLK_ENTER SDLK_RETURN +#define SDLK_EQUAL SDLK_EQUALS +#define SDLK_APOSTROPHE SDLK_QUOTE +#define SDLK_BRACKET_LEFT SDLK_LEFTBRACKET +#define SDLK_BRACKET_RIGHT SDLK_RIGHTBRACKET +#define SDLK_SCROLLLOCK SDLK_SCROLLOCK /* I guess the SDL people don't like lots of Ls... */ +#define SDLK_GRAVE SDLK_BACKQUOTE +#define MK(k) SDLK_##k +#define MK_COUNT (SDLK_LAST+1) +#elif DOS + +#define SCAN_GRAVE 0x29 +#define SCAN_1 0x02 +#define SCAN_2 0x03 +#define SCAN_3 0x04 +#define SCAN_4 0x05 +#define SCAN_5 0x06 +#define SCAN_6 0x07 +#define SCAN_7 0x08 +#define SCAN_8 0x09 +#define SCAN_9 0x0A +#define SCAN_0 0x0B +#define SCAN_MINUS 0x0C +#define SCAN_EQUAL 0x0D +#define SCAN_BACKSLASH 0x2B +#define SCAN_BACKSPACE 0x0E +#define SCAN_TAB 0x0F +#define SCAN_Q 0x10 +#define SCAN_W 0x11 +#define SCAN_E 0x12 +#define SCAN_R 0x13 +#define SCAN_T 0x14 +#define SCAN_Y 0x15 +#define SCAN_U 0x16 +#define SCAN_I 0x17 +#define SCAN_O 0x18 +#define SCAN_P 0x19 +#define SCAN_BRACKET_LEFT 0x1A +#define SCAN_BRACKET_RIGHT 0x1B +#define SCAN_LOWBACKSLASH 0x2B +#define SCAN_CAPSLOCK 0x3A +#define SCAN_A 0x1E +#define SCAN_S 0x1F +#define SCAN_D 0x20 +#define SCAN_F 0x21 +#define SCAN_G 0x22 +#define SCAN_H 0x23 +#define SCAN_J 0x24 +#define SCAN_K 0x25 +#define SCAN_L 0x26 +#define SCAN_SEMICOLON 0x27 +#define SCAN_APOSTROPHE 0x28 +#define SCAN_ENTER 0x1C +#define SCAN_LEFTSHIFT 0x2A +#define SCAN_Z 0x2C +#define SCAN_X 0x2D +#define SCAN_C 0x2E +#define SCAN_V 0x2F +#define SCAN_B 0x30 +#define SCAN_N 0x31 +#define SCAN_M 0x32 +#define SCAN_COMMA 0x33 +#define SCAN_PERIOD 0x34 +#define SCAN_SLASH 0x35 +#define SCAN_RIGHTSHIFT 0x36 +#define SCAN_LEFTCONTROL 0x1D +#define SCAN_LEFTALT 0x38 +#define SCAN_SPACE 0x39 + +/* Extended keys. */ +#define SCAN_RIGHTALT (0x38|0x80) +#define SCAN_RIGHTCONTROL (0x1D|0x80) +#define SCAN_BL_INSERT (0x52|0x80) +#define SCAN_BL_DELETE (0x53|0x80) +#define SCAN_BL_CURSORLEFT (0x4B|0x80) +#define SCAN_BL_HOME (0x47|0x80) +#define SCAN_BL_END (0x4F|0x80) +#define SCAN_BL_CURSORUP (0x48|0x80) +#define SCAN_BL_CURSORDOWN (0x50|0x80) +#define SCAN_BL_PAGEUP (0x49|0x80) +#define SCAN_BL_PAGEDOWN (0x51|0x80) +#define SCAN_BL_CURSORRIGHT (0x4D|0x80) + +#define SCAN_SCROLLLOCK 0x46 +/* Keys in the key pad area. */ +#define SCAN_NUMLOCK 0x45 +#define SCAN_HOME 0x47 +#define SCAN_CURSORLEFT 0x4B +#define SCAN_END 0x4F +#define SCAN_SLASH 0x35 +#define SCAN_CURSORUP 0x48 +#define SCAN_CENTER 0x4C +#define SCAN_CURSORDOWN 0x50 +#define SCAN_INSERT 0x52 +#define SCAN_ASTERISK 0x37 +#define SCAN_PAGEUP 0x49 +#define SCAN_CURSORRIGHT 0x4D +#define SCAN_PAGEDOWN 0x51 +#define SCAN_DELETE 0x53 +#define SCAN_KP_MINUS 0x4A +#define SCAN_KP_PLUS 0x4E +#define SCAN_KP_ENTER 0x1C + +#define SCAN_ESCAPE 0x01 +#define SCAN_F1 0x3B +#define SCAN_F2 0x3C +#define SCAN_F3 0x3D +#define SCAN_F4 0x3E +#define SCAN_F5 0x3F +#define SCAN_F6 0x40 +#define SCAN_F7 0x41 +#define SCAN_F8 0x42 +#define SCAN_F9 0x43 +#define SCAN_F10 0x44 +#define SCAN_F11 0x57 +#define SCAN_F12 0x58 + +#define MK_COUNT 256 +#define MK(k) SCAN_##k +#endif diff --git a/drivers/cli/lnx-joystick.c b/drivers/cli/lnx-joystick.c new file mode 100644 index 0000000..4c2ae6a --- /dev/null +++ b/drivers/cli/lnx-joystick.c @@ -0,0 +1,201 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include "main.h" +#include "lnx-joystick.h" + +int joy[4]={0,0,0,0}; +int joyBMap[4][4]; + +static int32 joybuttons[4]={0,0,0,0}; +static int32 joyx[4]={0,0,0,0}; +static int32 joyy[4]={0,0,0,0}; + +static void ConfigJoystick(int z); +static int fd[4]; + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +static void UpdateJoyData(int x) +{ + struct js_event e; + + while(read(fd[x],&e,sizeof(struct js_event))==sizeof(struct js_event)) + { + e.type&=~JS_EVENT_INIT; + if(e.type==JS_EVENT_BUTTON) + { + if(e.value) + joybuttons[x]|=(1<=16383) ret|=JOY_RIGHT<<(x<<3); + if(joyy[x]<=-16383) ret|=JOY_UP<<(x<<3); + else if(joyy[x]>=16383) ret|=JOY_DOWN<<(x<<3); + } + return ret; +} + +void KillJoysticks(void) +{ + int x; + for(x=0;x<4;x++) + if(joy[x]) + close(fd[x]); +} + +int InitJoysticks(void) +{ + char dbuf[64]; + int version; + int z; + + for(z=0;z<4;z++) + { + if(!joy[z]) continue; + sprintf(dbuf,"/dev/js%d",joy[z]-1); + if((fd[z]=open(dbuf,O_RDONLY|O_NONBLOCK))<0) + { + printf("Could not open %s.\n",dbuf); + joy[z]=0; + continue; + } + + if(ioctl(fd[z], JSIOCGVERSION, &version)==-1) + { + printf("Error using ioctl JSIOCGVERSION on %s.\n",dbuf); + joy[z]=0; + close(fd[z]); + continue; + } + + if(!(joyBMap[z][0]|joyBMap[z][1]|joyBMap[z][2]|joyBMap[z][3])) + ConfigJoystick(z); + } + + return(joy[0]|joy[1]|joy[2]|joy[3]); +} + +#define WNOINPUT(); for(;;){uint8 t; if(read(fileno(stdin),&t,1)==-1) \ + {break;}} + +static void BConfig(int z,int b) +{ + WNOINPUT(); + for(;;) + { + uint8 t; + if(read(fileno(stdin),&t,1)==-1) + { + if(errno!=EAGAIN) break; + } + else + break; + + { + struct js_event e; + + while(read(fd[z],&e,sizeof(struct js_event))==sizeof(struct js_event)) + { + if(e.type==JS_EVENT_BUTTON) + { + if(!e.value) + { + joyBMap[z][b]=1< +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "throttle.h" + +#include "../common/config.h" +#include "../common/args.h" +#include "../common/unixdsp.h" +#include "../common/cheat.h" + +#include "dface.h" + +static int ntsccol=0,ntschue=-1,ntsctint=-1; +static int soundvol=100; +static int inited=0; + +int srendlinev[2]={8,0}; +int erendlinev[2]={239,239}; +int srendline,erendline; + + +static char BaseDirectory[2048]; + +int eoptions=0; + +static void DriverKill(void); +static int DriverInitialize(void); + +static int gametype; +#include "input.c" + +static void ParseGI(FCEUGI *gi) +{ + gametype=gi->type; + + InputType[0]=UsrInputType[0]; + InputType[1]=UsrInputType[1]; + InputTypeFC=UsrInputTypeFC; + + if(gi->input[0]>=0) + InputType[0]=gi->input[0]; + if(gi->input[1]>=0) + InputType[1]=gi->input[1]; + if(gi->inputfc>=0) + InputTypeFC=gi->inputfc; + FCEUI_GetCurrentVidSystem(&srendline,&erendline); +} + +void FCEUD_PrintError(char *s) +{ + puts(s); +} + +static char *cpalette=0; +static void LoadCPalette(void) +{ + char tmpp[192]; + FILE *fp; + + if(!(fp=fopen(cpalette,"rb"))) + { + printf(" Error loading custom palette from file: %s\n",cpalette); + return; + } + fread(tmpp,1,192,fp); + FCEUI_SetPaletteArray(tmpp); + fclose(fp); +} + +static CFGSTRUCT fceuconfig[]={ + AC(soundvol), + ACS(cpalette), + AC(ntsctint), + AC(ntschue), + AC(ntsccol), + AC(UsrInputTypeFC), + ACA(UsrInputType), + AC(powerpadside), + AC(powerpadsc), + AC(eoptions), + ACA(srendlinev), + ACA(erendlinev), + ADDCFGSTRUCT(DriverConfig), + ENDCFGSTRUCT +}; + +static void SaveConfig(void) +{ + char tdir[2048]; + sprintf(tdir,"%s"PSS"fceu.cfg",BaseDirectory); + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + SaveFCEUConfig(tdir,fceuconfig); +} + +static void LoadConfig(void) +{ + char tdir[2048]; + sprintf(tdir,"%s"PSS"fceu.cfg",BaseDirectory); + LoadFCEUConfig(tdir,fceuconfig); + if(ntsctint>=0) DriverInterface(DES_SETNTSCTINT,&ntsctint); + if(ntschue>=0) DriverInterface(DES_SETNTSCHUE,&ntschue); +} + +static void CreateDirs(void) +{ + char *subs[5]={"fcs","snaps","gameinfo","sav","cheats"}; + char tdir[2048]; + int x; + + mkdir(BaseDirectory,S_IRWXU); + for(x=0;x<5;x++) + { + sprintf(tdir,"%s"PSS"%s",BaseDirectory,subs[x]); + mkdir(tdir,S_IRWXU); + } +} + +static void SetSignals(void (*t)(int)) +{ + int sigs[11]={SIGINT,SIGTERM,SIGHUP,SIGPIPE,SIGSEGV,SIGFPE,SIGKILL,SIGALRM,SIGABRT,SIGUSR1,SIGUSR2}; + int x; + for(x=0;x<11;x++) + signal(sigs[x],t); +} + +static void CloseStuff(int signum) +{ + DriverKill(); + printf("\nSignal %d has been caught and dealt with...\n",signum); + switch(signum) + { + case SIGINT:printf("How DARE you interrupt me!\n");break; + case SIGTERM:printf("MUST TERMINATE ALL HUMANS\n");break; + case SIGHUP:printf("Reach out and hang-up on someone.\n");break; + case SIGPIPE:printf("The pipe has broken! Better watch out for floods...\n");break; + case SIGSEGV:printf("Iyeeeeeeeee!!! A segmentation fault has occurred. Have a fluffy day.\n");break; + /* So much SIGBUS evil. */ + #ifdef SIGBUS + #if(SIGBUS!=SIGSEGV) + case SIGBUS:printf("I told you to be nice to the driver.\n");break; + #endif + #endif + case SIGFPE:printf("Those darn floating points. Ne'er know when they'll bite!\n");break; + case SIGALRM:printf("Don't throw your clock at the meowing cats!\n");break; + case SIGABRT:printf("Abort, Retry, Ignore, Fail?\n");break; + case SIGUSR1: + case SIGUSR2:printf("Killing your processes is not nice.\n");break; + } + exit(1); +} + +static void DoArgs(int argc, char *argv[]) +{ + static char *cortab[5]={"none","gamepad","zapper","powerpad","arkanoid"}; + static int cortabi[5]={SI_NONE,SI_GAMEPAD, + SI_ZAPPER,SI_POWERPAD,SI_ARKANOID}; + static char *fccortab[5]={"none","arkanoid","shadow","4player","fkb"}; + static int fccortabi[5]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW, + SIFC_4PLAYER,SIFC_FKB}; + + int x; + static char *inputa[2]={0,0}; + static char *fcexp=0; + static int docheckie[4]; + + static ARGPSTRUCT FCEUArgs[]={ + {"-soundvol",0,&soundvol,0}, + {"-cpalette",0,&cpalette,0x4001}, + + {"-ntsccol",0,&ntsccol,0}, + {"-pal",&docheckie[0],0,0}, + {"-input1",0,&inputa[0],0x4001},{"-input2",0,&inputa[1],0x4001}, + {"-fcexp",0,&fcexp,0x4001}, + + {"-gg",&docheckie[1],0,0}, + {"-no8lim",0,&eoptions,0x8001}, + {"-subase",0,&eoptions,0x8002}, + {"-snapname",0,&eoptions,0x8000|EO_SNAPNAME}, + {"-nofs",0,&eoptions,0x8000|EO_NOFOURSCORE}, + {"-clipsides",0,&eoptions,0x8000|EO_CLIPSIDES}, + {"-nothrottle",0,&eoptions,0x8000|EO_NOTHROTTLE}, + {"-slstart",0,&srendlinev[0],0},{"-slend",0,&erendlinev[0],0}, + {"-slstartp",0,&srendlinev[1],0},{"-slendp",0,&erendlinev[1],0}, + {0,(void *)DriverArgs,0,0}, + {0,0,0,0} + }; + + memset(docheckie,0,sizeof(docheckie)); + ParseArguments(argc, argv, FCEUArgs); + if(cpalette) + { + if(cpalette[0]=='0') + if(cpalette[1]==0) + { + free(cpalette); + cpalette=0; + } + } + if(docheckie[0]) + FCEUI_SetVidSystem(1); + if(docheckie[1]) + FCEUI_SetGameGenie(1); + + FCEUI_DisableSpriteLimitation(eoptions&1); + FCEUI_SaveExtraDataUnderBase(eoptions&2); + FCEUI_SetSnapName(eoptions&EO_SNAPNAME); + + for(x=0;x<2;x++) + { + if(srendlinev[x]<0 || srendlinev[x]>239) srendlinev[x]=0; + if(erendlinev[x]239) erendlinev[x]=239; + } + + FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]); + FCEUI_SetSoundVolume(soundvol); + DriverInterface(DES_NTSCCOL,&ntsccol); + DoDriverArgs(); + + if(fcexp) + { + int y; + for(y=0;y<5;y++) + { + if(!strncmp(fccortab[y],fcexp,8)) + { + UsrInputTypeFC=fccortabi[y]; + break; + } + } + free(fcexp); + } + for(x=0;x<2;x++) + { + int y; + + if(!inputa[x]) + continue; + + for(y=0;y<5;y++) + { + if(!strncmp(cortab[y],inputa[x],8)) + { + UsrInputType[x]=cortabi[y]; + if(y==3) + { + powerpadside&=~(1< +#include +#include +#include + +#include "sdl.h" +static SDL_Joystick *jo[4] = {NULL, NULL, NULL, NULL}; + +static void ConfigJoystick (int z); + +#define JOY_A 0x01 +#define JOY_B 0x02 +#define JOY_SELECT 0x04 +#define JOY_START 0x08 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +/* Gets the current joystick position information. */ +uint32 GetJSOr (void) +{ + int n; /* joystick index */ + int b; /* button index */ + int *joym; /* pointer to a joystick's button map */ + Sint16 pos; /* axis position */ + uint32 ret = 0; /* return value */ + + for (n = 0; n < 4; n++) + { + if (joy[n] == 0) + continue; + joym = joyBMap[n]; + + /* Axis information. */ + pos = SDL_JoystickGetAxis(jo[n], joyAMap[n][0]); + if (pos <= -16383) + ret |= JOY_LEFT << (n << 3); + else if (pos >= 16363) + ret |= JOY_RIGHT << (n << 3); + pos = SDL_JoystickGetAxis(jo[n], joyAMap[n][1]); + if (pos <= -16383) + ret |= JOY_UP << (n << 3); + else if (pos >= 16383) + ret |= JOY_DOWN << (n << 3); + + /* Button information. */ + for (b = 0; b < 4; b++) + { + if (SDL_JoystickGetButton(jo[n], joym[b])) + ret |= (1 << b) << (n << 3); + } + } + + return ret; +} + +/* Cleanup opened joysticks. */ +void KillJoysticks (void) +{ + int n; /* joystick index */ + + for (n = 0; n < 4; n++) + { + if (joy[n] != 0) + SDL_JoystickClose(jo[n]); + } + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + return; +} + +/* Initialize joysticks. */ +int InitJoysticks (void) +{ + int n; /* joystick index */ + if(!(joy[0]|joy[1]|joy[2]|joy[3])) + return(0); + SDL_InitSubSystem(SDL_INIT_JOYSTICK); + for (n = 0; n < 4; n++) + { + if (joy[n] == 0) + continue; + + /* Open the joystick under SDL. */ + jo[n] = SDL_JoystickOpen(joy[n] - 1); + if (jo[n] == NULL) + { + printf("Could not open joystick %d: %s.\n", + joy[n] - 1, SDL_GetError()); + joy[n] = 0; + continue; + } + + /* Check for a button map. */ + if (!(joyBMap[n][0] | joyBMap[n][1] | joyBMap[n][2] | + joyBMap[n][3])) + { + ConfigJoystick(n); + } + } + + return (1); +} + +#define WNOINPUT(); for(;;){uint8 t; if(read(fileno(stdin),&t,1)==-1) \ + {break;}} + +/* Configure a joystick button. */ +static void BConfig (int n, int b) +{ + SDL_Event event; /* SDL event structure */ + WNOINPUT(); + while (1) + { + uint8 t; + if (read(fileno(stdin), &t, 1) == -1) + { + if (errno != EAGAIN) + break; + } + else + break; + + if (SDL_PollEvent(&event) && event.type == SDL_JOYBUTTONDOWN) + { + joyBMap[n][b] = event.jbutton.button; + goto endsa; + } + } + + endsa: + WNOINPUT(); + + return; +} + +/* Joystick button and axis configuration. */ +void ConfigJoystick (int n) +{ + int sa; /* buffer value */ + char buf[128]; /* input buffer */ + + printf("\n\n Joystick button and axis configuration:\n\n"); + printf(" Select the joystick axes to use for the virtual d-pad.\n"); + printf(" If you do not wish to assign an axis, press Enter to skip\n"); + printf(" that axis.\n"); + printf(" Push the button to map to the virtual joystick.\n"); + printf(" If you do not wish to assign a button, press Enter to skip\n"); + printf(" that button.\n Press enter to continue...\n"); + getchar(); + printf("**** Configuring joystick %d ****\n\n", n + 1); + + printf("** Enter axis to use for the x-axis (default 0).\n"); + fgets(buf, sizeof(buf), stdin); + joyAMap[n][0] = ('0' <= buf[0] && buf[9] <= '9') ? atoi(buf) : 0; + + printf("** Enter axis to use for the y-axis (default 1).\n"); + fgets(buf, sizeof(buf), stdin); + joyAMap[n][1] = ('0' <= buf[0] && buf[9] <= '9') ? atoi(buf) : 1; + + sa = fcntl(fileno(stdin), F_GETFL); + fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); + + printf("** Press button for \"Select\".\n"); + BConfig(n, 2); + + printf("** Press button for \"Start\".\n"); + BConfig(n, 3); + + printf("** Press button for \"B\".\n"); + BConfig(n, 1); + + printf("** Press button for \"A\".\n"); + BConfig(n, 0); + + fcntl(fileno(stdin), F_SETFL, sa); +} diff --git a/drivers/cli/sdl-netplay.c b/drivers/cli/sdl-netplay.c new file mode 100644 index 0000000..38a4c81 --- /dev/null +++ b/drivers/cli/sdl-netplay.c @@ -0,0 +1,163 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2001 LULU + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "sdl.h" +#include +#include "sdl-netplay.h" + +char *netplayhost=0; + +static int tonowait; + +int Port=0xFCE; +int FDnetplay=0; + + +static SDLNet_SocketSet socketset = NULL; +static TCPsocket tcpsock = NULL, servsock = NULL; + +void cleanup(void) +{ + if (tcpsock != NULL) { + SDLNet_TCP_DelSocket(socketset, tcpsock); + SDLNet_TCP_Close(tcpsock); + tcpsock = NULL; + } + if (servsock != NULL) { + SDLNet_TCP_DelSocket(socketset, servsock); + SDLNet_TCP_Close(servsock); + servsock = NULL; + } + if (socketset != NULL) { + SDLNet_FreeSocketSet(socketset); + socketset = NULL; + } +} + +int FCEUD_NetworkConnect(void) +{ + IPaddress serverIP; + + tonowait=0; + + if (netplay == 2) { + /* client */ + printf("connecting to %s\n", netplayhost); + + SDLNet_ResolveHost(&serverIP, netplayhost, Port); + if (serverIP.host == INADDR_NONE) { + fprintf(stderr, "Couldn't connected to %s\n", netplayhost); + return -1; + } else { + tcpsock = SDLNet_TCP_Open(&serverIP); + if (tcpsock == NULL) { + fprintf(stderr, "Couldn't connected to %s\n", netplayhost); + return -1; + } + } + printf("connected to %s\n", netplayhost); + + socketset = SDLNet_AllocSocketSet(1); + if (socketset == NULL) { + fprintf(stderr, "Couldn't create socket set: %s\n", + SDLNet_GetError()); + return -1; + } + SDLNet_TCP_AddSocket(socketset, tcpsock); + + return 1; + } else { + /* server */ + + SDLNet_ResolveHost(&serverIP, NULL, Port); + printf("Server IP: %x, %d\n", serverIP.host, serverIP.port); + servsock = SDLNet_TCP_Open(&serverIP); + if (servsock == NULL) { + cleanup(); + fprintf(stderr, "Couldn't create server socket: %s\n", + SDLNet_GetError()); + return -1; + } + + socketset = SDLNet_AllocSocketSet(2); + if (socketset == NULL) { + fprintf(stderr, "Couldn't create socket set: %s\n", + SDLNet_GetError()); + return -1; + } + SDLNet_TCP_AddSocket(socketset, servsock); + + if (SDLNet_CheckSockets(socketset, ~0)) { + tcpsock = SDLNet_TCP_Accept(servsock); + if (tcpsock == NULL) { + return -1; + } + SDLNet_TCP_AddSocket(socketset, tcpsock); + + printf("OK, connected\n"); + return 1; + } + } + + return -1; +} + +void FCEUD_NetworkClose(void) +{ + cleanup(); +} + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block) +{ + if(block) + { + if(SDLNet_TCP_Recv(tcpsock, (void *) data, len)!=len) + { + cleanup(); + return(0); + } + switch(SDLNet_CheckSockets(socketset,0)) + { + case -1:return(0); + case 0:NoWaiting&=~2;tonowait=0;break; + default:if(tonowait>=3) + NoWaiting|=2; + else tonowait++; + break; + } + return(1); + } + else + { + int t=SDLNet_CheckSockets(socketset,0); + if(t<0) return(0); + if(!t) return(-1); + return(SDLNet_TCP_Recv(tcpsock, (void *) data, len)==len); + } +} + +/* 0 on failure, 1 on success. This function should always block. */ +int FCEUD_NetworkSendData(uint8 *Value, uint32 len) +{ + if (tcpsock) + return(SDLNet_TCP_Send(tcpsock, (void *) Value, len)==len); + return 0; +} diff --git a/drivers/cli/sdl-netplay.h b/drivers/cli/sdl-netplay.h new file mode 100644 index 0000000..48769f6 --- /dev/null +++ b/drivers/cli/sdl-netplay.h @@ -0,0 +1,5 @@ +extern char *netplayhost; +extern int Port; +extern int FDnetplay; +#define netplay FDnetplay + diff --git a/drivers/cli/sdl-sound.c b/drivers/cli/sdl-sound.c new file mode 100644 index 0000000..bbe716b --- /dev/null +++ b/drivers/cli/sdl-sound.c @@ -0,0 +1,148 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "sdl.h" + +#ifndef DSPSOUND + +//#define BSIZE (32768-1024) + +static int32 BSIZE; +static volatile int16 AudioBuf[32768]; +static volatile uint32 readoffs,writeoffs; +void fillaudio(void *udata, uint8 *stream, int len) +{ + int16 *dest=(int16 *)stream; + + len>>=1; + while(len) + { + *dest=AudioBuf[readoffs]; + dest++; + readoffs=(readoffs+1)&32767; + len--; + } +} + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + while(Count) + { + while(writeoffs==((readoffs-BSIZE)&32767)) + if(NoWaiting) + return; + AudioBuf[writeoffs]=*Buffer; + writeoffs=(writeoffs+1)&32767; + Buffer++; + Count--; + } +} + +int InitSound(void) +{ + if(_sound) + { + SDL_AudioSpec spec; + + if(_lbufsize<_ebufsize) + { + puts("Ack, lbufsize must not be smaller than ebufsize!"); + return(0); + } + if(_lbufsize<6 || _lbufsize>13) + { + puts("lbufsize out of range"); + return(0); + } + if(_ebufsize<5) + { + puts("ebufsize out of range"); + return(0); + } + memset(&spec,0,sizeof(spec)); + if(SDL_InitSubSystem(SDL_INIT_AUDIO)<0) + { + puts(SDL_GetError()); + return(0); + } + if(_sound==1) _sound=44100; + spec.freq=_sound; + spec.format=AUDIO_S16; + spec.channels=1; + spec.samples=1<<_ebufsize; + spec.callback=fillaudio; + spec.userdata=0; + + if(SDL_OpenAudio(&spec,0)<0) + { + puts(SDL_GetError()); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + return(0); + } + FCEUI_Sound(_sound); + BSIZE=32768-(1<<_lbufsize); + SDL_PauseAudio(0); + return(1); + } + return(0); +} + +void SilenceSound(int n) +{ + SDL_PauseAudio(n); + +} + +void KillSound(void) +{ + SDL_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +#else +#include "../common/unixdsp.h" + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + WriteUNIXDSPSound(Buffer, Count, NoWaiting); +} + +int InitSound(void) +{ + if(_sound) + { + int rate; + if(_sound==1) + _sound=48000; + rate=_sound; + if(InitUNIXDSPSound(&rate,_f8bit?0:1,8,8)) + { + FCEUI_Sound(rate); + return(1); + } + } + return(0); +} +void KillSound(void) +{ + KillUNIXDSPSound(); +} +#endif diff --git a/drivers/cli/sdl-video.c b/drivers/cli/sdl-video.c new file mode 100644 index 0000000..5ca240c --- /dev/null +++ b/drivers/cli/sdl-video.c @@ -0,0 +1,228 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "sdl.h" +#include "../common/vidblit.h" + +#define _sline srendline +#define _eline erendline + +SDL_Surface *screen; + +static int tlines; +static int inited=0; + +static int exs,eys,eefx; +#define NWIDTH (256-((eoptions&EO_CLIPSIDES)?16:0)) +#define NOFFSET (eoptions&EO_CLIPSIDES?8:0) + +static void CleanSurface(void) +{ + uint32 x; + + x=screen->pitch*screen->h; + + if(SDL_MUSTLOCK(screen)) + SDL_LockSurface(screen); + + memset((uint8*)screen->pixels, 0x80, x); + + if(SDL_MUSTLOCK(screen)) + SDL_UnlockSurface(screen); + + SDL_UpdateRect(screen, 0, 0, 0, 0); +} + +static int paletterefresh; + +void KillVideo(void) +{ + if(inited&1) + { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + } + inited=0; +} + +int InitVideo(void) +{ + const SDL_VideoInfo *vinf; + int flags=0; + + #ifdef BROKEN + if(_fullscreen && _fshack) + setenv("SDL_VIDEODRIVER",_fshack,1); + else + { + if(!_fshacksave) + unsetenv("SDL_VIDEODRIVER"); + else + setenv("SDL_VIDEODRIVER",_fshacksave,1); + } + #endif + if(SDL_InitSubSystem(SDL_INIT_VIDEO)==-1) + { + puts(SDL_GetError()); + return(0); + } + inited|=1; + + SDL_ShowCursor(0); + tlines=_eline-_sline+1; + + vinf=SDL_GetVideoInfo(); + + if(vinf->hw_available) + flags|=SDL_HWSURFACE; + + if(_fullscreen) + flags|=SDL_FULLSCREEN; + flags|=SDL_HWPALETTE; + + if(_fullscreen) + { + exs=_xscalefs; + eys=_yscalefs; + eefx=_efxfs; + if(_xrespixels; + + if(_fullscreen) + { + xo=(((screen->w-NWIDTH*exs))>>1); + dest+=xo; + if(screen->h>(tlines*eys)) + { + yo=((screen->h-tlines*eys)>>1); + dest+=yo*screen->pitch; + } + } + + Blit8To8(XBuf+NOFFSET,dest, NWIDTH, tlines, screen->pitch,exs,eys,eefx); + + if(SDL_MUSTLOCK(screen)) + SDL_UnlockSurface(screen); + + SDL_UpdateRect(screen, xo, yo, NWIDTH*exs, tlines*eys); +} + +uint32 PtoV(uint16 x, uint16 y) +{ + if(_fullscreen) + { + + } + else + { + if(eoptions&EO_CLIPSIDES) + x+=8; + y+=srendline; + } + return(x|(y<<16)); +} diff --git a/drivers/cli/sdl-video.h b/drivers/cli/sdl-video.h new file mode 100644 index 0000000..036c933 --- /dev/null +++ b/drivers/cli/sdl-video.h @@ -0,0 +1 @@ +uint32 PtoV(uint16 x, uint16 y); diff --git a/drivers/cli/sdl.c b/drivers/cli/sdl.c new file mode 100644 index 0000000..dfa5d40 --- /dev/null +++ b/drivers/cli/sdl.c @@ -0,0 +1,215 @@ +#include +#include +#include + +#include "sdl.h" +#include "sdl-video.h" +#ifdef NETWORK +#include "unix-netplay.h" +#endif + +DSETTINGS Settings; +CFGSTRUCT DriverConfig[]={ + AC(_xscale), + AC(_yscale), + AC(_xscalefs), + AC(_yscalefs), + AC(_efx), + AC(_efxfs), + AC(_sound), + #ifdef DSPSOUND + AC(_f8bit), + #else + AC(_ebufsize), + AC(_lbufsize), + #endif + AC(_fullscreen), + AC(_xres), + AC(_yres), + ACA(joyBMap), + ACA(joyAMap), + ACA(joy), + //ACS(_fshack), + ENDCFGSTRUCT +}; + +//-fshack x Set the environment variable SDL_VIDEODRIVER to \"x\" when +// entering full screen mode and x is not \"0\". + +char *DriverUsage= +"-xres x Set horizontal resolution to x for full screen mode.\n\ +-yres x Set vertical resolution to x for full screen mode.\n\ +-xscale(fs) x Multiply width by x.\n\ +-yscale(fs) x Multiply height by x.\n\ +-efx(fs) x Enable scanlines effect if x is non zero. yscale must be >=2\n\ + and preferably a multiple of 2.\n\ +-fs x Select full screen mode if x is non zero.\n\ +-joyx y Use joystick y as virtual joystick x.\n\ +-sound x Sound.\n\ + 0 = Disabled.\n\ + Otherwise, x = playback rate.\n\ +" +#ifdef DSPSOUND +"-f8bit x Force 8-bit sound.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +" +#else +"-lbufsize x Internal FCE Ultra sound buffer size. Size = 2^x samples.\n\ +-ebufsize x External SDL sound buffer size. Size = 2^x samples.\n\ +" +#endif +"-connect s Connect to server 's' for TCP/IP network play.\n\ +-server Be a host/server for TCP/IP network play.\n\ +-netport x Use TCP/IP port x for network play."; + +static int docheckie[2]={0,0}; +ARGPSTRUCT DriverArgs[]={ + {"-joy1",0,&joy[0],0},{"-joy2",0,&joy[1],0}, + {"-joy3",0,&joy[2],0},{"-joy4",0,&joy[3],0}, + {"-xscale",0,&_xscale,0}, + {"-yscale",0,&_yscale,0}, + {"-efx",0,&_efx,0}, + {"-xscalefs",0,&_xscalefs,0}, + {"-yscalefs",0,&_yscalefs,0}, + {"-efxfs",0,&_efxfs,0}, + {"-xres",0,&_xres,0}, + {"-yres",0,&_yres,0}, + {"-fs",0,&_fullscreen,0}, + //{"-fshack",0,&_fshack,0x4001}, + {"-sound",0,&_sound,0}, + #ifdef DSPSOUND + {"-f8bit",0,&_f8bit,0}, + #else + {"-lbufsize",0,&_lbufsize,0}, + {"-ebufsize",0,&_ebufsize,0}, + #endif + #ifdef NETWORK + {"-connect",&docheckie[0],&netplayhost,0x4001}, + {"-server",&docheckie[1],0,0}, + {"-netport",0,&Port,0}, + #endif + {0,0,0,0} +}; + +static void SetDefaults(void) +{ + _xres=320; + _yres=240; + _fullscreen=0; + _sound=48000; + #ifdef DSPSOUND + _f8bit=0; + #else + _lbufsize=10; + _ebufsize=8; + #endif + _xscale=_yscale=_xscalefs=_yscalefs=1; + _efx=_efxfs=0; + //_fshack=_fshacksave=0; + memset(joy,0,sizeof(joy)); +} + +void DoDriverArgs(void) +{ + int x; + + #ifdef BROKEN + if(_fshack) + { + if(_fshack[0]=='0') + if(_fshack[1]==0) + { + free(_fshack); + _fshack=0; + } + } + #endif + + #ifdef NETWORK + if(docheckie[0]) + netplay=2; + else if(docheckie[1]) + netplay=1; + + if(netplay) + FCEUI_SetNetworkPlay(netplay); + #endif + + for(x=0;x<4;x++) + if(!joy[x]) + { + memset(joyBMap[x],0,sizeof(joyBMap[0])); + memset(joyAMap[x],0,sizeof(joyAMap[0])); + } +} +int InitMouse(void) +{ + return(0); +} +void KillMouse(void){} +void GetMouseData(uint32 *d) +{ + int x,y; + uint32 t; + + t=SDL_GetMouseState(&x,&y); + d[2]=0; + if(t&SDL_BUTTON(1)) + d[2]|=1; + if(t&SDL_BUTTON(3)) + d[2]|=2; + t=PtoV(x,y); + d[0]=t&0xFFFF; + d[1]=(t>>16)&0xFFFF; +} + +int InitKeyboard(void) +{ + return(1); +} + +int UpdateKeyboard(void) +{ + return(1); +} + +void KillKeyboard(void) +{ + +} + +char *GetKeyboard(void) +{ + SDL_PumpEvents(); + return(SDL_GetKeyState(0)); +} +#include "unix-basedir.h" + +int main(int argc, char *argv[]) +{ + puts("\nStarting FCE Ultra "VERSION_STRING"...\n"); + if(SDL_Init(0)) + { + printf("Could not initialize SDL: %s.\n", SDL_GetError()); + return(-1); + } + SetDefaults(); + + #ifdef BROKEN + if(getenv("SDL_VIDEODRIVER")) + { + if((_fshacksave=malloc(strlen(getenv("SDL_VIDEODRIVER"))+1))) + strcpy(_fshacksave,getenv("SDL_VIDEODRIVER")); + } + else + _fshacksave=0; + #endif + + { + int ret=CLImain(argc,argv); + SDL_Quit(); + return(ret); + } +} + diff --git a/drivers/cli/sdl.h b/drivers/cli/sdl.h new file mode 100644 index 0000000..a235951 --- /dev/null +++ b/drivers/cli/sdl.h @@ -0,0 +1,47 @@ +#include +#include "../../driver.h" +#include "../common/args.h" +#include "../common/config.h" +#include "main.h" + +typedef struct { + int xres; + int yres; + int xscale,yscale; + int xscalefs,yscalefs; + int efx,efxfs; + int fullscreen; + int sound; + #ifdef DSPSOUND + int f8bit; + #else + int lbufsize,ebufsize; + #endif + int joy[4]; + int joyAMap[4][2]; + int joyBMap[4][4]; + char *fshack; + char *fshacksave; +} DSETTINGS; + +extern DSETTINGS Settings; + +#define _xres Settings.xres +#define _yres Settings.yres +#define _fullscreen Settings.fullscreen +#define _sound Settings.sound +#define _f8bit Settings.f8bit +#define _xscale Settings.xscale +#define _yscale Settings.yscale +#define _xscalefs Settings.xscalefs +#define _yscalefs Settings.yscalefs +#define _efx Settings.efx +#define _efxfs Settings.efxfs +#define _ebufsize Settings.ebufsize +#define _lbufsize Settings.lbufsize +#define _fshack Settings.fshack +#define _fshacksave Settings.fshacksave + +#define joyAMap Settings.joyAMap +#define joyBMap Settings.joyBMap +#define joy Settings.joy diff --git a/drivers/cli/svga-video.c b/drivers/cli/svga-video.c new file mode 100644 index 0000000..67f4ecb --- /dev/null +++ b/drivers/cli/svga-video.c @@ -0,0 +1,497 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 \Firebug\ + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +#define inportb inb +#define outportb(port, value) outb(value, port) +#define outportw(port, value) outw(value, port) + +#include "main.h" +#include "svgalib.h" +#include "svga-video.h" + + +int vmode=1; + +#ifdef FRAMESKIP +int FCEUDfskip=0; +#endif + +static int vidready=0; +static int conlock=0; + +void LockConsole(void) +{ + if(!conlock) + { + vga_lockvc(); + conlock=1; + FCEUI_DispMessage("Console locked."); + } +} + +void UnlockConsole(void) +{ + if(conlock) + { + vga_unlockvc(); + conlock=0; + FCEUI_DispMessage("Console unlocked."); + } +} + +void SetBorder(void) +{ + if(!conlock) + vga_lockvc(); + inportb(0x3da); + outportb(0x3c0,(0x11|0x20)); + outportb(0x3c0,0x80); + if(!conlock) + vga_unlockvc(); +} + +#include "vgatweak.c" + +void TweakVGA(int VGAMode) +{ + int I; + + if(!conlock) + vga_lockvc(); + + outportb(0x3C8,0x00); + for(I=0;I<768;I++) outportb(0x3C9,0x00); + + outportb(0x3D4,0x11); + I=inportb(0x3D5)&0x7F; + outportb(0x3D4,0x11); + outportb(0x3D5,I); + + switch(VGAMode) + { + case 1: for(I=0;I<25;I++) VGAPortSet(v256x240[I]);break; + case 2: for(I=0;I<25;I++) VGAPortSet(v256x256[I]);break; + case 3: for(I=0;I<25;I++) VGAPortSet(v256x256S[I]);break; + case 6: for(I=0;I<25;I++) VGAPortSet(v256x224S[I]);break; + case 8: for(I=0;I<25;I++) VGAPortSet(v256x224_103[I]);break; + default: break; + } + + outportb(0x3da,0); + if(!conlock) + vga_unlockvc(); +} + + +static uint8 palettedbr[256],palettedbg[256],palettedbb[256]; + +static void FlushPalette(void) +{ + int x; + for(x=0;x<256;x++) + { + int z=x; + if(vmode==4 || vmode==5 || vmode==7) z^=0x80; + vga_setpalette(z,palettedbr[x]>>2,palettedbg[x]>>2,palettedbb[x]>>2); + } +} + +void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b) +{ + palettedbr[index]=r; + palettedbg[index]=g; + palettedbb[index]=b; + + if(vidready) + { + if(vmode==4 || vmode==5 || vmode==7) index^=0x80; + vga_setpalette(index,r>>2,g>>2,b>>2); + } +} + + +void FCEUD_GetPalette(uint8 i, uint8 *r, uint8 *g, uint8 *b) +{ + *r=palettedbr[i]; + *g=palettedbg[i]; + *b=palettedbb[i]; +} + +static void vcfix(void) +{ + int z; + + if(!conlock) + vga_lockvc(); + z=inportb(0x3cc); + if(!conlock) + vga_unlockvc(); + if(z!=0xe3 && z!=0xe7) // Common value in all tweaked video modes(and not in 320x200 mode). + { + TweakVGA(vmode); + SetBorder(); + FlushPalette(); + } +} + +static uint8 *ScreenLoc; + +int InitVideo(void) +{ + #ifdef DUMMY + return(1); + #endif + vidready=0; + + if(vmode<=3 || vmode==6 || vmode==8) + { + if(vga_getcurrentchipset()==FBDEV) + { + puts("Tweaked VGA video modes will not work. Using a 320x240 video mode instead..."); + vmode=7; + } + } + + switch(vmode) + { + default: + case 1: + case 2: + case 3: + case 6: + case 8: + vga_setmode(G320x200x256); + vidready|=1; + ScreenLoc=vga_getgraphmem(); + TweakVGA(vmode); + SetBorder(); + memset(ScreenLoc,128,256*256); + break; + case 4: + case 5: + if(!(vga_getmodeinfo(G640x480x256)->flags & CAPABLE_LINEAR)) + { + puts("Video: No linear addressing mode available!"); + return 0; + } + if(vga_setmode(G640x480x256)==-1) + { + puts("Video: Could not set 640x480x8bpp video mode!"); + return 0; + } + vidready|=1; + + vga_setpage(0); + if(vga_setlinearaddressing()!=-1) + ScreenLoc=vga_getgraphmem(); + else + { + puts("Video: Could not set linear addressing!"); + return 0; + } + memset(ScreenLoc,0,640*480); + break; + case 7: + if(!(vga_getmodeinfo(G320x240x256V)->flags & CAPABLE_LINEAR)) + { + puts("Video: No linear addressing mode available!"); + return 0; + } + if(vga_setmode(G320x240x256V)==-1) + { + puts("Video: Could not set 320x240x8bpp video mode!"); + return 0; + } + vidready|=1; + + vga_setpage(0); + if(vga_setlinearaddressing()!=-1) + ScreenLoc=vga_getgraphmem(); + else + { + puts("Video: Could not set linear addressing!"); + return 0; + } + memset(ScreenLoc,0,320*240); + break; + } + vidready|=2; + FlushPalette(); // Needed for cheat console code(and it isn't a bad thing to do anyway...). + return 1; +} + +void KillVideo(void) +{ + if(vidready) + { + vga_setmode(TEXT); + vidready=0; + } +} + + +void BlitScreen(uint8 *XBuf) +{ + static int conto=0; + uint8 *dest; + int tlines; + #ifdef DUMMY + return; + #endif + #ifdef FRAMESKIP + FCEUI_FrameSkip(FCEUDfskip); + #endif + + if(doptions&DO_VSYNC && !NoWaiting) + { + vga_waitretrace(); + } + + tlines=erendline-srendline+1; + + dest=ScreenLoc; + + if(vmode!=4 && vmode!=5 && vmode!=7) + { + conto=(conto+1)&0x3F; + if(!conto) vcfix(); + } + switch(vmode) + { + case 1:dest+=(((240-tlines)>>1)<<8);break; + case 2: + case 3:dest+=(((256-tlines)>>1)<<8);break; + case 4: + case 5:dest+=(((240-tlines)>>1)*640+((640-512)>>1));break; + case 8: + case 6:if(tlines>224) tlines=224;dest+=(((224-tlines)>>1)<<8);break; + case 7:dest+=(((240-tlines)>>1)*320)+32;break; + } + + XBuf+=(srendline<<8)+(srendline<<4); + + if(eoptions&EO_CLIPSIDES) + { + if(vmode==5) + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "ckoop1:\n\t" + "movb $120,%%al \n\t" + "ckoop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne ckoop2\n\t" + + "addl $32,%%esi\n\t" + "addl $800,%%edi\n\t" + "decb %%bl\n\t" + "jne ckoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==4) + { + asm volatile( + "cyoop1:\n\t" + "movb $120,%%al \n\t" + "cyoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne cyoop2\n\t" + + "addl $32,%%esi\n\t" + "addl $800,%%edi\n\t" + "decb %%bl\n\t" + "jne cyoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==7) + { + asm volatile( + "cgoop81:\n\t" + "movl $30,%%eax\n\t" + "cgoop82:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "xorl $0x80808080,%%edx\n\t" + "xorl $0x80808080,%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne cgoop82\n\t" + "addl $80,%%edi\n\t" + "addl $32,%%esi\n\t" + "decb %%bl\n\t" + "jne cgoop81\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + else + { + asm volatile( + "cgoop1:\n\t" + "movl $30,%%eax\n\t" + "cgoop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne cgoop2\n\t" + "addl $32,%%esi\n\t" + "addl $16,%%edi\n\t" + "decb %%bl\n\t" + "jne cgoop1\n\t" + : + : "S" (XBuf+8), "D" (dest+8), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + } + else + { + if(vmode==5) + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "koop1:\n\t" + "movb $128,%%al \n\t" + "koop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne koop2\n\t" + + "addl $16,%%esi\n\t" + "addl $768,%%edi\n\t" + "decb %%bl\n\t" + "jne koop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==4) + { + asm volatile( + "yoop1:\n\t" + "movb $128,%%al \n\t" + "yoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne yoop2\n\t" + + "addl $16,%%esi\n\t" + "addl $768,%%edi\n\t" + "decb %%bl\n\t" + "jne yoop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%al", "%edx", "%cc" ); + } + else if(vmode==7) + { + asm volatile( + "goop81:\n\t" + "movl $32,%%eax\n\t" + "goop82:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "xorl $0x80808080,%%edx\n\t" + "xorl $0x80808080,%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne goop82\n\t" + "addl $64,%%edi\n\t" + "addl $16,%%esi\n\t" + "decb %%bl\n\t" + "jne goop81\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + else + { + asm volatile( + "goop1:\n\t" + "movl $32,%%eax\n\t" + "goop2:\n\t" + "movl (%%esi),%%edx\n\t" + "movl 4(%%esi),%%ecx\n\t" + "movl %%edx,(%%edi)\n\t" + "movl %%ecx,4(%%edi)\n\t" + "addl $8,%%esi\n\t" + "addl $8,%%edi\n\t" + "decl %%eax\n\t" + "jne goop2\n\t" + "addl $16,%%esi\n\t" + "decb %%bl\n\t" + "jne goop1\n\t" + : + : "S" (XBuf), "D" (dest), "b" (tlines) + : "%eax","%cc","%edx","%ecx" ); + } + } +} + + diff --git a/drivers/cli/svga-video.h b/drivers/cli/svga-video.h new file mode 100644 index 0000000..e0a991b --- /dev/null +++ b/drivers/cli/svga-video.h @@ -0,0 +1,32 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern int vmode; + +#ifdef FRAMESKIP +extern int FCEUDfskip; +#endif + +void LockConsole(void); +void UnlockConsole(void); +int InitVideo(void); +void KillVideo(void); +void FCEUD_BlitScreen(uint8 *XBuf); + diff --git a/drivers/cli/svgalib.c b/drivers/cli/svgalib.c new file mode 100644 index 0000000..7d293e9 --- /dev/null +++ b/drivers/cli/svgalib.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include + +#include "../../driver.h" +#include "../common/args.h" +#include "../common/config.h" +#include "../common/unixdsp.h" + +#include "svgalib.h" +#include "svga-video.h" +#include "lnx-joystick.h" +#include "unix-netplay.h" + +static int soundo=48000; +static int f8bit=0; +static int sfragsize=7,snfrags=8; + +int doptions=0; + +CFGSTRUCT DriverConfig[]={ + NAC("sound",soundo), + AC(doptions), + AC(f8bit), + AC(vmode), + NACA("joybmap",joyBMap), + ACA(joy), + AC(snfrags), + AC(sfragsize), + ENDCFGSTRUCT +}; + + +char *DriverUsage= +"-vmode x Select video mode(all are 8 bpp).\n\ + 1 = 256x240 5 = 640x480(\"1 per 4\")\n\ + 2 = 256x256 6 = 256x224(with scanlines)\n\ + 3 = 256x256(with scanlines) 7 = 320x240\n\ + 4 = 640x480(with scanlines) 8 = 256x224\n\ +-vsync x Wait for the screen's vertical retrace before updating the\n\ + screen. Refer to the documentation for caveats.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-joyx y Joystick mapped to virtual joystick x(1-4).\n\ + 0 = Disabled, reset configuration.\n\ + Otherwise, y(1-inf) = joystick number.\n\ +-sound x Sound.\n\ + 0 = Disabled.\n\ + Otherwise, x = playback rate.\n\ +-sfragsize x Set sound fragment size to 2^x samples.\n\ +-snfrags x Set number of sound fragments to x.\n\ +-f8bit x Force 8-bit sound.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-connect s Connect to server 's' for TCP/IP network play.\n\ +-server Be a host/server for TCP/IP network play.\n\ +-netport x Use TCP/IP port x for network play."; + + +static int docheckie[2]={0,0}; +ARGPSTRUCT DriverArgs[]={ + {"-joy1",0,&joy[0],0},{"-joy2",0,&joy[1],0}, + {"-joy3",0,&joy[2],0},{"-joy4",0,&joy[3],0}, + {"-snfrags",0,&snfrags,0},{"-sfragsize",0,&sfragsize,0}, + {"-vmode",0,&vmode,0}, + {"-vsync",0,&doptions,0x8000|DO_VSYNC}, + {"-sound",0,&soundo,0}, + {"-f8bit",0,&f8bit,0}, + {"-connect",&docheckie[0],&netplayhost,0x4001}, + {"-server",&docheckie[1],0,0}, + {"-netport",0,&Port,0}, + {0,0,0,0} +}; + +void DoDriverArgs(void) +{ + int x; + + if(docheckie[0]) + netplay=2; + else if(docheckie[1]) + netplay=1; + + if(netplay) + FCEUI_SetNetworkPlay(netplay); + + for(x=0;x<4;x++) + if(!joy[x]) memset(joyBMap[x],0,4*sizeof(int)); +} + +int InitSound(void) +{ + if(soundo) + { + int rate; + if(soundo==1) + soundo=48000; + rate=soundo; + if(InitUNIXDSPSound(&rate,f8bit?0:1,sfragsize,snfrags)) + { + FCEUI_Sound(rate); + return(1); + } + } + return(0); +} + +void WriteSound(int32 *Buffer, int Count, int NoWaiting) +{ + WriteUNIXDSPSound(Buffer,Count,NoWaiting); +} + +void KillSound(void) +{ + KillUNIXDSPSound(); +} + +int InitMouse(void) +{ + vga_setmousesupport(1); + mouse_setxrange(0,260); + mouse_setyrange(0,260); + mouse_setscale(1); + return(1); +} + +void KillMouse(void) +{ + mouse_close(); +} + +void GetMouseData(uint32 *MouseData) +{ + int z; + mouse_update(); + MouseData[0]=mouse_getx(); + MouseData[1]=mouse_gety(); + z=mouse_getbutton(); + MouseData[2]=((z&MOUSE_LEFTBUTTON)?1:0)|((z&MOUSE_RIGHTBUTTON)?2:0); +} + +#include "unix-basedir.h" + +int InitKeyboard(void) +{ + if(keyboard_init()==-1) + { + puts("Error initializing keyboard."); + return 0; + } + keyboard_translatekeys(TRANSLATE_CURSORKEYS | TRANSLATE_DIAGONAL); + return 1; +} + +int UpdateKeyboard(void) +{ + return(keyboard_update()); +} + +char *GetKeyboard(void) +{ + return(keyboard_getstate()); +} + +void KillKeyboard(void) +{ + keyboard_close(); +} + +int main(int argc, char *argv[]) +{ + puts("\nStarting FCE Ultra "VERSION_STRING"...\n"); + vga_init(); + return(CLImain(argc,argv)); +} diff --git a/drivers/cli/svgalib.h b/drivers/cli/svgalib.h new file mode 100644 index 0000000..8f18b74 --- /dev/null +++ b/drivers/cli/svgalib.h @@ -0,0 +1,2 @@ +#define DO_VSYNC 1 +extern int doptions; diff --git a/drivers/cli/throttle.c b/drivers/cli/throttle.c new file mode 100644 index 0000000..604cef7 --- /dev/null +++ b/drivers/cli/throttle.c @@ -0,0 +1,41 @@ +#include +#include "main.h" +#include "throttle.h" + +static uint64 tfreq; +static uint64 desiredfps; + +void RefreshThrottleFPS(void) +{ + desiredfps=FCEUI_GetDesiredFPS()>>8; + tfreq=1000000; + tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ +} + +static uint64 GetCurTime(void) +{ + uint64 ret; + struct timeval tv; + + gettimeofday(&tv,0); + ret=(uint64)tv.tv_sec*1000000; + ret+=tv.tv_usec; + return(ret); +} + +void SpeedThrottle(void) +{ + static uint64 ttime,ltime; + + waiter: + + ttime=GetCurTime(); + + if( (ttime-ltime) < (tfreq/desiredfps) ) + goto waiter; + if( (ttime-ltime) >= (tfreq*4/desiredfps)) + ltime=ttime; + else + ltime+=tfreq/desiredfps; +} + diff --git a/drivers/cli/throttle.h b/drivers/cli/throttle.h new file mode 100644 index 0000000..0b0ad9f --- /dev/null +++ b/drivers/cli/throttle.h @@ -0,0 +1,2 @@ +void RefreshThrottleFPS(void); +void SpeedThrottle(void); diff --git a/drivers/cli/unix-basedir.h b/drivers/cli/unix-basedir.h new file mode 100644 index 0000000..4f6808c --- /dev/null +++ b/drivers/cli/unix-basedir.h @@ -0,0 +1,15 @@ +#include +void GetBaseDirectory(char *BaseDirectory) +{ + char *ol; + + ol=getenv("HOME"); + BaseDirectory[0]=0; + if(ol) + { + strncpy(BaseDirectory,ol,2047); + BaseDirectory[2047]=0; + strcat(BaseDirectory,"/.fceultra"); + } +} + diff --git a/drivers/cli/unix-netplay.c b/drivers/cli/unix-netplay.c new file mode 100644 index 0000000..d5d1cc4 --- /dev/null +++ b/drivers/cli/unix-netplay.c @@ -0,0 +1,158 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 +#include +#include +#include + +#ifndef socklen_t +#define socklen_t int +#endif + +static int Socket=-1; +#include "main.h" +#include "unix-netplay.h" + +char *netplayhost=0; +int Port=0xFCE; +int netplay=0; + +int FCEUD_NetworkConnect(void) +{ + struct sockaddr_in sockn; + int TSocket; + + memset(&sockn,0,sizeof(sockn)); + sockn.sin_family=AF_INET; + sockn.sin_port=htons(Port); + + if((TSocket=socket(AF_INET, SOCK_STREAM, 0))<0) + { + puts("Error creating socket."); + return(0); + } + + if(netplay==1) /* Be a server. */ + { + sockn.sin_addr.s_addr=INADDR_ANY; + if(bind(TSocket, (struct sockaddr *)&sockn, sizeof(sockn))<0) + { + close(TSocket); + puts("Error binding to socket."); + return(0); + } + if(listen(TSocket, 1)<0) + { + puts("Error listening on socket."); + close(TSocket); + return(0); + } + { + socklen_t len=sizeof(sockn); + + printf("Accepting connection on port %d...\n",Port); + if((Socket=accept(TSocket,(struct sockaddr *)&sockn,&len))<0 ) + { + puts("Error accepting a connection."); + close(TSocket); + return(0); + } + close(TSocket); + } + + } + else /* Connect as a client if not a server. */ + { + struct hostent *Host; + + if((sockn.sin_addr.s_addr=inet_addr(netplayhost))==INADDR_NONE) + { + if(!(Host=gethostbyname(netplayhost))) + { + puts("Error getting network host entry."); + return(0); + } + memcpy(&sockn.sin_addr,Host->h_addr,Host->h_length); + } + printf("Attempting to connect to %s...\n",netplayhost); + if( connect(TSocket, (struct sockaddr *)&sockn, sizeof(sockn)) <0 ) + { + puts("Error connecting to remote host."); + close(TSocket); + return(0); + } + Socket=TSocket; + } + return(1); +} + +/* 0 on failure, 1 on success, -1 if it would block and blocking is not + specified. +*/ + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block) +{ + if(block) + { + int t; + uint8 temp[32]; + t=recv(Socket,temp,32,MSG_PEEK|MSG_DONTWAIT); + if(t==-1) + { + if(errno!=EAGAIN) return(0); + } + else if(t==32) + NoWaiting|=2; + else + NoWaiting&=~2; + return(recv(Socket,data,len,0)==len); + } + else + { + int t=recv(Socket,data,len,MSG_DONTWAIT); + if(t==-1) + { + if(errno==EAGAIN) // Would block + return(-1); + return(0); + } + return(1); + } +} + +/* 0 on failure, 1 on success. This function should always block. */ + +int FCEUD_NetworkSendData(uint8 *Value, uint32 len) +{ + return(send(Socket,Value,len,0)==len); +} + +void FCEUD_NetworkClose(void) +{ + if(Socket>0) + close(Socket); + Socket=-1; +} + diff --git a/drivers/cli/unix-netplay.h b/drivers/cli/unix-netplay.h new file mode 100644 index 0000000..48769f6 --- /dev/null +++ b/drivers/cli/unix-netplay.h @@ -0,0 +1,5 @@ +extern char *netplayhost; +extern int Port; +extern int FDnetplay; +#define netplay FDnetplay + diff --git a/drivers/cli/usage.h b/drivers/cli/usage.h new file mode 100644 index 0000000..425a907 --- /dev/null +++ b/drivers/cli/usage.h @@ -0,0 +1,56 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ShowUsage(char *prog) +{ +printf("\nUsage is as follows:\n%s filename\n\n",prog); +puts("Options:"); +puts(DriverUsage); +puts("-cpalette x Load a custom global palette from file x.\n\ +-ntsccol x Emulate an NTSC's TV's colors.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-pal Emulate a PAL NES.\n\ +-soundvol x Sound volume. x is an integral percentage value.\n\ +-inputx str Select device mapped to virtual input port x(1-2).\n\ + str may be: none, gamepad, zapper, powerpada, powerpadb,\n\ + arkanoid\n\ +-fcexp str Select Famicom expansion port device.\n\ + str may be: none, shadow, arkanoid, 4player, fkb\n\ +-nofs x Disables Four-Score emulation if x is 1.\n\ +-gg Enable Game Genie emulation.\n\ +-no8lim x Disables the 8 sprites per scanline limitation.\n\ + 0 = Limitation enabled.\n\ + 1 = Limitation disabled.\n\ +-subase x Save extra game data files under the base directory if enabled.\n\ + 0 = Disabled.\n\ + 1 = Enabled.\n\ +-snapname x Selects what type of file name snapshots will have.\n\ + 0 = Numeric(0.png)\n\ + 1 = File base and numeric(mario-0.png)\n\ +-nothrottle x Disable artificial speed throttling if x is non-zero.\n\ +-clipsides x Clip leftmost and rightmost 8 columns of pixels of video output.\n\ + 0 = No clipping.\n\ + 1 = Clipping.\n\ +-slstart x Set the first drawn emulated scanline. Valid values for x are\n\ + 0 through 239.\n\ +-slend x Set the last drawn emulated scanline. Valid values for x are\n\ + 0 through 239."); +} diff --git a/drivers/cli/vgatweak.c b/drivers/cli/vgatweak.c new file mode 100644 index 0000000..a04cf94 --- /dev/null +++ b/drivers/cli/vgatweak.c @@ -0,0 +1,168 @@ +/* This file is "#include"d from dos-video.c and svga-video.c */ + +typedef struct { + uint8 p; + uint8 i; + uint8 v; +} vgareg; + +vgareg v256x224_103[25] = +{ + { 0xc2, 0x0, 0xe7}, + { 0xd4, 0x0, 0x45}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x86}, + { 0xd4, 0x4, 0x3f}, + { 0xd4, 0x5, 0x10}, + { 0xd4, 0x6, 0xcd}, + { 0xd4, 0x7, 0x1f}, + { 0xd4, 0x8, 0x0}, + { 0xd4, 0x9, 0x41}, + { 0xd4, 0x10, 0xc0}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xbf}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, // + { 0xd4, 0x15, 0xe7}, + { 0xd4, 0x16, 0x06}, // + { 0xd4, 0x17, 0xa3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, // + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0}, +}; + +vgareg v256x240[25] = +{ + { 0xc2, 0x0, 0xe3}, + { 0xd4, 0x0, 0x4f}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x92}, + { 0xd4, 0x4, 0x44}, + { 0xd4, 0x5, 0x10}, + { 0xd4, 0x6, 0x0a}, + { 0xd4, 0x7, 0x3e}, + { 0xd4, 0x8, 0x00}, + { 0xd4, 0x9, 0x41}, + { 0xd4, 0x10, 0xea}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xdf}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, + { 0xd4, 0x15, 0xe7}, + { 0xd4, 0x16, 0x06}, + { 0xd4, 0x17, 0xa3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0} +}; + +vgareg v256x224S[25] = +{ + { 0xc2, 0x0, 0xe3}, + { 0xd4, 0x0, 0x5f}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x82}, + { 0xd4, 0x4, 0x4e}, + { 0xd4, 0x5, 0x96}, + { 0xd4, 0x6, 0x5}, + { 0xd4, 0x7, 0x1}, + { 0xd4, 0x8, 0x0}, + { 0xd4, 0x9, 0x40}, + { 0xd4, 0x10, 0xea}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xdf}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, + { 0xd4, 0x15, 0xe7}, + { 0xd4, 0x16, 0x0}, + { 0xd4, 0x17, 0xe3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0} +}; + +vgareg v256x256[25] = +{ + { 0xc2, 0x0, 0xe7}, + { 0xd4, 0x0, 0x5f}, + { 0xd4, 0x1, 0x3f}, + { 0xd4, 0x2, 0x40}, + { 0xd4, 0x3, 0x82}, + { 0xd4, 0x4, 0x4a}, + { 0xd4, 0x5, 0x9a}, + { 0xd4, 0x6, 0x23}, + { 0xd4, 0x7, 0xb2}, + { 0xd4, 0x8, 0x0}, + { 0xd4, 0x9, 0x61}, + { 0xd4, 0x10, 0xa}, + { 0xd4, 0x11, 0xac}, + { 0xd4, 0x12, 0xff}, + { 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40}, + { 0xd4, 0x15, 0x7}, + { 0xd4, 0x16, 0x1a}, + { 0xd4, 0x17, 0xa3}, + { 0xc4, 0x1, 0x1}, + { 0xc4, 0x4, 0xe}, + { 0xce, 0x5, 0x40}, + { 0xce, 0x6, 0x5}, + { 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x0} +}; + +vgareg v256x256S[25] = +{ + { 0xc2, 0x00, 0xe7},{ 0xd4, 0x00, 0x5F},{ 0xd4, 0x01, 0x3f}, + { 0xd4, 0x02, 0x40},{ 0xd4, 0x03, 0x82},{ 0xd4, 0x04, 0x4a}, + { 0xd4, 0x05, 0x9a},{ 0xd4, 0x06, 0x25},{ 0xd4, 0x07, 0x15}, + { 0xd4, 0x08, 0x00},{ 0xd4, 0x09, 0x60},{ 0xd4, 0x10, 0x0a}, + { 0xd4, 0x11, 0xac},{ 0xd4, 0x12, 0xff},{ 0xd4, 0x13, 0x20}, + { 0xd4, 0x14, 0x40},{ 0xd4, 0x15, 0x07},{ 0xd4, 0x16, 0x1a}, + { 0xd4, 0x17, 0xa3},{ 0xc4, 0x01, 0x01},{ 0xc4, 0x04, 0x0e}, + { 0xce, 0x05, 0x40},{ 0xce, 0x06, 0x05},{ 0xc0, 0x10, 0x41}, + { 0xc0, 0x13, 0x00} +}; + +static void VGAPortSet(vgareg R) +{ + int p,i,v; + + p=0x300|R.p; + i=R.i; + v=R.v; + + switch(p) + { + case 0x3C0: inportb(0x3DA); + outportb(0x3C0,i); + outportb(0x3C0,v); + break; + case 0x3C2: + case 0x3C3: + default: outportb(p, v); + break; + case 0x3C4: if(i==1) + { + outportw(0x3c4,0x100); + outportw(0x3c4,(v<<8)|1); + outportw(0x3c4,0x300); + break; + } + case 0x3CE: + case 0x3D4: outportw(p,i|(v<<8)); + break; + } +} + diff --git a/drivers/common/args.c b/drivers/common/args.c new file mode 100644 index 0000000..7c6c8fe --- /dev/null +++ b/drivers/common/args.c @@ -0,0 +1,91 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains code for parsing command-line */ +/* options. */ +/* */ +/****************************************************************/ + +#include +#include +#include + +#include "../../types.h" +#include "args.h" + +void ParseEA(int x, int argc, char *argv[], ARGPSTRUCT *argsps) +{ + int y=0; + + do + { + if(!argsps[y].name) + { + ParseEA(x,argc,argv,(void *)argsps[y].var); + y++; + continue; + } + if(!strcmp(argv[x],argsps[y].name)) // A match. + { + if(argsps[y].subs) + { + if((x+1)>=argc) + break; + 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 1: // String + if(argsps[y].substype&0x4000) + { + if(*(char **)argsps[y].subs) + free(*(char **)argsps[y].subs); + if(!( *(char **)argsps[y].subs=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); +} + +void ParseArguments(int argc, char *argv[], ARGPSTRUCT *argsps) +{ + int x; + + for(x=0;x +#include +#include "../../driver.h" + +static void GetString(char *s) +{ + int x; + fgets(s,256,stdin); + + for(x=0;x<256;x++) + if(s[x]=='\n') + { + s[x]=0; + break; + } +} + +/* Get unsigned 16-bit integer from stdin in hex. */ +static uint32 GetH16(unsigned int def) +{ + char buf[32]; + + fgets(buf,32,stdin); + if(buf[0]=='\n') + return(def); + if(buf[0]=='$') + sscanf(buf+1,"%04x",&def); + else + sscanf(buf,"%04x",&def); + return def; +} + +/* Get unsigned 8-bit integer from stdin in decimal. */ +static uint8 Get8(unsigned int def) +{ + char buf[32]; + + fgets(buf,32,stdin); + if(buf[0]=='\n') + return(def); + sscanf(buf,"%d",&def); + return def; +} + +static int GetYN(int def) +{ + char buf[32]; + printf("(Y/N)[%s]: ",def?"Y":"N"); + fgets(buf,32,stdin); + if(buf[0]=='y' || buf[0]=='Y') + return(1); + if(buf[0]=='n' || buf[0]=='N') + return(0); + return(def); +} + +/* +** Begin list code. +** +*/ +static int listcount; +static int listids[16]; +static int listsel; +static int mordoe; + +void BeginListShow(void) +{ + listcount=0; + listsel=-1; + mordoe=0; +} + +/* Hmm =0 for in list choices, hmm=1 for end of list choices. */ +/* Return equals 0 to continue, -1 to stop, otherwise a number. */ +int ListChoice(int hmm) +{ + char buf[32]; + + if(!hmm) + { + int num=0; + + tryagain: + printf(" <'Enter' to continue, (S)top, or enter a number.> "); + fgets(buf,32,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,32,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 { + 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) +{ + uint32 A; + int s; + + FCEUI_GetCheat(num,0,&A,0,&s); + s^=1; + FCEUI_SetCheat(num,0,-1,-1,s); + printf("Cheat for address $%04x %sabled.\n",(unsigned int)A,s?"en":"dis"); +} + +static void ModifyCheat(int num) +{ + char *name; + char buf[256]; + uint32 A; + uint8 V; + int s; + int t; + + FCEUI_GetCheat(num, &name, &A, &V, &s); + + printf("Name [%s]: ",name); + GetString(buf); + + /* 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("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,s); +} + +static void AddCheatParam(uint32 A, uint8 V) +{ + char name[256]; + + printf("Name: "); + GetString(name); + 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)) + 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 s) +{ + char tmp[512]; + int ret; + + sprintf(tmp,"%s $%04x:%03d - %s",s?"*":" ",(unsigned int)a,(unsigned int)v,name); + ret=AddToList(tmp,lid); + lid++; + return(ret); +} + +static void ListCheats(void) +{ + int which; + lid=0; + + BeginListShow(); + FCEUI_ListCheats(clistcallb); + which=EndListShow(); + if(which>=0) + { + char tmp[32]; + printf(" <(T)oggle status, (M)odify, or (D)elete this cheat.> "); + fgets(tmp,32,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) +{ + char tmp[13]; + 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); + which=EndListShow(); + if(which>=0) + AddCheatParam(which,0); + } +} + +static int ShowShortList(char *moe[], int n, int def) +{ + int x,c; + unsigned int baa; + char tmp[16]; + + red: + for(x=0;x ",def+1); + fgets(tmp,256,stdin); + if(tmp[0]=='\n') + return def; + c=tolower(tmp[0]); + baa=c-'1'; + + if(baa "); + fgets(buf,32,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)=men[c-1].action; + func(); + } + else + DoMenu(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/drivers/common/cheat.h b/drivers/common/cheat.h new file mode 100644 index 0000000..6422569 --- /dev/null +++ b/drivers/common/cheat.h @@ -0,0 +1,21 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void DoConsoleCheatConfig(void); diff --git a/drivers/common/config.c b/drivers/common/config.c new file mode 100644 index 0000000..6f87f55 --- /dev/null +++ b/drivers/common/config.c @@ -0,0 +1,151 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains routines for reading/writing the */ +/* configuration file. */ +/* */ +/****************************************************************/ + +#include +#include +#include + +#include "../../types.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; +} + +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=malloc(s))) + goto gogl; + fread(*(char **)v,1,s,fp); + continue; + } + else if(s>c || s +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../types.h" + +static int format; +static int dspfd; + +// fsize is in samples, not bytes(gets translated before ioctl()) +int InitUNIXDSPSound(int *rate, int bits, int fsize, int nfrags) +{ + int x; + + printf(" Opening /dev/dsp..."); + dspfd=open("/dev/dsp",O_WRONLY); + if(dspfd==-1) goto __disperror; + + if(!bits) goto skip16check; + x=AFMT_S16_LE; + format=0; + printf("\n Setting format to 16-bit, signed, LSB first..."); + if(ioctl(dspfd,SNDCTL_DSP_SETFMT,&x)==-1) + { + skip16check: + x=AFMT_U8; + printf("\n Setting format to 8-bit, unsigned..."); + if(ioctl(dspfd,SNDCTL_DSP_SETFMT,&x)==-1) goto __disperror; + format=1; + } + + printf("\n Setting fragment size to %d samples and number of fragments to %d...",1<65535) + { + printf(" Sample rate is out of the acceptable range(8192-65535).\n"); + close(dspfd); + return(0); + } + return 1; + __disperror: + printf("ERROR\n"); + return 0; +} + +void KillUNIXDSPSound(void) +{ + close(dspfd); +} + +static int16 MBuffer[2048]; +void WriteUNIXDSPSound(int32 *Buffer, int Count, int noblocking) +{ + int P,c; + int32 *src=Buffer; + + if(format) + { + uint8 *dest=(uint8 *)MBuffer; + for(P=Count;P;P--,dest++,src++) + *dest=(uint8)((*src)>>8)^128; + c=Count; + } + else + { + int16 *dest=MBuffer; + for(P=Count;P;P--,dest++,src++) + *dest=*src; + c=Count<<1; + } + +// noblocking=!noblocking; // speed testing + if(noblocking) + { + struct audio_buf_info ai; + if(!ioctl(dspfd,SNDCTL_DSP_GETOSPACE,&ai)) + if(ai.bytes + +#include "../../types.h" + +static uint32 CBM[3]; +static uint32 *palettetranslate=0; +static int Bpp; // BYTES per pixel + + +int InitBlitToHigh(int b, uint32 rmask, uint32 gmask, uint32 bmask) +{ + Bpp=b; + + if(Bpp<=1 || Bpp>4) + return(0); + + if(Bpp==2) + palettetranslate=malloc(65536*4); + else if(Bpp>=3) + palettetranslate=malloc(256*4); + if(!palettetranslate) + return(0); + + CBM[0]=rmask; + CBM[1]=gmask; + CBM[2]=bmask; + return(1); +} + +void KillBlitToHigh(void) +{ + free(palettetranslate); +} + +void SetPaletteBlitToHigh(uint8 *src) +{ + int cshiftr[3]; + int cshiftl[3]; + int a,x,z,y; + + cshiftl[0]=cshiftl[1]=cshiftl[2]=-1; + for(a=0;a<3;a++) + { + for(x=0,y=-1,z=0;x<32;x++) + { + if(CBM[a]&(1<>cshiftr[0])<>cshiftr[1])<>cshiftr[2])<>8)<<2)]>>cshiftr[0])<>8)<<2)+1]>>cshiftr[1])<>8)<<2)+2]>>cshiftr[2])<>1); + do + { + for(x=xr;x;x--,src++) + { + int too=xscale; + do + { + *(uint8 *)dest=*(uint8 *)src; + dest++; + } while(--too); + } + src-=xr; + dest+=pinc; + } while(--doo); + //src-=xr*(yscale-(yscale>>1)); + dest+=pitch*(yscale>>1); + + src+=xr; + } + + } + else + { + for(y=yr;y;y--,/*dest+=pinc,*/src+=272-xr) + { + int doo=yscale; + do + { + for(x=xr;x;x--,src++) + { + int too=xscale; + do + { + *(uint8 *)dest=*(uint8 *)src; + dest++; + } while(--too); + } + src-=xr; + dest+=pinc; + } while(--doo); + src+=xr; + } + } + + } + else + { + for(y=yr;y;y--,dest+=pinc,src+=272-xr) + for(x=xr;x;x-=4,dest+=4,src+=4) + *(uint32 *)dest=*(uint32 *)src; + } +} + +void Blit8ToHigh(uint8 *src, uint8 *dest, int xr, int yr, int pitch) +{ + int x,y; + int pinc; + + switch(Bpp) + { + case 4: + pinc=pitch-(xr<<2); + for(y=yr;y;y--) + { + for(x=xr;x;x--) + { + *(uint32 *)dest=palettetranslate[(uint32)*src]; + dest+=4; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 3: + pinc=pitch-(xr+xr+xr); + for(y=yr;y;y--) + { + for(x=xr;x;x--) + { + uint32 tmp; + tmp=palettetranslate[(uint32)*src]; + *(uint16*)dest=(uint16)tmp; + *&dest[2]=(uint8)(tmp>>16); + dest+=3; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 2: + pinc=pitch-(xr<<1); + for(y=yr;y;y--) + { + for(x=xr>>1;x;x--) + { + *(uint32 *)dest=palettetranslate[*(uint16 *)src]; + dest+=4; + src+=2; + } + dest+=pinc; + src+=16; + } + break; + } +} diff --git a/drivers/common/vidblit.h b/drivers/common/vidblit.h new file mode 100644 index 0000000..a84588c --- /dev/null +++ b/drivers/common/vidblit.h @@ -0,0 +1,25 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int InitBlitToHigh(int b, uint32 rmask, uint32 gmask, uint32 bmask); +void SetPaletteBlitToHigh(uint8 *src); +void KillBlitToHigh(void); +void Blit8ToHigh(uint8 *src, uint8 *dest, int xr, int yr, int pitch); +void Blit8To8(uint8 *src, uint8 *dest, int xr, int yr, int pitch, int xscale, int yscale, int efx); diff --git a/drivers/win/cheat.c b/drivers/win/cheat.c new file mode 100644 index 0000000..3b53c2f --- /dev/null +++ b/drivers/win/cheat.c @@ -0,0 +1,471 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "common.h" + +#include "cheat.h" + +static int selcheat; +static int scheatmethod=0; +static uint8 cheatval1=0; +static uint8 cheatval2=0; + +static void ConfigAddCheat(HWND wnd); + + +static uint16 StrToU16(char *s) +{ + unsigned int ret=0; + sscanf(s,"%4x",&ret); + return ret; +} + +static uint8 StrToU8(char *s) +{ + unsigned int ret=0; + sscanf(s,"%d",&ret); + return ret; +} + + +/* Need to be careful where these functions are used. */ +static char *U16ToStr(uint16 a) +{ + static char TempArray[16]; + sprintf(TempArray,"%04X",a); + return TempArray; +} + +static char *U8ToStr(uint8 a) +{ + static char TempArray[16]; + sprintf(TempArray,"%03d",a); + return TempArray; +} + + +static HWND RedoCheatsWND; +static int RedoCheatsCallB(char *name, uint32 a, uint8 v, int s) +{ + SendDlgItemMessage(RedoCheatsWND,101,LB_ADDSTRING,0,(LPARAM)(LPSTR)name); + return(1); +} + +static void RedoCheatsLB(HWND hwndDlg) +{ + SendDlgItemMessage(hwndDlg,101,LB_RESETCONTENT,0,0); + RedoCheatsWND=hwndDlg; + FCEUI_ListCheats(RedoCheatsCallB); +} + + +BOOL CALLBACK CheatsConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + RedoCheatsLB(hwndDlg); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + switch(HIWORD(wParam)) + { + case BN_CLICKED: + if(selcheat>=0) + { + if(LOWORD(wParam)==107) + FCEUI_SetCheat(selcheat,0,-1,-1,1); + else if(LOWORD(wParam)==108) + FCEUI_SetCheat(selcheat,0,-1,-1,0); + } + break; + case EN_KILLFOCUS: + if(selcheat>=0) + { + char TempArray[256]; + int32 t; + + GetDlgItemText(hwndDlg,LOWORD(wParam),TempArray,256); + switch(LOWORD(wParam)) + { + case 102:FCEUI_SetCheat(selcheat,TempArray,-1,-1,-1); + SendDlgItemMessage(hwndDlg,101,LB_INSERTSTRING,selcheat,(LPARAM)(LPCTSTR)TempArray); + SendDlgItemMessage(hwndDlg,101,LB_DELETESTRING,selcheat+1,0); + SendDlgItemMessage(hwndDlg,101,LB_SETCURSEL,selcheat,0); + break; + case 103:t=StrToU16(TempArray); + FCEUI_SetCheat(selcheat,0,t,-1,-1); + break; + case 104:t=StrToU8(TempArray); + FCEUI_SetCheat(selcheat,0,-1,t,-1); + break; + } + } + break; + } + + switch(LOWORD(wParam)) + { + case 101: + if(HIWORD(wParam)==LBN_SELCHANGE) + { + char *s; + uint32 a; + uint8 b; + int status; + + selcheat=SendDlgItemMessage(hwndDlg,101,LB_GETCURSEL,0,(LPARAM)(LPSTR)0); + if(selcheat<0) break; + + FCEUI_GetCheat(selcheat,&s,&a,&b,&status); + SetDlgItemText(hwndDlg,102,(LPTSTR)s); + SetDlgItemText(hwndDlg,103,(LPTSTR)U16ToStr(a)); + SetDlgItemText(hwndDlg,104,(LPTSTR)U8ToStr(b)); + + CheckRadioButton(hwndDlg,107,108,status?107:108); + } + break; + } + + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 106: + if(selcheat>=0) + { + FCEUI_DelCheat(selcheat); + SendDlgItemMessage(hwndDlg,101,LB_DELETESTRING,selcheat,0); + selcheat=-1; + SetDlgItemText(hwndDlg,102,(LPTSTR)""); + SetDlgItemText(hwndDlg,103,(LPTSTR)""); + SetDlgItemText(hwndDlg,104,(LPTSTR)""); + CheckRadioButton(hwndDlg,107,108,0); // Is this correct? + } + break; + case 105: + ConfigAddCheat(hwndDlg); + RedoCheatsLB(hwndDlg); + break; + case 1: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + + +void ConfigCheats(HWND hParent) +{ + if(!GI) + { + FCEUD_PrintError("You must have a game loaded before you can manipulate cheats."); + return; + } + + if(GI->type==GIT_NSF) + { + FCEUD_PrintError("Sorry, you can't cheat with NSFs."); + return; + } + + selcheat=-1; + DialogBox(fceu_hInstance,"CHEATS",hParent,CheatsConCallB); +} + + + + + + +HWND cfcallbw; + +int cfcallb(uint32 a, uint8 last, uint8 current) +{ + char temp[16]; + + sprintf(temp,"%04X:%03d:%03d",(unsigned int)a,last,current); + SendDlgItemMessage(cfcallbw,108,LB_ADDSTRING,0,(LPARAM)(LPSTR)temp); + return(1); +} + +static int scrollindex; +static int scrollnum; +static int scrollmax; + +int cfcallbinsert(uint32 a, uint8 last, uint8 current) +{ + char temp[16]; + + sprintf(temp,"%04X:%03d:%03d",(unsigned int)a,last,current); + SendDlgItemMessage(cfcallbw,108,LB_INSERTSTRING,13,(LPARAM)(LPSTR)temp); + return(1); +} + +int cfcallbinsertt(uint32 a, uint8 last, uint8 current) +{ + char temp[16]; + + sprintf(temp,"%04X:%03d:%03d",(unsigned int)a,last,current); + SendDlgItemMessage(cfcallbw,108,LB_INSERTSTRING,0,(LPARAM)(LPSTR)temp); + return(1); +} + + +void AddTheThing(HWND hwndDlg, char *s, int a, int v) +{ + if(FCEUI_AddCheat(s,a,v)) + MessageBox(hwndDlg,"Cheat Added","Cheat Added",MB_OK); +} + + +static void DoGet(void) +{ + int n=FCEUI_CheatSearchGetCount(); + int t; + scrollnum=n; + scrollindex=-32768; + + SendDlgItemMessage(cfcallbw,108,LB_RESETCONTENT,0,0); + FCEUI_CheatSearchGetRange(0,13,cfcallb); + + t=-32768+n-1-13; + if(t<-32768) + t=-32768; + scrollmax=t; + SendDlgItemMessage(cfcallbw,120,SBM_SETRANGE,-32768,t); + SendDlgItemMessage(cfcallbw,120,SBM_SETPOS,-32768,1); +} + + +BOOL CALLBACK AddCheatCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int lbfocus; + static HWND hwndLB; + cfcallbw=hwndDlg; + + + switch(uMsg) + { + case WM_VSCROLL: + if(scrollnum>13) + { + switch((int)LOWORD(wParam)) + { + case SB_TOP: + scrollindex=-32768; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + case SB_BOTTOM: + scrollindex=scrollmax; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + case SB_LINEUP: + if(scrollindex>-32768) + { + scrollindex--; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_DELETESTRING,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768,cfcallbinsertt); + } + break; + + case SB_PAGEUP: + scrollindex-=14; + if(scrollindex<-32768) scrollindex=-32768; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,13,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + + case SB_LINEDOWN: + if(scrollindexscrollmax) + scrollindex=scrollmax; + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,0,0); + FCEUI_CheatSearchGetRange(scrollindex+32768,scrollindex+32768+13,cfcallb); + break; + + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + scrollindex=(short int)HIWORD(wParam); + SendDlgItemMessage(hwndDlg,120,SBM_SETPOS,scrollindex,1); + SendDlgItemMessage(hwndDlg,108,LB_RESETCONTENT,0,0); + FCEUI_CheatSearchGetRange(32768+scrollindex,32768+scrollindex+13,cfcallb); + break; + } + + } + break; + + case WM_INITDIALOG: + SetDlgItemText(hwndDlg,110,(LPTSTR)U8ToStr(cheatval1)); + SetDlgItemText(hwndDlg,111,(LPTSTR)U8ToStr(cheatval2)); + DoGet(); + CheckRadioButton(hwndDlg,115,118,scheatmethod+115); + lbfocus=0; + hwndLB=0; + break; + + case WM_VKEYTOITEM: + if(lbfocus) + { + int real; + + real=SendDlgItemMessage(hwndDlg,108,LB_GETCURSEL,0,(LPARAM)(LPSTR)0); + switch((int)LOWORD(wParam)) + { + case VK_UP: + /* mmmm....recursive goodness */ + if(!real) + SendMessage(hwndDlg,WM_VSCROLL,SB_LINEUP,0); + return(-1); + break; + case VK_DOWN: + if(real==13) + SendMessage(hwndDlg,WM_VSCROLL,SB_LINEDOWN,0); + return(-1); + break; + case VK_PRIOR: + SendMessage(hwndDlg,WM_VSCROLL,SB_PAGEUP,0); + break; + case VK_NEXT: + SendMessage(hwndDlg,WM_VSCROLL,SB_PAGEDOWN,0); + break; + case VK_HOME: + SendMessage(hwndDlg,WM_VSCROLL,SB_TOP,0); + break; + case VK_END: + SendMessage(hwndDlg,WM_VSCROLL,SB_BOTTOM,0); + break; + } + return(-2); + } + break; + + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case 108: + switch(HIWORD(wParam)) + { + case LBN_SELCHANGE: + { + char TempArray[32]; + SendDlgItemMessage(hwndDlg,108,LB_GETTEXT,SendDlgItemMessage(hwndDlg,108,LB_GETCURSEL,0,(LPARAM)(LPSTR)0),(LPARAM)(LPCTSTR)TempArray); + TempArray[4]=0; + SetDlgItemText(hwndDlg,201,(LPTSTR)TempArray); + } + break; + case LBN_SETFOCUS: + lbfocus=1; + break; + case LBN_KILLFOCUS: + lbfocus=0; + break; + } + break; + } + + switch(HIWORD(wParam)) + { + case BN_CLICKED: + if(LOWORD(wParam)>=115 && LOWORD(wParam)<=118) + scheatmethod=LOWORD(wParam)-115; + break; + case EN_CHANGE: + { + char TempArray[256]; + GetDlgItemText(hwndDlg,LOWORD(wParam),TempArray,256); + switch(LOWORD(wParam)) + { + case 110:cheatval1=StrToU8(TempArray);break; + case 111:cheatval2=StrToU8(TempArray);break; + } + } + break; + } + + + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 112: + FCEUI_CheatSearchBegin(); + DoGet(); + break; + case 113: + FCEUI_CheatSearchEnd(scheatmethod,cheatval1,cheatval2); + DoGet(); + break; + case 114: + FCEUI_CheatSearchSetCurrentAsOriginal(); + DoGet(); + break; + case 107: + FCEUI_CheatSearchShowExcluded(); + DoGet(); + break; + case 105: + { + int a,v; + char temp[256]; + + GetDlgItemText(hwndDlg,201,temp,4+1); + a=StrToU16(temp); + GetDlgItemText(hwndDlg,202,temp,3+1); + v=StrToU8(temp); + + GetDlgItemText(hwndDlg,200,temp,256); + AddTheThing(hwndDlg,temp,a,v); + } + break; + case 106: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void ConfigAddCheat(HWND wnd) +{ + DialogBox(fceu_hInstance,"ADDCHEAT",wnd,AddCheatCallB); +} diff --git a/drivers/win/cheat.h b/drivers/win/cheat.h new file mode 100644 index 0000000..b3cd3cc --- /dev/null +++ b/drivers/win/cheat.h @@ -0,0 +1 @@ +void ConfigCheats(HWND hParent); diff --git a/drivers/win/common.h b/drivers/win/common.h new file mode 100644 index 0000000..91b8d3d --- /dev/null +++ b/drivers/win/common.h @@ -0,0 +1,20 @@ +#include +#include +#include + +#ifndef WIN32 +#define WIN32 +#endif +#undef WINNT +#define NONAMELESSUNION + +#define DIRECTSOUND_VERSION 0x0700 +#define DIRECTDRAW_VERSION 0x0700 +#define DIRECTINPUT_VERSION 0x700 +#include "../../driver.h" + +extern HWND hAppWnd; +extern HINSTANCE fceu_hInstance; + +extern int NoWaiting; +extern FCEUGI *GI; diff --git a/drivers/win/config.c b/drivers/win/config.c new file mode 100644 index 0000000..761a7a9 --- /dev/null +++ b/drivers/win/config.c @@ -0,0 +1,132 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************/ +/* FCE Ultra */ +/* */ +/* This file contains code to interface to the standard */ +/* FCE Ultra configuration file saving/loading code. */ +/* */ +/****************************************************************/ +#include "../common/config.h" + +static CFGSTRUCT fceuconfig[]={ + + ACS(rfiles[0]), + ACS(rfiles[1]), + ACS(rfiles[2]), + ACS(rfiles[3]), + ACS(rfiles[4]), + ACS(rfiles[5]), + ACS(rfiles[6]), + ACS(rfiles[7]), + ACS(rfiles[8]), + ACS(rfiles[9]), + + AC(ntsccol),AC(ntsctint),AC(ntschue), + + AC(soundsleep), + NAC("palyo",palyo), + NAC("genie",genie), + NAC("fs",fullscreen), + NAC("vgamode",vmod), + NAC("sound",soundo), + + ACS(gfsdir), + + NACS("odcheats",DOvers[0]), + NACS("odmisc",DOvers[1]), + NACS("odnonvol",DOvers[2]), + NACS("odstates",DOvers[3]), + NACS("odsnaps",DOvers[4]), + NACS("odbase",DOvers[5]), + + NAC("winsizemul",winsizemul), + + AC(soundrate), + AC(soundbuftime), + AC(soundoptions), + AC(soundvolume), + + NAC("eoptions",eoptions), + NACA("cpalette",cpalette), + + ACA(joy), + ACA(joyA),ACA(joyB),ACA(joySelect),ACA(joyStart), + + AC(joyOptions), + ACA(joyUp),ACA(joyDown),ACA(joyLeft),ACA(joyRight), + + NACA("InputType",UsrInputType), + NAC("keyben",keybEnable), + + NACA("keybm0",keyBMap[0]), + NACA("keybm1",keyBMap[1]), + NACA("keybm2",keyBMap[2]), + NACA("keybm3",keyBMap[3]), + NACA("ppasc0",powerpadsc[0]), + NACA("ppasc1",powerpadsc[1]), + + NAC("ppaside",powerpadside), + NAC("vmcx",vmodes[0].x), + NAC("vmcy",vmodes[0].y), + NAC("vmcb",vmodes[0].bpp), + NAC("vmcf",vmodes[0].flags), + NAC("vmcxs",vmodes[0].xscale), + NAC("vmcys",vmodes[0].yscale), + + NAC("srendline",srendlinen), + NAC("erendline",erendlinen), + NAC("srendlinep",srendlinep), + NAC("erendlinep",erendlinep), + + AC(UsrInputTypeFC), + AC(winsync), + AC(fssync), + AC(NoFourScore), + ACA(fkbmap), + ENDCFGSTRUCT +}; + +static void SaveConfig(char *filename) +{ + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + SaveFCEUConfig(filename,fceuconfig); +} + +static void LoadConfig(char *filename) +{ + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + LoadFCEUConfig(filename,fceuconfig); + DriverInterface(DES_NTSCCOL,&ntsccol); + DriverInterface(DES_SETNTSCTINT,&ntsctint); + DriverInterface(DES_SETNTSCHUE,&ntschue); + + palyo&=1; + FCEUI_SetVidSystem(palyo); + genie&=1; + FCEUI_SetGameGenie(genie); + fullscreen&=1; + soundo&=1; + FCEUI_SetSoundVolume(soundvolume); +} + diff --git a/drivers/win/input.c b/drivers/win/input.c new file mode 100644 index 0000000..aea3f47 --- /dev/null +++ b/drivers/win/input.c @@ -0,0 +1,288 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "common.h" +#include + +#include "input.h" +#include "keyboard.h" +#include "joystick.h" + + +int UsrInputType[2]={SI_GAMEPAD,SI_GAMEPAD}; +int UsrInputTypeFC=SIFC_NONE; +int InputType[2]; +int InputTypeFC; + +int NoFourScore=0; + +static uint32 powerpadbuf[2]; + +LPDIRECTINPUT7 lpDI; + +void FixGIGO(void) +{ + InputType[0]=UsrInputType[0]; + InputType[1]=UsrInputType[1]; + InputTypeFC=UsrInputTypeFC; + + if(GI) + { + if(GI->input[0]>=0) + InputType[0]=GI->input[0]; + if(GI->input[1]>=0) + InputType[1]=GI->input[1]; + if(GI->inputfc>=0) + InputTypeFC=GI->inputfc; + CreateInputStuff(); + } +} + +static uint32 JSreturn; +static uint32 mousedata[3]; + + +static void ConfigGamePad(HWND hParent, int port); + +int InitDInput(void) +{ + HRESULT ddrval; + + ddrval=DirectInputCreateEx(fceu_hInstance,DIRECTINPUT_VERSION,&IID_IDirectInput7,(LPVOID *)&lpDI,0); + if(ddrval!=DI_OK) + { + FCEUD_PrintError("DirectInput: Error creating DirectInput object."); + return 0; + } + return 1; +} + +static int screenmode=0; +void InputScreenChanged(int fs) +{ + int x; + for(x=0;x<2;x++) + if(InputType[x]==SI_ZAPPER) + FCEUI_SetInput(x,SI_ZAPPER,mousedata,fs); + if(InputTypeFC==SIFC_SHADOW) + FCEUI_SetInputFC(SIFC_SHADOW,mousedata,fs); + screenmode=fs; +} + +void InitInputStuff(void) +{ + KeyboardInitialize(); + InitJoysticks(hAppWnd); +} + +void CreateInputStuff(void) +{ + void *InputDPtr=0; + int x; + int TAttrib; + + for(x=0;x<2;x++) + { + TAttrib=0; + switch(InputType[x]) + { + case SI_GAMEPAD:InputDPtr=((uint8 *)&JSreturn)+(x<<1); + break; + case SI_POWERPAD:InputDPtr=&powerpadbuf[x];break; + case SI_ARKANOID:InputDPtr=mousedata;break; + case SI_ZAPPER:InputDPtr=mousedata; + TAttrib=screenmode; + break; + } + FCEUI_SetInput(x,InputType[x],InputDPtr,TAttrib); + } + + TAttrib=0; + switch(InputTypeFC) + { + case SIFC_SHADOW:InputDPtr=mousedata;TAttrib=screenmode;break; + case SIFC_ARKANOID:InputDPtr=mousedata;break; + case SIFC_FKB:InputDPtr=fkbkeys;memset(fkbkeys,0,sizeof(fkbkeys));break; + } + FCEUI_SetInputFC(InputTypeFC,InputDPtr,TAttrib); + FCEUI_DisableFourScore(NoFourScore); +} + +void DestroyInput(void) +{ + KillJoysticks(); + KeyboardClose(); +} + +void FCEUD_UpdateInput(void) +{ + int x; + uint32 JS; + int t=0; + + KeyboardUpdate(); + + for(x=0;x<2;x++) + switch(InputType[x]) + { + case SI_GAMEPAD:t|=1;break; + case SI_ARKANOID:t|=2;break; + case SI_ZAPPER:t|=2;break; + case SI_POWERPAD:powerpadbuf[x]=UpdatePPadData(x);break; + } + switch(InputTypeFC) + { + case SIFC_ARKANOID:t|=2;break; + case SIFC_SHADOW:t|=2;break; + case SIFC_FKB: if(cidisabled) UpdateFKB();break; + } + if(t&1) + { + JS=KeyboardDodo(); + if(joy[0]|joy[1]|joy[2]|joy[3]) + JS|=(uint32)GetJSOr(); + JSreturn=(JS&0xFF000000)|(JS&0xFF)|((JS&0xFF0000)>>8)|((JS&0xFF00)<<8); + } + if(t&2) + GetMouseData(&mousedata[0], &mousedata[1], &mousedata[2]); +} + + +BOOL CALLBACK InputConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static void (*boopar[5])(HWND hParent, int port)={0,ConfigGamePad,0,ConfigKeyboardiePowerpad,0}; + static void (*boopar2[5])(HWND hParent)={0,0,0,0,ConfigFKB}; + static char *strn[5]={"","Gamepad","Zapper","Power Pad","Arkanoid Paddle"}; + static char *strf[5]={"","Arkanoid Paddle","Hyper Shot gun","4-Player Adapter","Family Keyboard"}; + int x; + + switch(uMsg) { + case WM_INITDIALOG: + for(x=0;x<2;x++) + { + int y; + + for(y=0;y<5;y++) + SendDlgItemMessage(hwndDlg,104+x,CB_ADDSTRING,0,(LPARAM)(LPSTR)strn[y]); + + SendDlgItemMessage(hwndDlg,104+x,CB_SETCURSEL,UsrInputType[x],(LPARAM)(LPSTR)0); + EnableWindow(GetDlgItem(hwndDlg,106+x),boopar[InputType[x]]?1:0); + SetDlgItemText(hwndDlg,200+x,(LPTSTR)strn[InputType[x]]); + } + + + { + int y; + for(y=0;y<5;y++) + SendDlgItemMessage(hwndDlg,110,CB_ADDSTRING,0,(LPARAM)(LPSTR)strf[y]); + SendDlgItemMessage(hwndDlg,110,CB_SETCURSEL,UsrInputTypeFC,(LPARAM)(LPSTR)0); + EnableWindow(GetDlgItem(hwndDlg,111),boopar2[InputTypeFC]?1:0); + SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputTypeFC]); + } + + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(HIWORD(wParam)==CBN_SELENDOK) + { + switch(LOWORD(wParam)) + { + case 104: + case 105:UsrInputType[LOWORD(wParam)-104]=InputType[LOWORD(wParam)-104]=SendDlgItemMessage(hwndDlg,LOWORD(wParam),CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EnableWindow( GetDlgItem(hwndDlg,LOWORD(wParam)+2),boopar[InputType[LOWORD(wParam)-104]]?1:0); + SetDlgItemText(hwndDlg,200+LOWORD(wParam)-104,(LPTSTR)strn[InputType[LOWORD(wParam)-104]]); + break; + case 110:UsrInputTypeFC=InputTypeFC=SendDlgItemMessage(hwndDlg,110,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EnableWindow(GetDlgItem(hwndDlg,111),boopar2[InputTypeFC]?1:0); + SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputTypeFC]); + break; + + } + + } + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 111: + if(boopar2[InputTypeFC]) + boopar2[InputTypeFC](hwndDlg); + break; + + case 107: + case 106: + { + int t=(wParam&0xFFFF)-106; + if(boopar[InputType[t]]) + boopar[InputType[t]](hwndDlg,t); + } + break; + case 1: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +void ConfigInput(HWND hParent) +{ + DialogBox(fceu_hInstance,"INPUTCONFIG",hParent,InputConCallB); + CreateInputStuff(); +} + + +static int porttemp; + +BOOL CALLBACK GPConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + if(NoFourScore) + CheckDlgButton(hwndDlg,200,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 107:ConfigJoystickies(hwndDlg, porttemp);break; + case 106:ConfigKeyboardie(hwndDlg, porttemp);break; + case 1: + gornk: + NoFourScore=0; + if(IsDlgButtonChecked(hwndDlg,200)==BST_CHECKED) + NoFourScore=1; + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void ConfigGamePad(HWND hParent, int port) +{ + porttemp=port; + DialogBox(fceu_hInstance,"GAMEPADCONFIG",hParent,GPConCallB); +} + + diff --git a/drivers/win/input.h b/drivers/win/input.h new file mode 100644 index 0000000..559f1af --- /dev/null +++ b/drivers/win/input.h @@ -0,0 +1,25 @@ +void ConfigInput(HWND hParent); +int InitDInput(void); +void CreateInputStuff(void); +void InitInputStuff(void); +void DestroyInput(void); +void InputScreenChanged(int fs); +void FixGIGO(void); + +extern LPDIRECTINPUT7 lpDI; + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + + +extern int InputType[2]; +extern int InputTypeFC; +extern int NoFourScore; +extern int UsrInputType[2]; +extern int UsrInputTypeFC; diff --git a/drivers/win/joystick.c b/drivers/win/joystick.c new file mode 100644 index 0000000..375b7af --- /dev/null +++ b/drivers/win/joystick.c @@ -0,0 +1,407 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "common.h" +#include + +#include "input.h" +#include "joystick.h" + + +HRESULT ddrval; + +static GUID joyGUID[64]; + +static int joycounter; + +static LPDIRECTINPUTDEVICE7 lpJoy[4]={0,0,0,0}; + +int joyOptions[4]={0,0,0,0}; +int joyA[4]={1,1,1,1}; +int joyB[4]={0,0,0,0}; +int joySelect[4]={2,2,2,2}; +int joyStart[4]={3,3,3,3}; +int joyUp[4]={4,4,4,4}; +int joyDown[4]={5,5,5,5}; +int joyLeft[4]={6,6,6,6}; +int joyRight[4]={7,7,7,7}; + +int joy[4]={0,0,0,0}; + +static int JoyXMax[4]; +static int JoyXMin[4]; + +static int JoyYMax[4]; +static int JoyYMin[4]; + +static DIJOYSTATE2 JoyStatus; + +static void ShowDIJErr(int w, char *s) +{ + char tempo[128]; + sprintf(tempo,"DirectInput: Joystick %d: %s",w+1,s); + FCEUD_PrintError(tempo); +} + +static void JoyAutoRestore(HRESULT ddrval,LPDIRECTINPUTDEVICE7 lpJJoy) +{ + switch(ddrval) + { + case DIERR_INPUTLOST: + case DIERR_NOTACQUIRED: + IDirectInputDevice7_Acquire(lpJJoy); + break; + } +} + +static int GetJoystickButton(int x) +{ + int errc=0; + int z; + + if(lpJoy[x]) + { + doagaino: + if(errc>8) return(-1); + + ddrval=IDirectInputDevice7_Poll(lpJoy[x]); + if(ddrval!=DI_OK && ddrval!=DI_NOEFFECT) {JoyAutoRestore(ddrval,lpJoy[x]);errc++;goto doagaino;} + + ddrval=IDirectInputDevice7_GetDeviceState(lpJoy[x],sizeof(JoyStatus),&JoyStatus); + if(ddrval!=DI_OK) {JoyAutoRestore(ddrval,lpJoy[x]);errc++;goto doagaino;} + + for(z=0;z<128;z++) + if(JoyStatus.rgbButtons[z]&0x80) + return z; + } + return(-1); +} + +uint32 GetJSOr(void) +{ + unsigned long ret; + int x; + ret=0; + + for(x=0;x<4;x++) + { + if(lpJoy[x]) + { + + ddrval=IDirectInputDevice7_Poll(lpJoy[x]); + if(ddrval!=DI_OK && ddrval!=DI_NOEFFECT) JoyAutoRestore(ddrval,lpJoy[x]); + + ddrval=IDirectInputDevice7_GetDeviceState(lpJoy[x],sizeof(JoyStatus),&JoyStatus); + if(ddrval!=DI_OK) JoyAutoRestore(ddrval,lpJoy[x]); + + if(joyOptions[x]&1) + { + if(JoyStatus.rgbButtons[joyUp[x]&127]&0x80) ret|=JOY_UP<<(x<<3); + if(JoyStatus.rgbButtons[joyDown[x]&127]&0x80) ret|=JOY_DOWN<<(x<<3); + if(JoyStatus.rgbButtons[joyLeft[x]&127]&0x80) ret|=JOY_LEFT<<(x<<3); + if(JoyStatus.rgbButtons[joyRight[x]&127]&0x80) ret|=JOY_RIGHT<<(x<<3); + } + else + { + if(JoyStatus.lX>=JoyXMax[x]) + ret|=JOY_RIGHT<<(x<<3); + else if(JoyStatus.lX<=JoyXMin[x]) + ret|=JOY_LEFT<<(x<<3); + + if(JoyStatus.lY>=JoyYMax[x]) + ret|=JOY_DOWN<<(x<<3); + else if(JoyStatus.lY<=JoyYMin[x]) + ret|=JOY_UP<<(x<<3); + } + if(JoyStatus.rgbButtons[joyA[x]&127]&0x80) ret|=1<<(x<<3); + if(JoyStatus.rgbButtons[joyB[x]&127]&0x80) ret|=2<<(x<<3); + if(JoyStatus.rgbButtons[joySelect[x]&127]&0x80) ret|=4<<(x<<3); + if(JoyStatus.rgbButtons[joyStart[x]&127]&0x80) ret|=8<<(x<<3); + } + } + + return ret; +} + +static void KillJoystick(int w) +{ + if(lpJoy[w]) + { + IDirectInputDevice7_Unacquire(lpJoy[w]); + IDirectInputDevice7_Release(lpJoy[w]); + lpJoy[w]=0; + } +} + +void KillJoysticks(void) +{ + int x; + for(x=0;x<4;x++) + KillJoystick(x); +} + +static BOOL CALLBACK JoystickFound(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + if(joycounter<64) + { + joyGUID[joycounter]=lpddi->guidInstance; + joycounter++; + if(pvRef) + { + SendDlgItemMessage(pvRef,106,CB_ADDSTRING,0,(LPARAM)(LPSTR)lpddi->tszProductName); + SendDlgItemMessage(pvRef,112,CB_ADDSTRING,0,(LPARAM)(LPSTR)lpddi->tszProductName); + } + return DIENUM_CONTINUE; + } + else + return 0; +} + +void InitJoystick(int w, HWND wnd) +{ + if(joy[w]) + { + if(joy[w]>joycounter) + { + ShowDIJErr(w,"Not found."); + joy[w]=0; + return; + } + ddrval=IDirectInput7_CreateDeviceEx(lpDI,&joyGUID[joy[w]-1],&IID_IDirectInputDevice7,(LPVOID *)&lpJoy[w],0); + if(ddrval != DI_OK) + { + ShowDIJErr(w,"Error creating device."); + joy[w]=0; + return; + } + ddrval=IDirectInputDevice7_SetCooperativeLevel(lpJoy[w],wnd,DISCL_FOREGROUND|DISCL_NONEXCLUSIVE); + if (ddrval != DI_OK) + { + ShowDIJErr(w,"Error setting cooperative level."); + KillJoystick(w); + joy[w]=0; + return; + } + ddrval=IDirectInputDevice7_SetDataFormat(lpJoy[w],&c_dfDIJoystick2); + if (ddrval != DI_OK) + { + ShowDIJErr(w,"Error setting data format."); + KillJoystick(w); + joy[w]=0; + return; + } + + { + DIPROPRANGE diprg; + int r; + + memset(&diprg,0,sizeof(DIPROPRANGE)); + diprg.diph.dwSize=sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER); + diprg.diph.dwHow=DIPH_BYOFFSET; + diprg.diph.dwObj=DIJOFS_X; + ddrval=IDirectInputDevice7_GetProperty(lpJoy[w],DIPROP_RANGE,&diprg.diph); + if(ddrval!=DI_OK) + { + ShowDIJErr(w,"Error getting X axis range."); + joy[w]=0; + KillJoystick(w); + joy[w]=0; + return; + } + r=diprg.lMax-diprg.lMin; + JoyXMax[w]=diprg.lMax-(r>>2); + JoyXMin[w]=diprg.lMin+(r>>2); + + memset(&diprg,0,sizeof(DIPROPRANGE)); + diprg.diph.dwSize=sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER); + diprg.diph.dwHow=DIPH_BYOFFSET; + diprg.diph.dwObj=DIJOFS_Y; + ddrval=IDirectInputDevice7_GetProperty(lpJoy[w],DIPROP_RANGE,&diprg.diph); + if(ddrval!=DI_OK) + { + ShowDIJErr(w,"Error getting X axis range."); + KillJoystick(w); + joy[w]=0; + return; + } + r=diprg.lMax-diprg.lMin; + JoyYMax[w]=diprg.lMax-(r>>2); + JoyYMin[w]=diprg.lMin+(r>>2); + } + + } +} + +void InitJoysticks(HWND wnd) +{ + int x; + + joycounter=0; + IDirectInput7_EnumDevices(lpDI, DIDEVTYPE_JOYSTICK,JoystickFound,0,DIEDFL_ATTACHEDONLY); + + for(x=0;x<4;x++) + InitJoystick(x,wnd); +} + + +static int joyconport; + +static BOOL CALLBACK JoyConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + char tempo[64]; + int x; + static int bid; + + switch(uMsg) { + case WM_TIMER: + if(bid>=200 && bid<=215) + { + int z; + + /* GetJoystickButton() makes sure there is a joystick, + so we don't need to here. + */ + if(bid<=207) + { + if( (z=GetJoystickButton(joyconport))!=-1) + SetDlgItemInt(hwndDlg,bid,z,0); + } + else + { + if( (z=GetJoystickButton(2+joyconport))!=-1) + SetDlgItemInt(hwndDlg,bid,z,0); + } + } + break; + case WM_INITDIALOG: + bid=0; + SetTimer(hwndDlg,666,20,0); /* Every 20ms(50x a second).*/ + + InitJoysticks(hwndDlg); + + SendDlgItemMessage(hwndDlg,106,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + SendDlgItemMessage(hwndDlg,112,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + + sprintf(tempo,"Virtual Gamepad %d",joyconport+1); + SetDlgItemText(hwndDlg,102,tempo); + sprintf(tempo,"Virtual Gamepad %d",joyconport+3); + SetDlgItemText(hwndDlg,104,tempo); + + for(x=0;x<=2;x+=2) + { + SetDlgItemInt(hwndDlg,200+(x<<2),joySelect[x+joyconport],0); + SetDlgItemInt(hwndDlg,201+(x<<2),joyStart[x+joyconport],0); + SetDlgItemInt(hwndDlg,202+(x<<2),joyB[x+joyconport],0); + SetDlgItemInt(hwndDlg,203+(x<<2),joyA[x+joyconport],0); + + SetDlgItemInt(hwndDlg,204+(x<<2),joyUp[x+joyconport],0); + SetDlgItemInt(hwndDlg,205+(x<<2),joyDown[x+joyconport],0); + SetDlgItemInt(hwndDlg,206+(x<<2),joyLeft[x+joyconport],0); + SetDlgItemInt(hwndDlg,207+(x<<2),joyRight[x+joyconport],0); + + } + joycounter=0; + IDirectInput7_EnumDevices(lpDI, DIDEVTYPE_JOYSTICK,JoystickFound,hwndDlg,DIEDFL_ATTACHEDONLY); + + SendDlgItemMessage(hwndDlg,106,CB_SETCURSEL,joy[0+joyconport],(LPARAM)(LPSTR)0); + SendDlgItemMessage(hwndDlg,112,CB_SETCURSEL,joy[2+joyconport],(LPARAM)(LPSTR)0); + + if(joyOptions[joyconport]&1) + CheckDlgButton(hwndDlg,300,BST_CHECKED); + if(joyOptions[joyconport+2]&1) + CheckDlgButton(hwndDlg,301,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(HIWORD(wParam)==EN_SETFOCUS) + { + bid=LOWORD(wParam); + } + else if(HIWORD(wParam)==EN_KILLFOCUS) + { + bid=0; + } + else if(HIWORD(wParam)==CBN_SELENDOK) + { + switch(LOWORD(wParam)) + { + case 106: + KillJoystick(joyconport); + joy[0+(joyconport)]=SendDlgItemMessage(hwndDlg,106,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + InitJoystick(joyconport,hwndDlg); + SendDlgItemMessage(hwndDlg,106,CB_SETCURSEL,joy[0+joyconport],(LPARAM)(LPSTR)0); + break; + case 112: + KillJoystick(2+joyconport); + joy[2+(joyconport)]=SendDlgItemMessage(hwndDlg,112,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + InitJoystick(2+joyconport,hwndDlg); + SendDlgItemMessage(hwndDlg,112,CB_SETCURSEL,joy[2+joyconport],(LPARAM)(LPSTR)0); + break; + } + } + + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + + KillTimer(hwndDlg,666); + KillJoysticks(); + + for(x=0;x<=2;x+=2) + { + joySelect[x+(joyconport)]=GetDlgItemInt(hwndDlg,200+(x<<2),0,0); + joyStart[x+(joyconport)]=GetDlgItemInt(hwndDlg,201+(x<<2),0,0); + joyB[x+(joyconport)]=GetDlgItemInt(hwndDlg,202+(x<<2),0,0); + joyA[x+(joyconport)]=GetDlgItemInt(hwndDlg,203+(x<<2),0,0); + + joyUp[x+(joyconport)]=GetDlgItemInt(hwndDlg,204+(x<<2),0,0); + joyDown[x+(joyconport)]=GetDlgItemInt(hwndDlg,205+(x<<2),0,0); + joyLeft[x+(joyconport)]=GetDlgItemInt(hwndDlg,206+(x<<2),0,0); + joyRight[x+(joyconport)]=GetDlgItemInt(hwndDlg,207+(x<<2),0,0); + } + if(IsDlgButtonChecked(hwndDlg,300)==BST_CHECKED) + joyOptions[joyconport]|=1; + else + joyOptions[joyconport]&=~1; + if(IsDlgButtonChecked(hwndDlg,301)==BST_CHECKED) + joyOptions[joyconport+2]|=1; + else + joyOptions[joyconport+2]&=~1; + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +void ConfigJoystickies(HWND hParent, int port) +{ + joyconport=port; + + KillJoysticks(); + DialogBox(fceu_hInstance,"JOYCONFIG",hParent,JoyConCallB); + InitJoysticks(hAppWnd); +} + diff --git a/drivers/win/joystick.h b/drivers/win/joystick.h new file mode 100644 index 0000000..a5cdb40 --- /dev/null +++ b/drivers/win/joystick.h @@ -0,0 +1,16 @@ +void ConfigJoystickies(HWND hParent, int port); +void InitJoysticks(HWND wnd); +void KillJoysticks(void); +uint32 GetJSOr(void); + +extern int joyOptions[4]; +extern int joyA[4]; +extern int joyB[4]; +extern int joySelect[4]; +extern int joyStart[4]; +extern int joyUp[4]; +extern int joyDown[4]; +extern int joyLeft[4]; +extern int joyRight[4]; +extern int joy[4]; + diff --git a/drivers/win/keyboard.c b/drivers/win/keyboard.c new file mode 100644 index 0000000..b521081 --- /dev/null +++ b/drivers/win/keyboard.c @@ -0,0 +1,458 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "common.h" +#include + + +#include "input.h" +#include "keyboard.h" + +#include "keyscan.h" + + +HRESULT ddrval; + +static LPDIRECTINPUTDEVICE7 lpdid=0; +static int porttemp; + + +int keyBMap[4][8]={ + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT}, + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT}, + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT}, + {SCAN_LEFTALT,SCAN_LEFTCONTROL,SCAN_TAB,SCAN_ENTER,SCAN_BL_CURSORUP,SCAN_BL_CURSORDOWN,SCAN_BL_CURSORLEFT,SCAN_BL_CURSORRIGHT} + }; +int keybEnable=1; + +int powerpadside=0; +int powerpadsc[2][12]={ + { + SCAN_O,SCAN_P,SCAN_BRACKET_LEFT, + SCAN_BRACKET_RIGHT,SCAN_K,SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE, + SCAN_M,SCAN_COMMA,SCAN_PERIOD,SCAN_SLASH + }, + { + SCAN_O,SCAN_P,SCAN_BRACKET_LEFT, + SCAN_BRACKET_RIGHT,SCAN_K,SCAN_L,SCAN_SEMICOLON,SCAN_APOSTROPHE, + SCAN_M,SCAN_COMMA,SCAN_PERIOD,SCAN_SLASH + } + }; + + + + +void KeyboardClose(void) +{ + if(lpdid) IDirectInputDevice7_Unacquire(lpdid); + lpdid=0; +} + +static char keys[256]; +static void KeyboardUpdateState(void) +{ + ddrval=IDirectInputDevice7_GetDeviceState(lpdid,256,keys); + switch(ddrval) + { + case DIERR_INPUTLOST: + case DIERR_NOTACQUIRED: + IDirectInputDevice7_Acquire(lpdid); + break; + } +} + +int KeyboardInitialize(void) +{ + + if(lpdid) + return(1); + + ddrval=IDirectInput7_CreateDeviceEx(lpDI, &GUID_SysKeyboard,&IID_IDirectInputDevice7, (LPVOID *)&lpdid,0); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error creating keyboard device."); + return 0; + } + + ddrval=IDirectInputDevice7_SetCooperativeLevel(lpdid, hAppWnd,DISCL_FOREGROUND|DISCL_NONEXCLUSIVE); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error setting keyboard cooperative level."); + return 0; + } + + ddrval=IDirectInputDevice7_SetDataFormat(lpdid,&c_dfDIKeyboard); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error setting keyboard data format."); + return 0; + } + + ddrval=IDirectInputDevice7_Acquire(lpdid); + if(ddrval != DI_OK) + { + FCEUD_PrintError("DirectInput: Error acquiring keyboard."); + return 0; + } + return 1; +} + +static int DIPS=0; +static uint8 keyonce[256]; +#define KEY(__a) keys[SCAN_##__a] +#define keyonly(__a,__z) {if(KEY(__a)){if(!keyonce[SCAN_##__a]) {keyonce[SCAN_##__a]=1;__z}} else{keyonce[SCAN_##__a]=0;}} +int cidisabled=0; + +void KeyboardUpdate(void) +{ + KeyboardUpdateState(); + + if(InputTypeFC==SIFC_FKB && cidisabled) + return; + + NoWaiting&=~1; + if(KEY(GRAVE)) + NoWaiting|=1; + + if(GI) + { + if(GI->type==GIT_FDS) + { + keyonly(S,DriverInterface(DES_FDSSELECT,0);) + keyonly(I,DriverInterface(DES_FDSINSERT,0);) + keyonly(E,DriverInterface(DES_FDSEJECT,0);) + } + + if(GI->type!=GIT_NSF) + { + keyonly(F5,FCEUI_SaveState();) + keyonly(F7,FCEUI_LoadState();) + } + keyonly(F9,FCEUI_SaveSnapshot();) + + if(GI->type==GIT_VSUNI) + { + keyonly(C,DriverInterface(DES_VSUNICOIN,0);) + keyonly(V,DIPS^=1;DriverInterface(DES_VSUNITOGGLEDIPVIEW,0);) + if(!(DIPS&1)) goto DIPSless; + keyonly(1,DriverInterface(DES_VSUNIDIPSET,(void *)1);) + keyonly(2,DriverInterface(DES_VSUNIDIPSET,(void *)2);) + keyonly(3,DriverInterface(DES_VSUNIDIPSET,(void *)3);) + keyonly(4,DriverInterface(DES_VSUNIDIPSET,(void *)4);) + keyonly(5,DriverInterface(DES_VSUNIDIPSET,(void *)5);) + keyonly(6,DriverInterface(DES_VSUNIDIPSET,(void *)6);) + keyonly(7,DriverInterface(DES_VSUNIDIPSET,(void *)7);) + keyonly(8,DriverInterface(DES_VSUNIDIPSET,(void *)8);) + } + else + { + keyonly(H,DriverInterface(DES_NTSCSELHUE,0);) + keyonly(T,DriverInterface(DES_NTSCSELTINT,0);) + if(KEY(KP_MINUS) || KEY(MINUS)) DriverInterface(DES_NTSCDEC,0); + if(KEY(KP_PLUS) || KEY(EQUAL)) DriverInterface(DES_NTSCINC,0); + + DIPSless: + keyonly(0,FCEUI_SelectState(0);) + keyonly(1,FCEUI_SelectState(1);) + keyonly(2,FCEUI_SelectState(2);) + keyonly(3,FCEUI_SelectState(3);) + keyonly(4,FCEUI_SelectState(4);) + keyonly(5,FCEUI_SelectState(5);) + keyonly(6,FCEUI_SelectState(6);) + keyonly(7,FCEUI_SelectState(7);) + keyonly(8,FCEUI_SelectState(8);) + keyonly(9,FCEUI_SelectState(9);) + } + } +} + +uint32 KeyboardDodo(void) +{ + uint32 JS=0; + + + if(GI) + if(GI->type!=GIT_NSF) + { + int x,y,u,b; + + for(u=0;u<4;u++) + { + if(keybEnable&(1<=0;b--) + if(keys[tmpo[b]]) JS|=(1<>16)) + { + wParam&=0xFFFF; + if((wParam>=600 && wParam<=607) || (wParam>=610 && wParam<=617)) + inkeyloop=wParam; + else switch(wParam) + { + case 1: + gornk: + for(x=0;x<2;x++) + { + for(y=0;y<8;y++) + keyBMap[porttemp+(x<<1)][y]=GetDlgItemInt(hwndDlg,600+y+x*10,0,0); + + if(IsDlgButtonChecked(hwndDlg,320+x)==BST_CHECKED) + keybEnable|=(1<<((x<<1)+porttemp)); + else + keybEnable&=~(1<<((x<<1)+porttemp)); + } + + EndDialog(hwndDlg,0); + break; + } + } + } + return 0; +} + +static BOOL CALLBACK KeyPPConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int x; + char tempo[64]; + + switch(uMsg) { + case WM_USER+666: + if(inkeyloop) + { + SetDlgItemInt(hwndDlg,inkeyloop,lParam,0); + inkeyloop=0; + } + break; + case WM_INITDIALOG: + for(x=0;x<12;x++) + SetDlgItemInt(hwndDlg,500+x,powerpadsc[porttemp][x],0); + CheckDlgButton(hwndDlg,300+((powerpadside>>porttemp)&1),BST_CHECKED); + sprintf(tempo,"Virtual Power Pad %d",porttemp+1); + SetDlgItemText(hwndDlg,302,tempo); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + { + wParam&=0xFFFF; + if(wParam>=500 && wParam<=511) + inkeyloop=wParam; + else switch(wParam) + { + case 1: + gornk: + for(x=0;x<12;x++) + powerpadsc[porttemp][x]=GetDlgItemInt(hwndDlg,500+x,0,0); + powerpadside&=~(1<>16)) + { + wParam&=0xFFFF; + if(wParam>=300 && wParam<=371) + inkeyloop=wParam; + else switch(wParam) + { + case 1: + gornk: + EndDialog(hwndDlg,0); + break; + } + } + } + return 0; +} + +static HHOOK hHook; +static LRESULT CALLBACK FilterFunc(int nCode, WORD wParam, DWORD lParam) +{ + MSG FAR *ptrMsg; + LPARAM tmpo; + + if(nCode>=0) + { + if(nCode==MSGF_DIALOGBOX) + { + ptrMsg=(MSG FAR *)lParam; + if(ptrMsg->message==WM_KEYDOWN || ptrMsg->message==WM_SYSKEYDOWN) + { + tmpo=((ptrMsg->lParam>>16)&0x7F)|((ptrMsg->lParam>>17)&0x80); + PostMessage(GetParent(ptrMsg->hwnd),WM_USER+666,0,tmpo); + if(inkeyloop) return 1; + } + } + } + return CallNextHookEx(hHook,nCode,wParam,lParam); +} + + +void ConfigKeyboardie(HWND hParent, int port) +{ + porttemp=port; + + hHook=SetWindowsHookEx(WH_MSGFILTER,(HOOKPROC)FilterFunc,fceu_hInstance,GetCurrentThreadId()); + DialogBox(fceu_hInstance,"KEYCONFIG",hParent,KeyConCallB); + UnhookWindowsHookEx(hHook); +} + +void ConfigKeyboardiePowerpad(HWND hParent, int port) +{ + porttemp=port; + + hHook=SetWindowsHookEx(WH_MSGFILTER,(HOOKPROC)FilterFunc,fceu_hInstance,GetCurrentThreadId()); + DialogBox(fceu_hInstance,"KEYPPCONFIG",hParent,KeyPPConCallB); + UnhookWindowsHookEx(hHook); +} + +void ConfigFKB(HWND hParent) +{ + hHook=SetWindowsHookEx(WH_MSGFILTER,(HOOKPROC)FilterFunc,fceu_hInstance,GetCurrentThreadId()); + DialogBox(fceu_hInstance,"FKBCONFIG",hParent,FKBConCallB); + UnhookWindowsHookEx(hHook); +} diff --git a/drivers/win/keyboard.h b/drivers/win/keyboard.h new file mode 100644 index 0000000..f118fc6 --- /dev/null +++ b/drivers/win/keyboard.h @@ -0,0 +1,22 @@ +void KeyboardClose(void); +int KeyboardInitialize(void); +void KeyboardUpdate(void); +uint32 KeyboardDodo(void); +uint32 UpdatePPadData(int w); +void UpdateFKB(void); + + +void ConfigFKB(HWND hParent); +void ConfigKeyboardie(HWND hParent, int port); +void ConfigKeyboardiePowerpad(HWND hParent, int port); + +extern int cidisabled; + +/* Config stuff: */ +extern int keyBMap[4][8]; +extern int keybEnable; +extern int powerpadside; +extern int powerpadsc[2][12]; + +extern int fkbmap[0x48]; +extern uint8 fkbkeys[0x48]; diff --git a/drivers/win/keyscan.h b/drivers/win/keyscan.h new file mode 100644 index 0000000..689d780 --- /dev/null +++ b/drivers/win/keyscan.h @@ -0,0 +1,125 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 SCAN_GRAVE 0x29 +#define SCAN_1 0x02 +#define SCAN_2 0x03 +#define SCAN_3 0x04 +#define SCAN_4 0x05 +#define SCAN_5 0x06 +#define SCAN_6 0x07 +#define SCAN_7 0x08 +#define SCAN_8 0x09 +#define SCAN_9 0x0A +#define SCAN_0 0x0B +#define SCAN_MINUS 0x0C +#define SCAN_EQUAL 0x0D +#define SCAN_BACKSLASH 0x2B +#define SCAN_BACKSPACE 0x0E +#define SCAN_TAB 0x0F +#define SCAN_Q 0x10 +#define SCAN_W 0x11 +#define SCAN_E 0x12 +#define SCAN_R 0x13 +#define SCAN_T 0x14 +#define SCAN_Y 0x15 +#define SCAN_U 0x16 +#define SCAN_I 0x17 +#define SCAN_O 0x18 +#define SCAN_P 0x19 +#define SCAN_BRACKET_LEFT 0x1A +#define SCAN_BRACKET_RIGHT 0x1B +#define SCAN_LOWBACKSLASH 0x2B +#define SCAN_CAPSLOCK 0x3A +#define SCAN_A 0x1E +#define SCAN_S 0x1F +#define SCAN_D 0x20 +#define SCAN_F 0x21 +#define SCAN_G 0x22 +#define SCAN_H 0x23 +#define SCAN_J 0x24 +#define SCAN_K 0x25 +#define SCAN_L 0x26 +#define SCAN_SEMICOLON 0x27 +#define SCAN_APOSTROPHE 0x28 +#define SCAN_ENTER 0x1C +#define SCAN_LEFTSHIFT 0x2A +#define SCAN_Z 0x2C +#define SCAN_X 0x2D +#define SCAN_C 0x2E +#define SCAN_V 0x2F +#define SCAN_B 0x30 +#define SCAN_N 0x31 +#define SCAN_M 0x32 +#define SCAN_COMMA 0x33 +#define SCAN_PERIOD 0x34 +#define SCAN_SLASH 0x35 +#define SCAN_RIGHTSHIFT 0x36 +#define SCAN_LEFTCONTROL 0x1D +#define SCAN_LEFTALT 0x38 +#define SCAN_SPACE 0x39 + +#define SCAN_RIGHTALT (0x38|0x80) +#define SCAN_RIGHTCONTROL (0x1D|0x80) +#define SCAN_BL_INSERT (0x52|0x80) +#define SCAN_BL_DELETE (0x53|0x80) +#define SCAN_BL_CURSORLEFT (0x4B|0x80) +#define SCAN_BL_HOME (0x47|0x80) +#define SCAN_BL_END (0x4F|0x80) +#define SCAN_BL_CURSORUP (0x48|0x80) +#define SCAN_BL_CURSORDOWN (0x50|0x80) +#define SCAN_BL_PAGEUP (0x49|0x80) +#define SCAN_BL_PAGEDOWN (0x51|0x80) +#define SCAN_BL_CURSORRIGHT (0x4D|0x80) + +#define SCAN_SCROLLLOCK 0x46 +/* Keys in the key pad area. */ +#define SCAN_NUMLOCK 0x45 +#define SCAN_HOME 0x47 +#define SCAN_CURSORLEFT 0x4B +#define SCAN_END 0x4F +#define SCAN_SLASH 0x35 +#define SCAN_CURSORUP 0x48 +#define SCAN_CENTER 0x4C +#define SCAN_CURSORDOWN 0x50 +#define SCAN_INSERT 0x52 +#define SCAN_ASTERISK 0x37 +#define SCAN_PAGEUP 0x49 +#define SCAN_CURSORRIGHT 0x4D +#define SCAN_PAGEDOWN 0x51 +#define SCAN_KP_DELETE 0x53 +#define SCAN_KP_MINUS 0x4A +#define SCAN_KP_PLUS 0x4E +#define SCAN_KP_ENTER 0x1C + +#define SCAN_ESCAPE 0x01 +#define SCAN_F1 0x3B +#define SCAN_F2 0x3C +#define SCAN_F3 0x3D +#define SCAN_F4 0x3E +#define SCAN_F5 0x3F +#define SCAN_F6 0x40 +#define SCAN_F7 0x41 +#define SCAN_F8 0x42 +#define SCAN_F9 0x43 +#define SCAN_F10 0x44 +#define SCAN_F11 0x57 +#define SCAN_F12 0x58 + diff --git a/drivers/win/main.c b/drivers/win/main.c new file mode 100644 index 0000000..2faf4eb --- /dev/null +++ b/drivers/win/main.c @@ -0,0 +1,338 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include // For directories configuration dialog. + +#include "input.h" +#include "joystick.h" +#include "keyboard.h" +#include "cheat.h" + + +#define EO_BGRUN 1 + +#define EO_CPALETTE 4 +#define EO_NOSPRLIM 8 +#define EO_BSAV 16 +#define EO_FSAFTERLOAD 32 +#define EO_FOAFTERSTART 64 +#define EO_NOTHROTTLE 128 +#define EO_CLIPSIDES 256 +#define EO_SNAPNAME 512 + +/* EO_USERFORCE is something I've been playing with. + The code for it isn't finished. +*/ +#define EO_USERFORCE 1024 + + +#define VNSCLIP ((eoptions&EO_CLIPSIDES)?8:0) +#define VNSWID ((eoptions&EO_CLIPSIDES)?240:256) + +static int eoptions=EO_BGRUN; + +void ResetVideo(void); +void ShowCursorAbs(int w); +void HideFWindow(int h); +int SetMainWindowStuff(void); +int GetClientAbsRect(LPRECT lpRect); +void UpdateFCEUWindow(void); + + +HWND hAppWnd=0; +HINSTANCE fceu_hInstance; + +HRESULT ddrval; + +FCEUGI *GI=0; + +// cheats, misc, nonvol, states, snaps, base +static char *DOvers[6]={0,0,0,0,0,0}; +static char *defaultds[5]={"cheats","gameinfo","sav","fcs","snaps"}; + +static char TempArray[2048]; +static char BaseDirectory[2048]; + +void SetDirs(void) +{ + int x; + static int jlist[5]= + {FCEUIOD_CHEATS,FCEUIOD_MISC,FCEUIOD_NV,FCEUIOD_STATE,FCEUIOD_SNAPS}; + + for(x=0;x<5;x++) + FCEUI_SetDirOverride(jlist[x], DOvers[x]); + if(DOvers[5]) + FCEUI_SetBaseDirectory(DOvers[5]); + else + FCEUI_SetBaseDirectory(BaseDirectory); + FCEUI_SaveExtraDataUnderBase(eoptions&EO_BSAV); +} +/* Remove empty, unused directories. */ +void RemoveDirs(void) +{ + int x; + + for(x=0;x<5;x++) + if(!DOvers[x]) + { + sprintf(TempArray,"%s\\%s",DOvers[5]?DOvers[5]:BaseDirectory,defaultds[x]); + RemoveDirectory(TempArray); + } +} + +void CreateDirs(void) +{ + int x; + + for(x=0;x<5;x++) + if(!DOvers[x]) + { + sprintf(TempArray,"%s\\%s",DOvers[5]?DOvers[5]:BaseDirectory,defaultds[x]); + CreateDirectory(TempArray,0); + } +} + +static char *gfsdir=0; +void GetBaseDirectory(void) +{ + int x; + BaseDirectory[0]=0; + GetModuleFileName(0,(LPTSTR)BaseDirectory,2047); + + for(x=strlen(BaseDirectory);x>=0;x--) + { + if(BaseDirectory[x]=='\\' || BaseDirectory[x]=='/') + {BaseDirectory[x]=0;break;} + } +} + +static int exiting=0; +int BlockingCheck(void) +{ + MSG msg; + + while( PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE ) ) { + if( GetMessage( &msg, 0, 0, 0)>0 ) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if(exiting) return(0); + + return(1); +} + +int NoWaiting=0; +static int fullscreen=0; +static int soundflush=0; +static int soundsleep=0; +static int genie=0; +static int palyo=0; +static int windowedfailed; +static int winsizemul=1; +static int winwidth,winheight; + +static volatile int nofocus=0; +static volatile int userpause=0; + +#define SO_FORCE8BIT 1 +#define SO_SECONDARY 2 +#define SO_GFOCUS 4 +#define SO_D16VOL 8 + +static int soundrate=44100; +static int soundbuftime=46; +static int soundbufsize; +static int soundoptions=0; +static int soundvolume=100; + +static unsigned int srendline,erendline; +static unsigned int srendlinen=8; +static unsigned int erendlinen=239; +static unsigned int srendlinep=0; +static unsigned int erendlinep=239; + + +static unsigned int totallines; + +static void FixFL(void) +{ + FCEUI_GetCurrentVidSystem(&srendline,&erendline); + totallines=erendline-srendline+1; +} + +static void UpdateRendBounds(void) +{ + FCEUI_SetRenderedLines(srendlinen,erendlinen,srendlinep,erendlinep); + FixFL(); +} + +static uint8 cpalette[192]; +static int vmod=1; +static int soundo=1; +static int ntsccol=0,ntsctint,ntschue; + +void FCEUD_PrintError(char *s) +{ + if(fullscreen) ShowCursorAbs(1); + MessageBox(0,s,"FCE Ultra Error",MB_ICONERROR|MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + if(fullscreen)ShowCursorAbs(0); +} + +void ShowAboutBox(void) +{ + sprintf(TempArray,"FCE Ultra "VERSION_STRING"\n\nhttp://fceultra.sourceforge.net\n\n"__TIME__"\n"__DATE__"\n""gcc "__VERSION__); + MessageBox(hAppWnd,TempArray,"About FCE Ultra",MB_OK); +} + +void DoFCEUExit(void) +{ + exiting=1; + if(GI) + { + FCEUI_CloseGame(); + GI=0; + } +} + +static int changerecursive=0; + +#include "throttle.c" + +#include "netplay.c" +#include "sound.c" +#include "video.c" +#include "window.c" +#include "config.c" + + +int DriverInitialize(void) +{ + if(!InitializeDDraw()) + return(0); + + if(soundo) + soundo=InitSound(); + + SetVideoMode(fullscreen); + InitInputStuff(); /* Initialize DInput interfaces. */ + CreateInputStuff(); /* Create and set virtual NES/FC devices. */ + return 1; +} + +static void DriverKill(void) +{ + sprintf(TempArray,"%s/fceu.cfg",BaseDirectory); + SaveConfig(TempArray); + DestroyInput(); + ResetVideo(); + if(soundo) TrashSound(); + CloseWave(); + ByebyeWindow(); +} + + +int main(int argc,char *argv[]) +{ + char *t; + + if(!FCEUI_Initialize()) + goto doexito; + + fceu_hInstance=GetModuleHandle(0); + + GetBaseDirectory(); + + sprintf(TempArray,"%s\\fceu.cfg",BaseDirectory); + LoadConfig(TempArray); + FixGIGO(); /* Since a game doesn't have to be + loaded before the GUI can be used, make + sure the temporary input type variables + are set. + */ + + + CreateDirs(); + SetDirs(); + + DoVideoConfigFix(); + DoMiscConfigFix(); + + if(eoptions&EO_CPALETTE) + FCEUI_SetPaletteArray(cpalette); + + t=0; + if(argc>1) + t=argv[1]; + if(!t) fullscreen=0; + + CreateMainWindow(); + + if(!InitDInput()) + goto doexito; + + if(!DriverInitialize()) + goto doexito; + + InitSpeedThrottle(); + UpdateMenu(); + + if(t) + ALoad(t); + else if(eoptions&EO_FOAFTERSTART) + LoadNewGamey(hAppWnd); + + doloopy: + UpdateFCEUWindow(); + if(GI) + { + FCEUI_Emulate(); + RedrawWindow(hAppWnd,0,0,RDW_ERASE|RDW_INVALIDATE); + StopSound(); + } + Sleep(50); + if(!exiting) + goto doloopy; + + doexito: + DriverKill(); + return(0); +} + +void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count) +{ + FCEUD_BlitScreen(XBuf); + if(Count) + FCEUD_WriteSoundData(Buffer,Count); + FCEUD_UpdateInput(); +} + diff --git a/drivers/win/netplay.c b/drivers/win/netplay.c new file mode 100644 index 0000000..6c7ac45 --- /dev/null +++ b/drivers/win/netplay.c @@ -0,0 +1,415 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 char netplayhost[256]={0}; +static int netplayport=0xFCE; + +static int netplayon=0; +static int netplaytype=0; + +static HWND hwndns=0; + +static SOCKET Socket=INVALID_SOCKET; +static int wsainit=0; + +static volatile int abortnetplay=0; +static volatile int concommand=0; + +static void WSE(char *ahh) +{ + char tmp[256]; + sprintf(tmp,"Winsock: %s",ahh); + FCEUD_PrintError(tmp); +} + +int SetBlockingSock(SOCKET Socko) +{ + unsigned long t; + t=1; + if(ioctlsocket(Socko,FIONBIO,&t)) + { + WSE("Error setting socket to non-blocking mode!\n"); + return 0; + } + return 1; +} + +BOOL CALLBACK BoogaDooga(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_USER+1: + if(WSAGETASYNCERROR(lParam)) + concommand=1; + else + concommand=2; + break; + case WM_USER: + if(WSAGETSELECTEVENT(lParam)==FD_CONNECT) + { + if(WSAGETSELECTERROR(lParam)) + concommand=1; + else + concommand=2; + } + break; + case WM_INITDIALOG: + if(!netplaytype) SetDlgItemText(hwndDlg,100,(LPTSTR)"Waiting for a connection..."); + else SetDlgItemText(hwndDlg,100,(LPTSTR)"Attempting to establish a connection..."); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + abortnetplay=1; + EndDialog(hwndDlg,0); + break; + } + } + return 0; + +} + +static void CloseNSDialog(void) +{ + if(hwndns) + { + SendMessage(hwndns,WM_COMMAND,1,0); + hwndns=0; + } +} + +void CreateStatusDialog(void) +{ + hwndns=CreateDialog(fceu_hInstance,"NETSTAT",hAppWnd,BoogaDooga); +} + +void FCEUD_NetworkClose(void) +{ + CloseNSDialog(); + if(Socket!=INVALID_SOCKET) + { + closesocket(Socket); + Socket=INVALID_SOCKET; + } + if(wsainit) + { + WSACleanup(); + wsainit=0; + } + /* Make sure blocking is returned to normal once network play is stopped. */ + NoWaiting&=~2; +} + +int FCEUD_NetworkConnect(void) +{ + WSADATA WSAData; + SOCKADDR_IN sockin; /* I want to play with fighting robots. */ + SOCKET TSocket; + + if(WSAStartup(MAKEWORD(1,1),&WSAData)) + { + FCEUD_PrintError("Error initializing Windows Sockets."); + return(0); + } + wsainit=1; + concommand=abortnetplay=0; + + if( (TSocket=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) + { + WSE("Error creating socket."); + FCEUD_NetworkClose(); + return(0); + } + + memset(&sockin,0,sizeof(sockin)); + sockin.sin_family=AF_INET; + sockin.sin_port=htons(netplayport); + + if(!netplaytype) /* Act as a server. */ + { + + int sockin_len; + sockin.sin_addr.s_addr=INADDR_ANY; + + sockin_len=sizeof(sockin); + + if(bind(TSocket,(struct sockaddr *)&sockin,sizeof(sockin))==SOCKET_ERROR) + { + WSE("Error binding to socket."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + if(listen(TSocket,1)==SOCKET_ERROR) + { + WSE("Error listening on socket."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + CreateStatusDialog(); + if(!SetBlockingSock(TSocket)) + { + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + while( (Socket=accept(TSocket,(struct sockaddr *) &sockin,(int *)&sockin_len)) == + INVALID_SOCKET) + { + if(abortnetplay || WSAGetLastError()!=WSAEWOULDBLOCK) + { + if(!abortnetplay) + WSE("Error accepting connection."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + else + BlockingCheck(); + } + + if(!SetBlockingSock(Socket)) + { + FCEUD_NetworkClose(); + return(0); + } + + } + else /* We're a client... */ + { + char phostentb[MAXGETHOSTSTRUCT]; + unsigned long hadr; + + hadr=inet_addr(netplayhost); + + CreateStatusDialog(); + + if(hadr!=INADDR_NONE) + sockin.sin_addr.s_addr=hadr; + else + { + if(!WSAAsyncGetHostByName(hwndns,WM_USER+1,(const char *)netplayhost,phostentb,MAXGETHOSTSTRUCT)) + { + ghosterr: + WSE("Error getting host network information."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + while(concommand!=2) + { + BlockingCheck(); + if(concommand==1 || abortnetplay) + goto ghosterr; + } + memcpy((char *)&sockin.sin_addr,((PHOSTENT)phostentb)->h_addr,((PHOSTENT)phostentb)->h_length); + } + concommand=0; + + if(WSAAsyncSelect(TSocket,hwndns,WM_USER,FD_CONNECT|FD_CLOSE)==SOCKET_ERROR) + { + eventnoterr: + WSE("Error setting event notification on socket."); + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + if(!SetBlockingSock(TSocket)) + { + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + if(connect(TSocket,(PSOCKADDR)&sockin,sizeof(sockin))==SOCKET_ERROR) + { + if(WSAGetLastError()!=WSAEWOULDBLOCK) + { + cerrav: + WSE("Error connecting to remote host."); + + cerra: + closesocket(TSocket); + FCEUD_NetworkClose(); + return(0); + } + + while(concommand!=2) + { + BlockingCheck(); + if(abortnetplay) goto cerra; + if(concommand==1) goto cerrav; + } + } + if(WSAAsyncSelect(TSocket,hAppWnd,WM_USER,0)==SOCKET_ERROR) + goto eventnoterr; + Socket=TSocket; + + } + CloseNSDialog(); + return(1); +} + + +int FCEUD_NetworkSendData(uint8 *data, uint32 len) +{ + int erc; + + while((erc=send(Socket,data,len,0))) + { + if(erc!=SOCKET_ERROR) + { + len-=erc; + data+=erc; + if(!len) + return(1); /* All data sent. */ + + if(!BlockingCheck()) return(0); + } + else + { + if(WSAGetLastError()==WSAEWOULDBLOCK) + { + if(!BlockingCheck()) return(0); + continue; + } + return(0); + } + } + return(0); +} + +int FCEUD_NetworkRecvData(uint8 *data, uint32 len, int block) +{ + int erc; + + if(block) // TODO: Add code elsewhere to handle sound buffer underruns. + { + while((erc=recv(Socket,data,len,0))!=len) + { + if(!erc) + return(0); + if(WSAGetLastError()==WSAEWOULDBLOCK) + { + if(!BlockingCheck()) return(0); + continue; + } + return(0); + } + + { + char buf[24]; + if(recv(Socket,buf,24,MSG_PEEK)==SOCKET_ERROR) + { + if(WSAGetLastError()==WSAEWOULDBLOCK) + NoWaiting&=~2; + else + return(0); + } + else + NoWaiting|=2; /* We're the client and we're lagging behind. + disable blocking(particularly sound...) to *try* + to catch up. + */ + } + + return 1; + } + + else /* We're the server. See if there's any new data + from player 2. If not, then return(-1). + */ + { + erc=recv(Socket,data,len,0); + if(!erc) + return(0); + if(erc==SOCKET_ERROR) + { + if(WSAGetLastError()==WSAEWOULDBLOCK) + return(-1); + return(0); // Some other(bad) error occurred. + } + return(1); + } // end else to if(block) +} + +BOOL CALLBACK NetConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + + CheckDlgButton(hwndDlg,100,netplayon?BST_CHECKED:BST_UNCHECKED); + CheckRadioButton(hwndDlg,101,102,101+netplaytype); + SetDlgItemInt(hwndDlg,107,netplayport,0); + + if(netplayhost[0]) + SetDlgItemText(hwndDlg,104,netplayhost); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + + netplayport=GetDlgItemInt(hwndDlg,107,0,0); + + if(IsDlgButtonChecked(hwndDlg,100)==BST_CHECKED) + netplayon=1; + else + netplayon=0; + + if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED) + netplaytype=0; + else + netplaytype=1; + + GetDlgItemText(hwndDlg,104,netplayhost,255); + netplayhost[255]=0; + + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + + +static void ConfigNetplay(void) +{ + DialogBox(fceu_hInstance,"NETPLAYCONFIG",hAppWnd,NetConCallB); + + if(netplayon) + FCEUI_SetNetworkPlay(netplaytype+1); + else + FCEUI_SetNetworkPlay(0); +} + diff --git a/drivers/win/res.res b/drivers/win/res.res new file mode 100644 index 0000000000000000000000000000000000000000..e7e533dfab203b429a61068ef238289b0098dda8 GIT binary patch literal 27108 zcmeHQYmimdmEH$L6j}oKAR|hy7)fJ99-{a_bDMVqO(PBXNYrlV2aWymb|YYno;G95 z6zXUbK{I0wQ)3d7ni7*t#Ht?4He(#k%Sqy^imAw$A5$rROeWUMey+XtT5GSp&(#=X3ZNtr!q?38=n0CCMViQBlPCKm)%aQf6+w|f zVqV9|pZsSL4`L9ivYCnn+=CJj3n+qOPy#ZIxCh0c1jLL*Pz*{y<^b+NF(?64yv zPy%8lMNnMsR{>QNP)q?(0rle^6oV2FZcsn&K?%q-;vN)(5)d0&1jV2PWDejS6oV3w z8Nxj%1|=YNstAffiR)kyEr>x0$P{n~ia`m8oiBo7Py#ZIxCh0c1VqLZK`|%+nFF{7 z#h?UahHwvxK?#UVEP`TC;uu;4Q)5s9GKEIek9$x8BD0I27?glaBkn;lC;@RGil7*j zfXo5hgJMtuGDElr#h?VlK`Vk{P~rx#h}!ut0hvOK`f&$JKpfm6C4yvPy)h3)Q@{m;smq^A&o%^$P|W9Kkh*Z2$@ko z?m-F2G~yl{42OT;Vmz@lfNUC zZxjFVKA;2ab<>%9HtoD+(9)ty2j}beDW`vXx~4f|%y`m}F=I(FTni=h0WU2x>&J|- zrF`Va6rjfo(BlQ@@dETX?uT#>`pA!IgdJ;y9czRgYlI!c{YQQb=FN~Pf@07|ehk?0 z(e;0m1D)-)wIj5w%U+$sB@tLY+bfGvxLv<=X3}(Z4GfeN!rxM<)YaKpZV&Dc9U2@w zv~OU5iAp7=E|iviQd(12D(we;c+i^eLr)A0?A<%iRj8X?J5WM3wSe~{`{6@k+7G{; z96o$#pmx)YmUIL8A|y!arQg?p7EehB(b?Yc|Wp6{T!~#JcB}#m$_G=xG23zLyuC;8vrXr_|c>&0Jv}Y%flFL8eBK(NW57F;! zz}O34H%niNTwSIW^|smc=S8dVy%}i>;5zxY!(3 z(9R}2Wje}&AHn5OgDsJxt50LIBHE0a$;s~W)BC{jRd~{Yc5+1{o|B~C)#kB_EcU|`26bZdRYNx{Fq6;=M#7{ml3nosG!L6A zfp52XRtNa&yO%8zGBv(U(CZfUUKrzSzZE9N^W%E|n&gdGAaUFH*`&|R8#-iW%( zINh}$C4LHWLD}oKed9;Jt#7}XYca~0$7G)DF@KTLU9<;zt*thN5qVKb=jM4!!q zeV|R@3=^BG`eVPDh!L-{bH~@K{W>YdgS?K{G&QNds~<$$DYBt*wjZrtjCw>*EVpxe zi*1o;1)r|YrlTH>(P~=eCiI1ESN-t>Cgwc!lrpu<)IzT<#f(@7$*eUi$pTyB>4KEd_VODwpx`snL;4W(uBc;opmiO<^PbOcJgc`J7&WG7<6bipPup-$-=+uO zcLJfuQ@|5?Yd)>7@#rmDf;KSK=vt9&!}-WnK<}yFX)#nn3n-KQ&bRBligd~u%JnYr zsQ^i44?FR#5~B>!e=fi(ag_UP+75VMY{xo0V>yzO zeksX1o?Bi2x@?UF$Tw@v_LltUaxJR?XL^CN|ICw5%iQ@EiuX%Svff^dI6Y%}aYX3# zQKs0Zv?NGNw%X_908?Iu6w+7rnjSbkcb44Y(lr|*90B%{ek!l=PS#IFo8JUZSx?(s zwVSGoX25prgN|rM%6?k{KSZBBjT$(o=zGPmMI7Uus4MMd`}*!r)i=C%Uk!dpUqoKo z&?8xY^w_?sAKz&@Cc9n&{#y+0aNz3*k}6$#}Gq31?Vu?Nzy4cz1URW9F#<0Hyd$hJOh##vqc z<59F>BK-I$`X{6$pFWHxmiDQiwcv-h_r>Tb%iEBz zsrG&tHdN6`b&R?~x_evquuoVGphr?C|urE7oRRH@dht&qK#~ijgfIZ`|H394;hpi1@|L(B5 z0QR24)&;Q1(e-vt*9WlEp(8jB>jT&vhg})KiVoWl!03mt%!UBg>adLgY`4QU1+cpv zwmE?Pjl-@AVBc}r)dB1W4!b6Rz2>lM1K67m`)mMv-(jB%U{fOD%XI;4k-5$C?1lig z+F^|W>^g@v1+X56Z3$psblBDaw$EYB0qmO&YYAZg=&;rR_M*eu0@xc4+ZMpyc367= zI~f>l)%5}F#7MC10c^Fo*Yf(t0CpX)2T*2509zbww)r{(SiQr#0@xOZbqBDOQLF7k zPXN0py4hkk1+Z%!RtjJ%qc7Qfy(#Qj#2+t)$!4r*rCk>-hxf%DlhMVD#-ry8%*^O^ zYxdLoPqNn1)frcevXeqDDijMCeeCiu@(&k3(iqd}h=8pt$M^C+EZ2>BPgndF&3^_s zum<@h9v8~L8vfdD_;id$(QlJgm97!)g0ImDKU>!0%aF#$>13ymp4}Yl1Jajeiz@v) zX_Lm;PcUbp&MVM+t~;dtbK&);J>xp?xd;EmXVfTTI=(*_l3Z;`P#-Z9x6> zu=6>`*!b&2M3$?flbwG)QY2{Rh^5lIt&Ev*tw9d7!4vMbYoCr>RK>^Qn$Z7Nq~f@#~ZWI$$l-QWj*%5HjYy6ry>^94h{25J4CgRY4NC! zMSOHZ^!;JwTh~h;!*~>j!5n;JWQ^-P&BxU?YxL_rnF9r^#VTk1 zOxs_^2`PP?Yb=KmONqiuZ#JS3hmaJ-vr>o_+h&c@pOY|O@Fy_|6-=I1%1vs(l`$l8jj z&}I*sMc^N8h4zFqSN5I8nzO84|2TNR5<5za{oU!xP#d`2++g*P?)m5EBa^Fb`i`ql z_Kkggx>v9DJ?ZLW3@%^agh=We&gQ1;n*h7FDyj!h*uM~;YB9%^Pud=_(c0$O<{|Sn z@Tk+4@j)aa%fq?m3D}LrsIRh5_nJp74|)E8vHg*F_;GU`#-a)-x#uO9w)M#ou({^z zs6U%8a(fHYvHq(;T#9~(mZ{jF!x)RtV++hTz|{#j_vEoN@amrKE3Sr>zh;!S@?7)V zAGbUo6@_y5Yv6zTXnaIlVSIn=6wVBGpsTiZD;Wx>g5dNxMo{Fg1eE3$Bb(Krw z_MX>(_guG*yQ zs|oyL)Lt%~^Pk6@-;6mgdq)m*b9axMyFbp%L7CSF9mf5je$7_SCv*3w0qmfg*Dq!; znXmU?zMfQ`ubW_<^b-8B%)IBxGWw}{Nt(OCCqL)cqQ|X}7ri9SL8S5eV*|#k4Ra}b zCTyy?&+0aQ~YlIfr7Lcx9(z}7$uGT+YwSc}7ck-?-d zwXmPQFC*!~M^XL>D1r8>0lf|B!e>U%g=wtDa>UEwgH{e@591G&!}~^%L*d&?nf|LB z3ifgU+vE85N&q|Puvasf@bn(=bYwY9^L#itd?v;3;pFg{)YxU^@U?N?mso>yxuf+Z zW}(&%=0)?a9ie;-=h*lXd(DwyeF?RX<6s~E8E1S&-qu@BpBkJlUzxydRR7&Id7oNVBOZNoFn6n{C8%%bOPUL6?_T?v)=Uk;eJ00cu zxf_&eQI?oUoPmq0SA%;&~{Bi8V&DdWWZvWx`n|(L|_TiZd`yg{|2DrpI^HyrV zNp*6w!`=;G3mx`$0IPS{I{|D4VhgPAcLA)~VgDV#7CP*|0$9Dn-V0ze%;#)qLWliN0IPQxU*(IPE!MN&{fK$-jJWur4e;w3|K!_na`|-YY&t!krs+h}8?)j# zKj-RjPOky;t#ZbN&xjw79@pUA1N@o>uHny)ei%%C-&ttU1? z_b|A|W`ve&!~7nCtmq}`%H05cOO9#jGZB~gco*I_kaam@&dkBHWn72vhL-UsgNbTX zME0;0lhrrhf@9QP(#Uycj+w7bq6^erx<2njeNvXBVx-_=5YM`?cZ`^`#q`)D&MxPr zxd*@~2ur7zDBc~{>&(ehv%7&ZZatc{xJ3Ec0`hUVy?|=$k?`F;azS=NTkHuojabZt z%(mDzUC4c6l*JKZ8@VII_yb$U{>(1>o@zTLn~UI8Ohk)07kI{-BeB!A|HE-88)uk| zIKy+8R}vTGIXtl*GZ7Qi)9LyyEPFtXa@4x5Oo%jajO1P00(KEQzzaDCO0LLCRHGYs zsn^9UX}wr4&q*`Z!EsV6swdJlrnL>_@$7CN%2qj%C<@2Zw}GeNMELo0%`eS8R{zL5rz+dWLgzdT4j)bDOn|@iBHrqewO`t& znOiZ6!@U)o-}?!@f55%y_2_LaJ~`Vd^L%%J?_y+k#&k}JzM~FcJW}OEv;k)%B}nv> z=tj;yo*vK$rC%pf?zykdvQ!4CQz#FTUgNh+BXPuP*HX?N#(w1t?QG=ghK%_1y*Ab; z`+h1HdPaK*Vs&?6PB1RjZ^zib`!^Zbg7Q3-y%g=M=sd}wc5t$}7ILNYYptDMtd$b& zqlRiFv?SS`)^@3#nFv1JiP2w#-PyD*2&1Yfw%*6(=i z^}!|Jjl5%$_1C2x;eLumRS9L0UkB-XjWW+0FxUOt*P-{%#=y6j4Bz5x+on8M*MmcH zGC)?fjOqI~&_{mrjQb&xUH?6VJ+1Wo=B%J~>#%k5V~kBV0THshFwbdwbZ_il=&pH4 z!Lw5G#(kyN6zbPR^y*6VOLP|3CbW3+e#)`XS^IGoCEX`V5B&|fq(9>f{iHLjjQIEI zW*=A~mTLbL?dHXbMU$ z+fJWmIXr{>S@(2p`S7`D%RR&Vd*`C7mx1@KXe~KFFSN29I$v+X z`{;a^T70CmEz-9^Mee(yn?DO*>aB?TEF4#QY&YO9{S=;GqxCMKG`?}Y(b^8(S5O{p z2anpoBk@=K$c{X2B89r{>p?wi$J?W7$0~3@`8x?GEo!4P-TZ9;S11=Z+cER& zowWCtpR3t*$vTWM^&ag6cTfw~Uh`ZX-`Cd_1b1a=d1$}Ya)!KIKQm_{H|JOjJPT?O z*~{etSx!6}KdW=)?Z6@O2xfwWBZ$^D3T-&ocsYtbr> ztdt;Qo}TNXotLC!QBDFGa7OraPILjR?3z*PS<#KEU#CUqg6o^j7i@nvgU9@35X!Dz zV#}vMhc1R6BfcotY`TgrKu0l#;eQuK&#A01jrhfe{23N0uN~!0!hV*Nw+&;QZP#6p zpW9IG?Y3R%-irh)4|H6nV6S2c>blLQPxFu5&96jRm+^!P^jT`~G|bQ|Rw>HM{O z!L?J$<2WDhH!{khrvap&zeburE2miiUv6F|mHK|qL9+e+XsQ%f*WUUEP9bf@divlD%6ZZmB5GRSy+AUYTFGpo&Sk|pMr zs5)X{n=um@AEeHaHNhWp>R$bwdC}yf{$|5O$k3+f(@u_f?u43+XJ#rb6*acBZjRM* z@=mvUZPW;%)>02=qjp)VCaYrAkBr|?2Xkd5?^;ScgPN76i|F6-m#X~P8|rH6B-$(b zNNkT<8}=f7rc?Y>Z~0iF?=N+;L}%F-(UfeLoYrM7>O8To@{GR&Bz`+%H1v!&*!ozf zKSQJ|M2Yx`#cTtQ#lkWjS7;iyN!KI1Ra?_q=1exyqwOXqye>_1bfL9Ova>Ah6l=h? zkf-D=Ei-i`Pk#H6=8ROXkjv?}4b=8a%#z$8xBW@~^eFIDwy%Ms8Loq~j=g4IL~~2& z`4N_0WFso+G>mo8Ix}v;v1C8kdU{9AmK(~|w8tW&nvcG*A3BB_h0X8EXY)`Z8R?br zW3Bl}V6~Md&wu(NtX%|tNqk;T&6nT4H;?Np> zl6W+IDE1(IZf-m3=dZ%~ch}YCeG_B(I8tlu!jEeqqrvKIsbUHBoKg{%ss(RwaP6b| zQ}yu4@HEaX+sL#o)RNR{iEa2UG(>G#Z?8ga^h$5BoMHr*I)w9)BTTETzM__*^?ny~ zbxFCL&9>i6tLN%QSH?}S@4CiWg52`f#d=#BSA*^LtXoIUM|&S_I~zH9-cQu-^Ag%l|H&e^7O}5HTL%;qIGqsQ}@7x|IA6*WULyNvMdHs$ z9hK?)I9`nymhO`*2M1{(8Lc^*HK7KT6{MurT@}%h>6neK8&npC%=gUSL*m&s?vZfR zMMrTzuEvf?lXl1P^ab-%#H*BN93if0xjtbGn7UB8Ed77Mya5ka)2~Gh!=!fO`3`<1 zguk)KeeHf%x6Y+^aCVFs_pTMwzMO=h*!j)im>iOnEx5>Y&>gb83f<=S~yHSIgD4#76Ud z+sE{8)@Yq;QQI|$AyK2N4$?YLh6Pwy*2$ym!wcrO_~pqe_d9L;ojSIRaXvZuO39Md zGxfEcbEUl!@3I%ZICoBI@nkI}t9#l%`WNzcnC?94n(!^0Xz;(asc|kDXdUP3NMwNL z5Okb9CNjXY4T@>m)ls@^i6XP)Q$P|gp9F0wapfDZI?yZa@7pM(DHQNyJCIubUkjpP Q^6RfBX7}>8))^128; + } + else + { + for(P=0;P=DSBufferSize) + if(playpos=(playpos+(soundbufsize<>=1; + if(stime>=5) + Sleep(stime); + k=1; + } + } + else if(soundsleep==2) + { + int stime; + stime=writepos-(playpos+(soundbufsize<>=1; + if(stime>=2) + Sleep(stime); + } + } + BlockingCheck(); + if(!soundo || NoWaiting) return; + goto ilicpo; + } + + if(netplaytype && netplayon) + { + if(writepos<=playpos+128) + writepos=playpos+(soundbufsize<>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + soundoptions=0; + if(IsDlgButtonChecked(hwndDlg,122)==BST_CHECKED) + soundoptions|=SO_FORCE8BIT; + if(IsDlgButtonChecked(hwndDlg,123)==BST_CHECKED) + soundoptions|=SO_SECONDARY; + if(IsDlgButtonChecked(hwndDlg,124)==BST_CHECKED) + soundoptions|=SO_GFOCUS; + if(IsDlgButtonChecked(hwndDlg,126)==BST_CHECKED) + soundo=1; + else + soundo=0; + x=GetDlgItemInt(hwndDlg,200,0,0); + if(x<8192 || x>65535) + { + FCEUD_PrintError("Sample rate is out of range(8192-65535)."); + break; + } + else + soundrate=x; + + soundvolume=200-SendDlgItemMessage(hwndDlg,500,TBM_GETPOS,0,0); + FCEUI_SetSoundVolume(soundvolume); + soundsleep=SendDlgItemMessage(hwndDlg,129,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + + +void ConfigSound(void) +{ + int backo=soundo,sr=soundrate; + int so=soundoptions; + + DialogBox(fceu_hInstance,"SOUNDCONFIG",hAppWnd,SoundConCallB); + + if(((backo?1:0)!=(soundo?1:0))) + { + if(!soundo) + TrashSound(); + else + soundo=InitSound(); + } + else if(( soundoptions!=so || (sr!=soundrate)) && soundo) + { + TrashSound(); + soundo=InitSound(); + } + soundbufsize=(soundbuftime*soundrate/1000); +} + + +void StopSound(void) +{ + if(soundo) + IDirectSoundBuffer_Stop(ppbufw); +} + +#include "wave.c" diff --git a/drivers/win/throttle.c b/drivers/win/throttle.c new file mode 100644 index 0000000..1ccf271 --- /dev/null +++ b/drivers/win/throttle.c @@ -0,0 +1,73 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 uint64 tmethod,tfreq; +static uint64 desiredfps; + +static void RefreshThrottleFPS(void) +{ + desiredfps=FCEUI_GetDesiredFPS()>>8; +} + +static uint64 GetCurTime(void) +{ + if(tmethod) + { + uint64 tmp; + + /* Practically, LARGE_INTEGER and uint64 differ only by signness and name. */ + QueryPerformanceCounter((LARGE_INTEGER*)&tmp); + + return(tmp); + } + else + return((uint64)GetTickCount()); + +} + +static void InitSpeedThrottle(void) +{ + tmethod=0; + if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) + { + tmethod=1; + } + else + tfreq=1000; + tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ +} + + +static void SpeedThrottle(void) +{ + static uint64 ttime,ltime; + + waiter: + + ttime=GetCurTime(); + + + if( (ttime-ltime) < (tfreq/desiredfps) ) + goto waiter; + if( (ttime-ltime) >= (tfreq*4/desiredfps)) + ltime=ttime; + else + ltime+=tfreq/desiredfps; +} diff --git a/drivers/win/video.c b/drivers/win/video.c new file mode 100644 index 0000000..a0438a0 --- /dev/null +++ b/drivers/win/video.c @@ -0,0 +1,1174 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 int RecalcCustom(void); + +#define VF_DDSTRETCHED 1 + +#define VEF_LOSTSURFACE 1 +#define VEF____INTERNAL 2 + +#define VMDF_DXBLT 1 +#define VMDF_STRFS 2 + +typedef struct { + int x; + int y; + int bpp; + int flags; + int xscale; + int yscale; + RECT srect; + RECT drect; +} vmdef; + +// left, top, right, bottom +static vmdef vmodes[11]={ + {320,240,8,0,1,1}, //0 + {320,240,8,0,1,1}, //1 + {512,384,8,0,1,1}, //2 + {640,480,8,0,1,1}, //3 + {640,480,8,0,1,1}, //4 + {640,480,8,0,1,1}, //5 + {640,480,8,VMDF_DXBLT,2,2}, //6 + {1024,768,8,VMDF_DXBLT,4,3}, //7 + {1280,1024,8,VMDF_DXBLT,5,4}, //8 + {1600,1200,8,VMDF_DXBLT,6,5}, //9 + {800,600,8,VMDF_DXBLT|VMDF_STRFS,0,0} //10 + }; +static DDCAPS caps; +static int mustrestore=0; +static DWORD CBM[3]; + +static int bpp; +static int vflags; +static int veflags; + +int fssync=0; +int winsync=0; + +static uint32 *palettetranslate=0; + +PALETTEENTRY color_palette[256]; +static int PaletteChanged=0; + +LPDIRECTDRAWCLIPPER lpClipper=0; +LPDIRECTDRAW lpDD=0; +LPDIRECTDRAW4 lpDD4=0; +LPDIRECTDRAWPALETTE lpddpal; + +DDSURFACEDESC2 ddsd; + +DDSURFACEDESC2 ddsdback; +LPDIRECTDRAWSURFACE4 lpDDSPrimary=0; +LPDIRECTDRAWSURFACE4 lpDDSDBack=0; +LPDIRECTDRAWSURFACE4 lpDDSBack=0; + +static void ShowDDErr(char *s) +{ + char tempo[512]; + sprintf(tempo,"DirectDraw: %s",s); + FCEUD_PrintError(tempo); +} + +int RestoreDD(int w) +{ + if(w) + { + if(!lpDDSBack) return 0; + if(IDirectDrawSurface4_Restore(lpDDSBack)!=DD_OK) return 0; + } + else + { + if(!lpDDSPrimary) return 0; + if(IDirectDrawSurface4_Restore(lpDDSPrimary)!=DD_OK) return 0; + } + veflags|=1; + return 1; +} + +void FCEUD_SetPalette(unsigned char index, unsigned char r, unsigned char g, unsigned char b) +{ + color_palette[index].peRed=r; + color_palette[index].peGreen=g; + color_palette[index].peBlue=b; + PaletteChanged=1; +} + +void FCEUD_GetPalette(unsigned char i, unsigned char *r, unsigned char *g, unsigned char *b) +{ + *r=color_palette[i].peRed; + *g=color_palette[i].peGreen; + *b=color_palette[i].peBlue; +} + +int InitializeDDraw(void) +{ + ddrval = DirectDrawCreate(NULL, &lpDD, NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating DirectDraw object."); + return 0; + } + + ddrval = IDirectDraw_QueryInterface(lpDD,&IID_IDirectDraw4,(LPVOID *)&lpDD4); + IDirectDraw_Release(lpDD); + + if (ddrval != DD_OK) + { + ShowDDErr("Error querying interface."); + return 0; + } + + caps.dwSize=sizeof(caps); + if(IDirectDraw4_GetCaps(lpDD4,&caps,0)!=DD_OK) + { + ShowDDErr("Error getting capabilities."); + return 0; + } + return 1; +} + +static int GetBPP(void) +{ + DDPIXELFORMAT ddpix; + + memset(&ddpix,0,sizeof(ddpix)); + ddpix.dwSize=sizeof(ddpix); + + ddrval=IDirectDrawSurface4_GetPixelFormat(lpDDSPrimary,&ddpix); + if (ddrval != DD_OK) + { + ShowDDErr("Error getting primary surface pixel format."); + return 0; + } + + if(ddpix.dwFlags&DDPF_RGB) + { + bpp=ddpix.DUMMYUNIONNAMEN(1).dwRGBBitCount; + CBM[0]=ddpix.DUMMYUNIONNAMEN(2).dwRBitMask; + CBM[1]=ddpix.DUMMYUNIONNAMEN(3).dwGBitMask; + CBM[2]=ddpix.DUMMYUNIONNAMEN(4).dwBBitMask; + } + else + { + ShowDDErr("RGB data not valid."); + return 0; + } + if(bpp==15) bpp=16; + + return 1; +} + +static int InitBPPStuff(void) +{ + if(bpp==16) + palettetranslate=malloc(65536*4); + else if(bpp>=24) + palettetranslate=malloc(256*4); + else if(bpp==8) + { + ddrval=IDirectDraw4_CreatePalette( lpDD4, DDPCAPS_8BIT|DDPCAPS_ALLOW256|DDPCAPS_INITIALIZE,color_palette,&lpddpal,NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating palette object."); + return 0; + } + ddrval=IDirectDrawSurface4_SetPalette(lpDDSPrimary, lpddpal); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting palette object."); + return 0; + } + } + return 1; +} + +int SetVideoMode(int fs) +{ + if(!lpDD4) // DirectDraw not initialized + return(1); + + if(fs) + if(!vmod) + if(!RecalcCustom()) + return(0); + + vflags=0; + veflags=1; + PaletteChanged=1; + + ResetVideo(); + + if(!fs) + { + ShowCursorAbs(1); + windowedfailed=1; + HideFWindow(0); + + ddrval = IDirectDraw4_SetCooperativeLevel ( lpDD4, hAppWnd, DDSCL_NORMAL); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting cooperative level."); + return 1; + } + + /* Beginning */ + memset(&ddsd,0,sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsd, &lpDDSPrimary,(IUnknown FAR*)NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating primary surface."); + return 1; + } + + memset(&ddsdback,0,sizeof(ddsdback)); + ddsdback.dwSize=sizeof(ddsdback); + ddsdback.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsdback.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN; + + ddsdback.dwWidth=256; + ddsdback.dwHeight=240; + + /* If no blit hardware is present, make sure buffer is created + in system memory. + */ + if(!(caps.dwCaps&DDCAPS_BLT)) + ddsdback.ddsCaps.dwCaps|=DDSCAPS_SYSTEMMEMORY; + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsdback, &lpDDSBack, (IUnknown FAR*)NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating secondary surface."); + return 0; + } + + if(!GetBPP()) + return 0; + + if(bpp!=16 && bpp!=24 && bpp!=32) + { + ShowDDErr("Current bit depth not supported!"); + return 0; + } + + if(!InitBPPStuff()) + return 0; + + ddrval=IDirectDraw4_CreateClipper(lpDD4,0,&lpClipper,0); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating clipper."); + return 0; + } + + ddrval=IDirectDrawClipper_SetHWnd(lpClipper,0,hAppWnd); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting clipper window."); + return 0; + } + ddrval=IDirectDrawSurface4_SetClipper(lpDDSPrimary,lpClipper); + if (ddrval != DD_OK) + { + ShowDDErr("Error attaching clipper to primary surface."); + return 0; + } + + windowedfailed=0; + SetMainWindowStuff(); + } + else + { + HideFWindow(1); + + ddrval = IDirectDraw4_SetCooperativeLevel ( lpDD4, hAppWnd,DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting cooperative level."); + return 0; + } + + ddrval = IDirectDraw4_SetDisplayMode(lpDD4, vmodes[vmod].x, vmodes[vmod].y,vmodes[vmod].bpp,0,0); + if (ddrval != DD_OK) + { + ShowDDErr("Error setting display mode."); + return 0; + } + if(vmodes[vmod].flags&VMDF_DXBLT) + { + memset(&ddsdback,0,sizeof(ddsdback)); + ddsdback.dwSize=sizeof(ddsdback); + ddsdback.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsdback.ddsCaps.dwCaps= DDSCAPS_OFFSCREENPLAIN; + + ddsdback.dwWidth=256; //vmodes[vmod].srect.right; + ddsdback.dwHeight=240; //vmodes[vmod].srect.bottom; + + if(!(caps.dwCaps&DDCAPS_BLT)) + ddsdback.ddsCaps.dwCaps|=DDSCAPS_SYSTEMMEMORY; + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsdback, &lpDDSBack, (IUnknown FAR*)NULL); + if(ddrval!=DD_OK) + { + ShowDDErr("Error creating secondary surface."); + return 0; + } + } + + // create foreground surface + + memset(&ddsd,0,sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + if(fssync==2) // Double buffering. + { + ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT; + ddsd.dwBackBufferCount = 1; + ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP; + } + + ddrval = IDirectDraw4_CreateSurface ( lpDD4, &ddsd, &lpDDSPrimary,(IUnknown FAR*)NULL); + if (ddrval != DD_OK) + { + ShowDDErr("Error creating primary surface."); + return 0; + } + + if(fssync==2) + { + DDSCAPS2 tmp; + + memset(&tmp,0,sizeof(tmp)); + tmp.dwCaps=DDSCAPS_BACKBUFFER; + + if(IDirectDrawSurface4_GetAttachedSurface(lpDDSPrimary,&tmp,&lpDDSDBack)!=DD_OK) + { + ShowDDErr("Error getting attached surface."); + return 0; + } + } + + if(!GetBPP()) + return 0; + if(!InitBPPStuff()) + return 0; + + mustrestore=1; + ShowCursorAbs(0); + } + + InputScreenChanged(fs); + fullscreen=fs; + return 1; +} + +static void BlitScreenWindow(uint8 *XBuf); +static void BlitScreenFull(uint8 *XBuf); + +void FCEUD_BlitScreen(uint8 *XBuf) +{ + doagain: + + UpdateFCEUWindow(); + + if(!(eoptions&EO_BGRUN)) + while(nofocus) + { + Sleep(50); + BlockingCheck(); + } + + + /* This complex statement deserves some explanation. + Make sure this special speed throttling hasn't been disabled by the user + first. Second, we don't want to throttle the speed if the fast-forward + button is pressed down(or during certain network play conditions). + + Now, if we're at this point, we'll throttle speed if sound is disabled. + Otherwise, it gets a bit more complicated. We'll throttle speed if focus + to FCE Ultra has been lost and we're writing to the primary sound buffer + because our sound code won't block. Blocking does seem to work when + writing to a secondary buffer, so we won't throttle when a secondary + buffer is used. + */ + + if(!(eoptions&EO_NOTHROTTLE)) + if(!NoWaiting) + if(!soundo || (soundo && nofocus && !(soundoptions&SO_SECONDARY)) ) + SpeedThrottle(); + + if(fullscreen) + { + if(fssync==1 && !NoWaiting) + IDirectDraw4_WaitForVerticalBlank(lpDD4,DDWAITVB_BLOCKBEGIN,0); + + BlitScreenFull(XBuf); + } + else + { + if(winsync && !NoWaiting) + IDirectDraw4_WaitForVerticalBlank(lpDD4,DDWAITVB_BLOCKBEGIN,0); + + if(!windowedfailed) + BlitScreenWindow(XBuf); + } + if(userpause) + { + StopSound(); + Sleep(50); + BlockingCheck(); + goto doagain; + } +} + +static INLINE void BlitVidHi(uint8 *src, uint8 *dest, /*int xr,*/ int yr, int pitch) +{ + int x,y; + int pinc; + + if(!(eoptions&EO_CLIPSIDES)) + switch(bpp) + { + case 32: + + pinc=pitch-(256<<2); + for(y=yr;y;y--) + { + for(x=256;x;x--) + { + *(uint32 *)dest=palettetranslate[(uint32)*src]; + dest+=4; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 24: + pinc=pitch-(256*3); + for(y=yr;y;y--) + { + for(x=256;x;x--) + { + uint32 tmp; + tmp=palettetranslate[(uint32)*src]; + *(uint16*)dest=(uint16)tmp; + *&dest[2]=(uint8)(tmp>>16); + dest+=3; + src++; + } + dest+=pinc; + src+=16; + } + break; + + case 16: + pinc=pitch-(256<<1); + for(y=yr;y;y--) + { + for(x=256>>1;x;x--) + { + *(unsigned long *)dest=palettetranslate[*(unsigned short *)src]; + dest+=4; + src+=2; + } + dest+=pinc; + src+=16; + } + break; + } + else + switch(bpp) + { + case 32: + + pinc=pitch-(240<<2); + for(y=yr;y;y--) + { + for(x=240;x;x--) + { + *(uint32 *)dest=palettetranslate[(uint32)*src]; + dest+=4; + src++; + } + dest+=pinc; + src+=32; + } + break; + + case 24: + pinc=pitch-(240*3); + for(y=yr;y;y--) + { + for(x=240;x;x--) + { + uint32 tmp; + tmp=palettetranslate[(uint32)*src]; + *(uint16*)dest=(uint16)tmp; + *&dest[2]=(uint8)(tmp>>16); + dest+=3; + src++; + } + dest+=pinc; + src+=32; + } + break; + case 16: + pinc=pitch-(240<<1); + for(y=yr;y;y--) + { + for(x=240>>1;x;x--) + { + *(unsigned long *)dest=palettetranslate[*(unsigned short *)src]; + dest+=4; + src+=2; + } + dest+=pinc; + src+=32; + } + break; + } +} + +static INLINE void FixPaletteHi(void) +{ + int x; + + switch(bpp) + { + case 16:{ + int cshiftr[3]; + int cshiftl[3]; + int a,x,z,y; + + cshiftl[0]=cshiftl[1]=cshiftl[2]=-1; + for(a=0;a<3;a++) + { + for(x=0,y=-1,z=0;x<16;x++) + { + if(CBM[a]&(1<>cshiftr[0])<>cshiftr[1])<>cshiftr[2])<>8].peRed>>cshiftr[0])<>8].peGreen>>cshiftr[1])<>8].peBlue>>cshiftr[2])<=16) + FixPaletteHi(); + else + for(x=0;x<=0x80;x+=0x80) + { + ddrval=IDirectDrawPalette_SetEntries(lpddpal,0,0x80^x,128,&color_palette[x]); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) RestoreDD(0); + return; + } + } + PaletteChanged=0; + } + + if(vmodes[vmod].flags&VMDF_DXBLT) + { + ddrval=IDirectDrawSurface4_Lock(lpDDSBack,NULL,&ddsdback, 0, NULL); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) RestoreDD(1); + return; + } + ScreenLoc=ddsdback.lpSurface; + pitch=ddsdback.DUMMYUNIONNAMEN(1).lPitch; + + srect.top=0; + srect.left=0; + srect.right=VNSWID; + srect.bottom=totallines; + if(vmodes[vmod].flags&VMDF_STRFS) + { + drect.top=0; + drect.left=0; + drect.right=vmodes[vmod].x; + drect.bottom=vmodes[vmod].y; + } + else + { + drect.top=(vmodes[vmod].y-(totallines*vmodes[vmod].yscale))>>1; + drect.bottom=drect.top+(totallines*vmodes[vmod].yscale); + drect.left=(vmodes[vmod].x-VNSWID*vmodes[vmod].xscale)>>1; + drect.right=drect.left+VNSWID*vmodes[vmod].xscale; + } + } + else + { + ddrval=IDirectDrawSurface4_Lock(lpDDSVPrimary,NULL,&ddsd, 0, NULL); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) RestoreDD(0); + return; + } + + ScreenLoc=ddsd.lpSurface; + pitch=ddsd.DUMMYUNIONNAMEN(1).lPitch; + } + + if(veflags&1) + { + if(vmodes[vmod].flags&VMDF_DXBLT) + { + veflags|=2; + memset((char *)ScreenLoc,0,pitch*srect.bottom); + } + else + { + memset((char *)ScreenLoc,0,pitch*vmodes[vmod].y); + } + PaletteChanged=1; + veflags&=~1; + } + + if(vmod==5) + { + if(eoptions&EO_CLIPSIDES) + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "akoop1:\n\t" + "movb $120,%%al \n\t" + "akoop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne akoop2\n\t" + "addl $32,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne akoop1\n\t" + : + : "S" (XBuf+srendline*272+VNSCLIP), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-(VNSWID<<1))/2),"b" (totallines), "c" ((pitch-VNSWID)<<1) + : "%al", "%edx", "%cc" ); + } + else + { + asm volatile( + "xorl %%edx, %%edx\n\t" + "koop1:\n\t" + "movb $128,%%al \n\t" + "koop2:\n\t" + "movb 1(%%esi),%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "xorl $0x00800080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne koop2\n\t" + "addl $16,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne koop1\n\t" + : + : "S" (XBuf+srendline*272), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-512)/2),"b" (totallines), "c" (pitch-512+pitch) + : "%al", "%edx", "%cc" ); + } + } + else if(vmod==4) + { + if(eoptions&EO_CLIPSIDES) + { + asm volatile( + "ayoop1:\n\t" + "movb $120,%%al \n\t" + "ayoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne ayoop2\n\t" + "addl $32,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne ayoop1\n\t" + : + : "S" (XBuf+srendline*272+VNSCLIP), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-(VNSWID<<1))/2),"b" (totallines), "c" ((pitch-VNSWID)<<1) + : "%al", "%edx", "%cc" ); + } + else + { + asm volatile( + "yoop1:\n\t" + "movb $128,%%al \n\t" + "yoop2:\n\t" + "movb 1(%%esi),%%dh\n\t" + "movb %%dh,%%dl\n\t" + "shl $16,%%edx\n\t" + "movb (%%esi),%%dl\n\t" + "movb %%dl,%%dh\n\t" // Ugh + "xorl $0x80808080,%%edx\n\t" + "movl %%edx,(%%edi)\n\t" + "addl $2,%%esi\n\t" + "addl $4,%%edi\n\t" + "decb %%al\n\t" + "jne yoop2\n\t" + "addl $16,%%esi\n\t" + "addl %%ecx,%%edi\n\t" + "decb %%bl\n\t" + "jne yoop1\n\t" + : + : "S" (XBuf+srendline*272), "D" (ScreenLoc+((240-totallines)/2)*pitch+(640-512)/2),"b" (totallines), "c" (pitch-512+pitch) + : "%al", "%edx", "%cc" ); + } + } + else + { + if(!(vmodes[vmod].flags&VMDF_DXBLT)) + { + ScreenLoc+=((vmodes[vmod].x-VNSWID)>>1)*(bpp>>3)+(((vmodes[vmod].y-totallines)>>1))*pitch; + } + if(bpp>=16) + { + BlitVidHi(XBuf+srendline*272+VNSCLIP, ScreenLoc, /*VNSWID,*/ totallines, pitch); + } + else + { + XBuf+=srendline*272+VNSCLIP; + if(eoptions&EO_CLIPSIDES) + { + for(y=totallines;y;y--) + { + for(x=60;x;x--) + { + *(long *)ScreenLoc=(*(long *)XBuf)^0x80808080; + ScreenLoc+=4; + XBuf+=4; + } + ScreenLoc+=pitch-240; + XBuf+=32; + } + } + else + { + for(y=totallines;y;y--) + { + for(x=64;x;x--) + { + *(long *)ScreenLoc=(*(long *)XBuf)^0x80808080; + ScreenLoc+=4; + XBuf+=4; + } + ScreenLoc+=pitch-256; + XBuf+=16; + } + } + } + } + + if(vmodes[vmod].flags&VMDF_DXBLT) + { + IDirectDrawSurface4_Unlock(lpDDSBack, NULL); + + if(veflags&2) + { + if(IDirectDrawSurface4_Lock(lpDDSVPrimary,NULL,&ddsd, 0, NULL)==DD_OK) + { + memset(ddsd.lpSurface,0,ddsd.DUMMYUNIONNAMEN(1).lPitch*vmodes[vmod].y); + IDirectDrawSurface4_Unlock(lpDDSVPrimary, NULL); + veflags&=~2; + } + } + + + if(IDirectDrawSurface4_Blt(lpDDSVPrimary, &drect,lpDDSBack,&srect,DDBLT_ASYNC,0)!=DD_OK) + { + ddrval=IDirectDrawSurface4_Blt(lpDDSVPrimary, &drect,lpDDSBack,&srect,DDBLT_WAIT,0); + if(ddrval!=DD_OK) + { + if(ddrval==DDERR_SURFACELOST) + { + RestoreDD(0); + RestoreDD(1); + } + return; + } + + } + } + else + IDirectDrawSurface4_Unlock(lpDDSVPrimary, NULL); + if(fssync==2) + { + IDirectDrawSurface4_Flip(lpDDSPrimary,0,0); + + } +} + +void ResetVideo(void) +{ + ShowCursorAbs(1); + if(palettetranslate) {free(palettetranslate);palettetranslate=0;} + if(lpDD4) + if(mustrestore) + {IDirectDraw4_RestoreDisplayMode(lpDD4);mustrestore=0;} + if(lpDDSBack) {IDirectDrawSurface4_Release(lpDDSBack);lpDDSBack=0;} + if(lpDDSPrimary) {IDirectDrawSurface4_Release(lpDDSPrimary);lpDDSPrimary=0;} + if(lpClipper) {IDirectDrawClipper_Release(lpClipper);lpClipper=0;} +} + +static int RecalcCustom(void) +{ + vmodes[0].flags&=~VMDF_DXBLT; + + if(vmodes[0].flags&VMDF_STRFS) + { + vmodes[0].flags|=VMDF_DXBLT; + + vmodes[0].srect.top=srendline; + vmodes[0].srect.left=VNSCLIP; + vmodes[0].srect.right=256-VNSCLIP; + vmodes[0].srect.bottom=erendline+1; + + vmodes[0].drect.top=vmodes[0].drect.left=0; + vmodes[0].drect.right=vmodes[0].x; + vmodes[0].drect.bottom=vmodes[0].y; + } + else if(vmodes[0].xscale!=1 || vmodes[0].yscale!=1) + { + vmodes[0].flags|=VMDF_DXBLT; + if(VNSWID*vmodes[0].xscale>vmodes[0].x) + { + FCEUD_PrintError("Scaled width is out of range. Reverting to no horizontal scaling."); + vmodes[0].xscale=1; + } + if(totallines*vmodes[0].yscale>vmodes[0].y) + { + FCEUD_PrintError("Scaled height is out of range. Reverting to no vertical scaling."); + vmodes[0].yscale=1; + } + + vmodes[0].srect.left=VNSCLIP; + vmodes[0].srect.top=srendline; + vmodes[0].srect.right=256-VNSCLIP; + vmodes[0].srect.bottom=erendline+1; + + vmodes[0].drect.top=(vmodes[0].y-(totallines*vmodes[0].yscale))>>1; + vmodes[0].drect.bottom=vmodes[0].drect.top+totallines*vmodes[0].yscale; + + vmodes[0].drect.left=(vmodes[0].x-(VNSWID*vmodes[0].xscale))>>1; + vmodes[0].drect.right=vmodes[0].drect.left+VNSWID*vmodes[0].xscale; + } + + if(vmodes[0].x>3)-1,(LPARAM)(LPSTR)0); + + SetDlgItemInt(hwndDlg,200,vmodes[0].x,0); + SetDlgItemInt(hwndDlg,201,vmodes[0].y,0); + + SetDlgItemInt(hwndDlg,302,vmodes[0].xscale,0); + SetDlgItemInt(hwndDlg,303,vmodes[0].yscale,0); + CheckRadioButton(hwndDlg,300,301,(vmodes[0].flags&VMDF_STRFS)?301:300); + if(eoptions&EO_FSAFTERLOAD) + CheckDlgButton(hwndDlg,102,BST_CHECKED); + + if(eoptions&EO_CLIPSIDES) + CheckDlgButton(hwndDlg,106,BST_CHECKED); + + SetDlgItemInt(hwndDlg,500,srendlinen,0); + SetDlgItemInt(hwndDlg,501,erendlinen,0); + + SetDlgItemInt(hwndDlg,502,srendlinep,0); + SetDlgItemInt(hwndDlg,503,erendlinep,0); + + + SetDlgItemInt(hwndDlg,103,winsizemul,0); + + SendDlgItemMessage(hwndDlg,104,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)""); + + SendDlgItemMessage(hwndDlg,104,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Wait for VBlank"); + SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Wait for VBlank"); + + SendDlgItemMessage(hwndDlg,105,CB_ADDSTRING,0,(LPARAM)(LPSTR)"Double Buffering"); + + SendDlgItemMessage(hwndDlg,104,CB_SETCURSEL,winsync,(LPARAM)(LPSTR)0); + SendDlgItemMessage(hwndDlg,105,CB_SETCURSEL,fssync,(LPARAM)(LPSTR)0); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + + if(IsDlgButtonChecked(hwndDlg,106)==BST_CHECKED) + eoptions|=EO_CLIPSIDES; + else + eoptions&=~EO_CLIPSIDES; + + srendlinen=GetDlgItemInt(hwndDlg,500,0,0); + erendlinen=GetDlgItemInt(hwndDlg,501,0,0); + srendlinep=GetDlgItemInt(hwndDlg,502,0,0); + erendlinep=GetDlgItemInt(hwndDlg,503,0,0); + + + if(erendlinen>239) erendlinen=239; + if(srendlinen>erendlinen) srendlinen=erendlinen; + + if(erendlinep>239) erendlinep=239; + if(srendlinep>erendlinen) srendlinep=erendlinep; + + UpdateRendBounds(); + + if(IsDlgButtonChecked(hwndDlg,301)==BST_CHECKED) + vmodes[0].flags|=VMDF_STRFS; + else + vmodes[0].flags&=~VMDF_STRFS; + + vmod=SendDlgItemMessage(hwndDlg,100,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + vmodes[0].x=GetDlgItemInt(hwndDlg,200,0,0); + vmodes[0].y=GetDlgItemInt(hwndDlg,201,0,0); + vmodes[0].bpp=(SendDlgItemMessage(hwndDlg,202,CB_GETCURSEL,0,(LPARAM)(LPSTR)0)+1)<<3; + + vmodes[0].xscale=GetDlgItemInt(hwndDlg,302,0,0); + vmodes[0].yscale=GetDlgItemInt(hwndDlg,303,0,0); + + if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED) + fullscreen=1; + else + fullscreen=0; + if(IsDlgButtonChecked(hwndDlg,102)==BST_CHECKED) + eoptions|=EO_FSAFTERLOAD; + else + eoptions&=~EO_FSAFTERLOAD; + + { + int t=GetDlgItemInt(hwndDlg,103,0,0); + if(t>0 && t<60) + winsizemul=t; + } + winsync=SendDlgItemMessage(hwndDlg,104,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + fssync=SendDlgItemMessage(hwndDlg,105,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void SetFSVideoMode(void) +{ + changerecursive=1; + if(!SetVideoMode(1)) + SetVideoMode(0); + changerecursive=0; +} + + + +void ConfigVideo(void) +{ + DialogBox(fceu_hInstance,"VIDEOCONFIG",hAppWnd,VideoConCallB); + UpdateRendBounds(); + if(fullscreen) + SetFSVideoMode(); + else + SetMainWindowStuff(); +} + +void DoVideoConfigFix(void) +{ + UpdateRendBounds(); +} + + +#ifdef moo + if(!vmod) + { + if(vmodes[0].x>8)&0xFF,soundlog); + fputc((s>>16)&0xFF,soundlog); + fputc((s>>24)&0xFF,soundlog); + + fseek(soundlog,0x28,SEEK_SET); + s=wsize; + fputc(s&0xFF,soundlog); + fputc((s>>8)&0xFF,soundlog); + fputc((s>>16)&0xFF,soundlog); + fputc((s>>24)&0xFF,soundlog); + + fclose(soundlog); + soundlog=0; + return 1; +} +int WriteWaveHeader(FILE *fp) +{ + int r; + fputs("RIFF",fp); + fseek(fp,4,SEEK_CUR); // Skip size + fputs("WAVEfmt ",fp); + fputc(0x10,fp); + fputc(0,fp); + fputc(0,fp); + fputc(0,fp); + + fputc(1,fp); // PCM + fputc(0,fp); + fputc(1,fp); // Monophonic + fputc(0,fp); + + r=44100; + fputc(r&0xFF,fp); + fputc((r>>8)&0xFF,fp); + fputc((r>>16)&0xFF,fp); + fputc((r>>24)&0xFF,fp); + r<<=1; + fputc(r&0xFF,fp); + fputc((r>>8)&0xFF,fp); + fputc((r>>16)&0xFF,fp); + fputc((r>>24)&0xFF,fp); + fputc(2,fp); + fputc(0,fp); + fputc(16,fp); + fputc(0,fp); + + fputs("data",fp); + fseek(fp,4,SEEK_CUR); + + return 1; +} + +int StartSoundLog(char *str) +{ + wsize=0; + soundlog=fopen(str,"wb"); + if(soundlog) + return WriteWaveHeader(soundlog); + else + return 0; +} +int CreateSoundSave(void) +{ + const char filter[]="MS WAVE(*.wav)\0*.wav\0"; + char nameo[2048]; + OPENFILENAME ofn; + + if(soundlog) + { + CloseWave(); + return 0; + } + + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize=sizeof(ofn); + ofn.hInstance=fceu_hInstance; + ofn.lpstrTitle="Log Sound As..."; + ofn.lpstrFilter=filter; + nameo[0]=0; + ofn.lpstrFile=nameo; + ofn.nMaxFile=256; + ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; + if(GetSaveFileName(&ofn)) + return StartSoundLog(nameo); + return 0; +} diff --git a/drivers/win/window.c b/drivers/win/window.c new file mode 100644 index 0000000..6ccd4e0 --- /dev/null +++ b/drivers/win/window.c @@ -0,0 +1,855 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 ConfigMisc(void); +static void ConfigPalette(void); +static void ConfigDirectories(void); + +static HMENU fceumenu=0; +static HMENU recentmenu; + +static int tog=0; + +void ShowCursorAbs(int w) +{ + static int stat=0; + if(w) + { + if(stat==-1) {stat++; ShowCursor(1);} + } + else + { + if(stat==0) {stat--; ShowCursor(0);} + } +} + + +RECT *CalcWindowSize(void) +{ + static RECT al; + al.left=0; + al.right=VNSWID*winsizemul; + al.top=0; + al.bottom=totallines*winsizemul; + + AdjustWindowRectEx(&al,GetWindowLong(hAppWnd,GWL_STYLE),GetMenu(hAppWnd)!=NULL,GetWindowLong(hAppWnd,GWL_EXSTYLE)); + + al.right-=al.left; + al.left=0; + al.bottom-=al.top; + al.top=0; + + return(&al); +} + +void UpdateMenu(void) +{ + static int *polo[2]={&genie,&palyo}; + static int polo2[2]={310,311}; + int x; + + for(x=0;x<2;x++) + CheckMenuItem(fceumenu,polo2[x],*polo[x]?MF_CHECKED:MF_UNCHECKED); + if(eoptions&EO_BGRUN) + CheckMenuItem(fceumenu,301,MF_CHECKED); + else + CheckMenuItem(fceumenu,301,MF_UNCHECKED); +} + +char *rfiles[10]={0,0,0,0,0,0,0,0,0,0}; + +void UpdateRMenu(void) +{ + MENUITEMINFO moo; + int x; + + moo.cbSize=sizeof(moo); + moo.fMask=MIIM_SUBMENU|MIIM_STATE; + + GetMenuItemInfo(GetSubMenu(fceumenu,0),102,FALSE,&moo); + moo.hSubMenu=recentmenu; + moo.fState=rfiles[0]?MFS_ENABLED:MFS_GRAYED; + + SetMenuItemInfo(GetSubMenu(fceumenu,0),102,FALSE,&moo); + + for(x=0;x<10;x++) + RemoveMenu(recentmenu,600+x,MF_BYCOMMAND); + for(x=9;x>=0;x--) + { + char tmp[128+5]; + if(!rfiles[x]) continue; + + moo.cbSize=sizeof(moo); + moo.fMask=MIIM_DATA|MIIM_ID|MIIM_TYPE; + + if(strlen(rfiles[x])<128) + { + sprintf(tmp,"&%d. %s",(x+1)%10,rfiles[x]); + } + else + sprintf(tmp,"&%d. %s",(x+1)%10,rfiles[x]+strlen(rfiles[x])-127); + + moo.cch=strlen(tmp); + moo.fType=0; + moo.wID=600+x; + moo.dwTypeData=tmp; + InsertMenuItem(recentmenu,0,1,&moo); + } + DrawMenuBar(hAppWnd); +} + +void AddRecent(char *fn) +{ + int x; + + for(x=0;x<10;x++) + if(rfiles[x]) + if(!strcmp(rfiles[x],fn)) // Item is already in list. + { + int y; + char *tmp; + + tmp=rfiles[x]; // Save pointer. + for(y=x;y;y--) + rfiles[y]=rfiles[y-1]; // Move items down. + + rfiles[0]=tmp; // Put item on top. + UpdateRMenu(); + return; + } + + if(rfiles[9]) free(rfiles[9]); + for(x=9;x;x--) rfiles[x]=rfiles[x-1]; + rfiles[0]=malloc(strlen(fn)+1); + strcpy(rfiles[0],fn); + UpdateRMenu(); +} + +void HideMenu(int h) +{ + if(h) + { + SetMenu(hAppWnd,0); + } + else + { + SetMenu(hAppWnd,fceumenu); + } +} + +static LONG WindowXC=1<<30,WindowYC; +void HideFWindow(int h) +{ + LONG desa; + + if(h) + { + RECT bo; + GetWindowRect(hAppWnd,&bo); + WindowXC=bo.left; + WindowYC=bo.top; + + SetMenu(hAppWnd,0); + desa=WS_POPUP|WS_CLIPSIBLINGS; + } + else + { + desa=WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS; + HideMenu(tog); + } + + SetWindowLong(hAppWnd,GWL_STYLE,desa|(GetWindowLong(hAppWnd,GWL_STYLE)&WS_VISIBLE)); + SetWindowPos(hAppWnd,0,0,0,0,0,SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOREPOSITION|SWP_NOSIZE|SWP_NOZORDER); +} + +void ToggleHideMenu(void) +{ + if(!fullscreen) + { + tog^=1; + HideMenu(tog); + SetMainWindowStuff(); + } +} + +static void ALoad(char *nameo) +{ + if((GI=FCEUI_LoadGame(nameo))) + { + FixFL(); + FixGIGO(); + SetMainWindowStuff(); + AddRecent(nameo); + RefreshThrottleFPS(); + if(eoptions&EO_FSAFTERLOAD) + SetFSVideoMode(); + } + else + StopSound(); +} + +void LoadNewGamey(HWND hParent) +{ + const char filter[]="All usable files(*.nes,*.nsf,*.fds,*.unf,*.zip,*.gz)\0*.nes;*.nsf;*.fds;*.unf;*.zip;*.gz\0All non-compressed usable files(*.nes,*.nsf,*.fds,*.unf)\0*.nes;*.nsf;*.fds;*.unf\0All files (*.*)\0*.*\0"; + char nameo[2048]; + OPENFILENAME ofn; + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize=sizeof(ofn); + ofn.hInstance=fceu_hInstance; + ofn.lpstrTitle="FCE Ultra Open File..."; + ofn.lpstrFilter=filter; + nameo[0]=0; + ofn.hwndOwner=hParent; + ofn.lpstrFile=nameo; + ofn.nMaxFile=256; + ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; //OFN_EXPLORER|OFN_ENABLETEMPLATE|OFN_ENABLEHOOK; + ofn.lpstrInitialDir=gfsdir; + if(GetOpenFileName(&ofn)) + { + if(gfsdir) free(gfsdir); + if((gfsdir=malloc(ofn.nFileOffset+1))) + { + strncpy(gfsdir,ofn.lpstrFile,ofn.nFileOffset); + gfsdir[ofn.nFileOffset]=0; + } + ALoad(nameo); + } +} + +static uint32 mousex,mousey,mouseb; +void GetMouseData(uint32 *x, uint32 *y, uint32 *b) +{ + *x=mousex; + *y=mousey; + if(!fullscreen) + { + if(eoptions&EO_USERFORCE) + { + RECT t; + GetClientRect(hAppWnd,&t); + + *x=*x*VNSWID/(t.right?t.right:1); + *y=*y*totallines/(t.bottom?t.bottom:1); + } + else + { + *x/=winsizemul; + *y/=winsizemul; + } + *x+=VNSCLIP; + } + + *y+=srendline; + *b=((mouseb==MK_LBUTTON)?1:0)|((mouseb==MK_RBUTTON)?2:0); +} + +static int sizchange=0; +static int vchanged=0; + +LRESULT FAR PASCAL AppWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) +{ + switch(msg) { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + mouseb=wParam; + goto proco; + case WM_MOUSEMOVE: + { + mousex=LOWORD(lParam); + mousey=HIWORD(lParam); + } + goto proco; + case WM_SIZING: + sizchange=1; + goto proco; + case WM_DISPLAYCHANGE: + if(!fullscreen && !changerecursive) + vchanged=1; + goto proco; + case WM_DROPFILES: + { + UINT len; + char *ftmp; + + len=DragQueryFile((HANDLE)wParam,0,0,0)+1; + if((ftmp=malloc(len))) + { + DragQueryFile((HANDLE)wParam,0,ftmp,len); + ALoad(ftmp); + free(ftmp); + } + } + break; + case WM_COMMAND: + if(!(wParam>>16)) + { + wParam&=0xFFFF; + if(wParam>=600 && wParam<=609) + { + if(rfiles[wParam-600]) ALoad(rfiles[wParam-600]); + } + switch(wParam) + { + case 300:ToggleHideMenu();break; + case 301:eoptions^=EO_BGRUN;UpdateMenu();break; + + case 310:genie^=1;FCEUI_SetGameGenie(genie);UpdateMenu();break; + case 311:palyo^=1; + FCEUI_SetVidSystem(palyo); + RefreshThrottleFPS(); + UpdateMenu(); + FixFL(); + SetMainWindowStuff(); + break; + + case 320:StopSound();ConfigDirectories();break; + case 321:StopSound();ConfigInput(hWnd);break; + case 322:ConfigMisc();break; + case 323:StopSound();ConfigNetplay();break; + case 324:StopSound();ConfigPalette();break; + case 325:StopSound();ConfigSound();break; + case 326:ConfigVideo();break; + + case 200:DriverInterface(DES_RESET,0);break; + case 201:DriverInterface(DES_POWER,0);break; + case 202:ConfigCheats(hWnd);break; + + case 100:StopSound(); + LoadNewGamey(hWnd); + break; + case 101:if(GI) + { + FCEUI_CloseGame(); + GI=0; + } + break; + case 110:FCEUI_SaveState();break; + case 111:FCEUI_LoadState();break; + + case 120: + { + MENUITEMINFO mi; + char *str; + + StopSound(); + if(CreateSoundSave()) + str="Stop Sound Logging"; + else + str="Log Sound As..."; + memset(&mi,0,sizeof(mi)); + mi.fMask=MIIM_DATA|MIIM_TYPE; + mi.cbSize=sizeof(mi); + GetMenuItemInfo(fceumenu,120,0,&mi); + mi.fMask=MIIM_DATA|MIIM_TYPE; + mi.cbSize=sizeof(mi); + mi.dwTypeData=str; + mi.cch=strlen(str); + SetMenuItemInfo(fceumenu,120,0,&mi); + } + break; + case 130:DoFCEUExit();break; + + case 400:StopSound();ShowAboutBox();break; + } + } + break; + + + case WM_SYSCOMMAND: + if(wParam==SC_KEYMENU) + if(GI && InputTypeFC==SIFC_FKB && cidisabled) + break; + goto proco; + case WM_SYSKEYDOWN: + if(GI && InputTypeFC==SIFC_FKB && cidisabled) + break; /* Hopefully this won't break DInput... */ + + if(fullscreen || tog) + { + if(wParam==VK_MENU) + break; + } + if(wParam==VK_F10) + { + if(!(lParam&0x40000000)) + DriverInterface(DES_RESET,0); + break; + } + goto proco; + + case WM_KEYDOWN: + if(GI) + { + /* Only disable command keys if a game is loaded(and the other + conditions are right, of course). */ + if(InputTypeFC==SIFC_FKB) + { + if(wParam==VK_SCROLL) + { + cidisabled^=1; + FCEUI_DispMessage("Family Keyboard %sabled.",cidisabled?"en":"dis"); + } + if(cidisabled) + break; /* Hopefully this won't break DInput... */ + } + if(GI->type==GIT_NSF) + switch(wParam) + { + case VK_UP:DriverInterface(DES_NSFINC,0);break; + case VK_DOWN:DriverInterface(DES_NSFDEC,0);break; + case VK_RETURN:DriverInterface(DES_NSFRES,0);break; + + case VK_LEFT:if(!(lParam&0x40000000)) + DriverInterface(DES_NSFDEC,0); break; + case VK_RIGHT:if(!(lParam&0x40000000)) + DriverInterface(DES_NSFINC,0); break; + } + + } + if(!(lParam&0x40000000)) + switch( wParam ) + { + case VK_F11:DriverInterface(DES_POWER,0);break; + case VK_F12:DoFCEUExit();break; + case VK_F2:userpause^=1;break; + case VK_F3:ToggleHideMenu();break; + case VK_F4: UpdateMenu(); + changerecursive=1; + if(!SetVideoMode(fullscreen^1)) + SetVideoMode(fullscreen); + changerecursive=0; + break; + } + goto proco; + + + case WM_NCRBUTTONDOWN: + case WM_NCMBUTTONDOWN:StopSound();goto proco; + case WM_NCLBUTTONDOWN:StopSound();goto proco; + + case WM_ENTERMENULOOP:StopSound();goto proco; + case WM_CLOSE: + case WM_DESTROY: + case WM_QUIT:DoFCEUExit();break; + case WM_ACTIVATEAPP: + if((BOOL)wParam) + { + nofocus=0; + } + else + { + nofocus=1; + } + default: + proco: + return DefWindowProc(hWnd,msg,wParam,lParam); + } + return 0; +} + +void UpdateFCEUWindow(void) +{ + int w,h; + RECT wrect; + + if(vchanged && !fullscreen && !changerecursive && !nofocus) + { + SetVideoMode(0); + vchanged=0; + } + + if(sizchange && !fullscreen && !(eoptions&EO_USERFORCE)) + { + GetWindowRect(hAppWnd,&wrect); + h=wrect.bottom-wrect.top; + w=wrect.right-wrect.left; + if(w!=winwidth) + winsizemul=(w-(winwidth-VNSWID*winsizemul)+(VNSWID>>1))>>8; + else + if(h!=winheight) + winsizemul=(h-(winheight-totallines*winsizemul)+(totallines>>1))>>8; + + if(winsizemul<1) + winsizemul=1; + SetMainWindowStuff(); + } + sizchange=0; + + BlockingCheck(); +} + +void ByebyeWindow(void) +{ + SetMenu(hAppWnd,0); + DestroyMenu(fceumenu); + DestroyWindow(hAppWnd); +} + +int CreateMainWindow(void) +{ + WNDCLASSEX winclass; + RECT tmp; + + memset(&winclass,0,sizeof(winclass)); + winclass.cbSize=sizeof(WNDCLASSEX); + winclass.style=CS_OWNDC|CS_HREDRAW|CS_VREDRAW|CS_SAVEBITS; + winclass.lpfnWndProc=AppWndProc; + winclass.cbClsExtra=0; + winclass.cbWndExtra=0; + winclass.hInstance=fceu_hInstance; + winclass.hIcon=LoadIcon(fceu_hInstance, "ICON_1"); + winclass.hIconSm=LoadIcon(fceu_hInstance, "ICON_1"); + winclass.hCursor=LoadCursor(NULL, IDC_ARROW); + winclass.hbrBackground=GetStockObject(BLACK_BRUSH); + //winclass.lpszMenuName="FCEUMENU"; + winclass.lpszClassName="FCEULTRA"; + + if(!RegisterClassEx(&winclass)) + return FALSE; + + AdjustWindowRectEx(&tmp,WS_OVERLAPPEDWINDOW,1,0); + + fceumenu=LoadMenu(fceu_hInstance,"FCEUMENU"); + recentmenu=CreateMenu(); + UpdateRMenu(); + + hAppWnd = CreateWindowEx(0,"FCEULTRA","FCE Ultra", + WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, /* Style */ + CW_USEDEFAULT,CW_USEDEFAULT,256,240, /* X,Y ; Width, Height */ + NULL,fceumenu,fceu_hInstance,NULL ); + DragAcceptFiles(hAppWnd, 1); + SetMainWindowStuff(); + return 1; +} + + +int SetMainWindowStuff(void) +{ + RECT *srect; + RECT tmp; + + GetWindowRect(hAppWnd,&tmp); + + if(WindowXC!=(1<<30)) + { + /* Subtracting and adding for if(eoptions&EO_USERFORCE) below. */ + tmp.bottom-=tmp.top; + tmp.bottom+=WindowYC; + + tmp.right-=tmp.left; + tmp.right+=WindowXC; + + + tmp.left=WindowXC; + tmp.top=WindowYC; + WindowXC=1<<30; + } + + if(eoptions&EO_USERFORCE) + { + SetWindowPos(hAppWnd,HWND_TOP,tmp.left,tmp.top,0,0,SWP_NOSIZE|SWP_SHOWWINDOW); + winwidth=tmp.right-tmp.left; + winheight=tmp.bottom-tmp.top; + } + else + { + srect=CalcWindowSize(); + SetWindowPos(hAppWnd,HWND_TOP,tmp.left,tmp.top,srect->right,srect->bottom,SWP_SHOWWINDOW); + winwidth=srect->right; + winheight=srect->bottom; + } + + + ShowWindow(hAppWnd, SW_SHOWNORMAL); + return 1; +} + +int GetClientAbsRect(LPRECT lpRect) +{ + POINT point; + point.x=point.y=0; + if(!ClientToScreen(hAppWnd,&point)) return 0; + + lpRect->top=point.y; + lpRect->left=point.x; + + if(eoptions&EO_USERFORCE) + { + RECT al; + + GetClientRect(hAppWnd,&al); + + lpRect->right=point.x+al.right; + lpRect->bottom=point.y+al.bottom; + } + else + { + lpRect->right=point.x+VNSWID*winsizemul; + lpRect->bottom=point.y+totallines*winsizemul; + } + return 1; +} + + +void LoadPaletteFile(void) +{ + FILE *fp; + const char filter[]="All usable files(*.pal)\0*.pal\0All files (*.*)\0*.*\0"; + char nameo[2048]; + OPENFILENAME ofn; + memset(&ofn,0,sizeof(ofn)); + ofn.lStructSize=sizeof(ofn); + ofn.hInstance=fceu_hInstance; + ofn.lpstrTitle="FCE Ultra Open Palette File..."; + ofn.lpstrFilter=filter; + nameo[0]=0; + ofn.lpstrFile=nameo; + ofn.nMaxFile=256; + ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; + ofn.lpstrInitialDir=0; + if(GetOpenFileName(&ofn)) + { + if((fp=fopen(nameo,"rb"))) + { + fread(cpalette,1,192,fp); + fclose(fp); + FCEUI_SetPaletteArray(cpalette); + eoptions|=EO_CPALETTE; + } + else + FCEUD_PrintError("Error opening palette file!"); + } +} + +BOOL CALLBACK PaletteConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + if(ntsccol) + CheckDlgButton(hwndDlg,100,BST_CHECKED); + SendDlgItemMessage(hwndDlg,500,TBM_SETRANGE,1,MAKELONG(0,128)); + SendDlgItemMessage(hwndDlg,501,TBM_SETRANGE,1,MAKELONG(0,128)); + DriverInterface(DES_GETNTSCTINT,&ntsctint); + DriverInterface(DES_GETNTSCHUE,&ntschue); + SendDlgItemMessage(hwndDlg,500,TBM_SETPOS,1,ntsctint); + SendDlgItemMessage(hwndDlg,501,TBM_SETPOS,1,ntschue); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 100:ntsccol^=1;DriverInterface(DES_NTSCCOL,&ntsccol);break; + case 200: + LoadPaletteFile(); + break; + case 201:FCEUI_SetPaletteArray(0); + eoptions&=~EO_CPALETTE; + break; + case 1: + gornk: + ntsctint=SendDlgItemMessage(hwndDlg,500,TBM_GETPOS,0,(LPARAM)(LPSTR)0); + ntschue=SendDlgItemMessage(hwndDlg,501,TBM_GETPOS,0,(LPARAM)(LPSTR)0); + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +static void ConfigPalette(void) +{ + DialogBox(fceu_hInstance,"PALCONFIG",hAppWnd,PaletteConCallB); + DriverInterface(DES_SETNTSCTINT,&ntsctint); + DriverInterface(DES_SETNTSCHUE,&ntschue); +} + + +static BOOL CALLBACK MiscConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_INITDIALOG: + if(eoptions&EO_NOSPRLIM) + CheckDlgButton(hwndDlg,100,BST_CHECKED); + if(eoptions&EO_FOAFTERSTART) + CheckDlgButton(hwndDlg,102,BST_CHECKED); + if(eoptions&EO_SNAPNAME) + CheckDlgButton(hwndDlg,103,BST_CHECKED); + if(eoptions&EO_NOTHROTTLE) + CheckDlgButton(hwndDlg,101,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + switch(wParam&0xFFFF) + { + case 1: + gornk: + if(IsDlgButtonChecked(hwndDlg,100)==BST_CHECKED) + eoptions|=EO_NOSPRLIM; + else + eoptions&=~EO_NOSPRLIM; + if(IsDlgButtonChecked(hwndDlg,102)==BST_CHECKED) + eoptions|=EO_FOAFTERSTART; + else + eoptions&=~EO_FOAFTERSTART; + if(IsDlgButtonChecked(hwndDlg,103)==BST_CHECKED) + eoptions|=EO_SNAPNAME; + else + eoptions&=~EO_SNAPNAME; + if(IsDlgButtonChecked(hwndDlg,101)==BST_CHECKED) + eoptions|=EO_NOTHROTTLE; + else + eoptions&=~EO_NOTHROTTLE; + EndDialog(hwndDlg,0); + break; + } + } + return 0; +} + +void DoMiscConfigFix(void) +{ + FCEUI_DisableSpriteLimitation(eoptions&EO_NOSPRLIM); + FCEUI_SetSnapName(eoptions&EO_SNAPNAME); +} + +static void ConfigMisc(void) +{ + DialogBox(fceu_hInstance,"MISCCONFIG",hAppWnd,MiscConCallB); + DoMiscConfigFix(); +} + +static int BrowseForFolder(HWND hParent, char *htext, char *buf) +{ + BROWSEINFO bi; + LPCITEMIDLIST pidl; + int ret=1; + + buf[0]=0; + + memset(&bi,0,sizeof(bi)); + + bi.hwndOwner=hParent; + bi.lpszTitle=htext; + bi.ulFlags=BIF_RETURNONLYFSDIRS; + + if(FAILED(CoInitialize(0))) + return(0); + + if(!(pidl=SHBrowseForFolder(&bi))) + { + ret=0; + goto end1; + } + + if(!SHGetPathFromIDList(pidl,buf)) + { + ret=0; + goto end2; + } + + end2: + /* This probably isn't the best way to free the memory... */ + CoTaskMemFree((PVOID)pidl); + + end1: + CoUninitialize(); + return(ret); +} + +static BOOL CALLBACK DirConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int x; + + switch(uMsg){ + case WM_INITDIALOG: + for(x=0;x<6;x++) + SetDlgItemText(hwndDlg,100+x,DOvers[x]); + + if(eoptions&EO_BSAV) + CheckDlgButton(hwndDlg,300,BST_CHECKED); + break; + case WM_CLOSE: + case WM_QUIT: goto gornk; + case WM_COMMAND: + if(!(wParam>>16)) + { + if((wParam&0xFFFF)>=200 && (wParam&0xFFFF)<=205) + { + static char *helpert[6]={"Cheats","Miscellaneous","Nonvolatile Game Data","Save States","Screen Snapshots","Base Directory"}; + char name[MAX_PATH]; + + if(BrowseForFolder(hwndDlg,helpert[((wParam&0xFFFF)-200)],name)) + SetDlgItemText(hwndDlg,100+((wParam&0xFFFF)-200),name); + } + else switch(wParam&0xFFFF) + { + case 1: + gornk: + + RemoveDirs(); // Remove empty directories. + + for(x=0;x<6;x++) + { + LONG len; + len=SendDlgItemMessage(hwndDlg,100+x,WM_GETTEXTLENGTH,0,0); + if(len<=0) + { + if(DOvers[x]) free(DOvers[x]); + DOvers[x]=0; + continue; + } + len++; // Add 1 for null character. + if(!(DOvers[x]=malloc(len))) + continue; + if(!GetDlgItemText(hwndDlg,100+x,DOvers[x],len)) + { + free(DOvers[x]); + DOvers[x]=0; + continue; + } + + } + if(IsDlgButtonChecked(hwndDlg,300)==BST_CHECKED) + eoptions|=EO_BSAV; + else + eoptions&=~EO_BSAV; + + CreateDirs(); // Create needed directories. + SetDirs(); // Set the directories in the core. + EndDialog(hwndDlg,0); + break; + } + } + } + return 0; +} + + + +static void ConfigDirectories(void) +{ + DialogBox(fceu_hInstance,"DIRCONFIG",hAppWnd,DirConCallB); +} diff --git a/endian.c b/endian.c new file mode 100644 index 0000000..8cd5b70 --- /dev/null +++ b/endian.c @@ -0,0 +1,71 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Contains file I/O functions that write/read data */ +/* LSB first. */ + + +#include +#include "types.h" +#include "endian.h" + +static uint8 s[4]; + +int write16(uint16 b, FILE *fp) +{ + s[0]=b; + s[1]=b>>8; + return((fwrite(s,1,2,fp)<2)?0:2); +} + +int write32(uint32 b, FILE *fp) +{ + s[0]=b; + s[1]=b>>8; + s[2]=b>>16; + s[3]=b>>24; + return((fwrite(s,1,4,fp)<4)?0:4); +} + +int read32(void *Bufo, FILE *fp) +{ + uint32 buf; + if(fread(&buf,1,4,fp)<4) + return 0; + #ifdef LSB_FIRST + *(uint32*)Bufo=buf; + #else + *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); + #endif + return 1; +} + +int read16(char *d, FILE *fp) +{ + #ifdef LSB_FIRST + return((fread(d,1,2,fp)<2)?0:2); + #else + int ret; + ret=fread(d+1,1,1,fp); + ret+=fread(d,1,1,fp); + return ret<2?0:2; + #endif +} + diff --git a/endian.h b/endian.h new file mode 100644 index 0000000..23ee4ed --- /dev/null +++ b/endian.h @@ -0,0 +1,3 @@ +int write16(uint16 b, FILE *fp); +int write32(uint32 b, FILE *fp); +int read32(void *Bufo, FILE *fp); diff --git a/fce.c b/fce.c new file mode 100644 index 0000000..653a3f6 --- /dev/null +++ b/fce.c @@ -0,0 +1,1565 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "x6502.h" +#include "fce.h" +#include "sound.h" +#include "svga.h" +#include "netplay.h" +#include "general.h" +#include "endian.h" +#include "version.h" +#include "memory.h" + +#include "cart.h" +#include "nsf.h" +#include "fds.h" +#include "ines.h" +#include "unif.h" +#include "cheat.h" + +#include "state.h" +#include "video.h" +#include "input.h" +#include "file.h" +#include "crc32.h" + +#define Pal (PALRAM) + +static void FetchSpriteData(void); +static void FASTAPASS(1) RefreshLine(uint8 *target); +static void PRefreshLine(void); +static void FASTAPASS(1) RefreshSprite(uint8 *target); +static void ResetPPU(void); +static void PowerPPU(void); + +uint64 timestampbase=0; + +int MMC5Hack; +uint32 MMC5HackVROMMask; +uint8 *MMC5HackExNTARAMPtr; +uint8 *MMC5HackVROMPTR; +uint8 MMC5HackCHRMode=0; +uint8 MMC5HackSPMode; +uint8 MMC5HackSPScroll; +uint8 MMC5HackSPPage; + +uint8 *MMC5SPRVPage[8]; +uint8 *MMC5BGVPage[8]; + + +uint8 VRAMBuffer,PPUGenLatch; + +uint8 *vnapage[4]; +uint8 PPUNTARAM; +uint8 PPUCHRRAM; + +/* Color deemphasis emulation. Joy... */ +static uint8 deemp=0; +static int deempcnt[8]; + +static int tosprite=256; + +FCEUGI FCEUGameInfo; +void (*GameInterface)(int h); + +void FP_FASTAPASS(1) (*PPU_hook)(uint32 A); + +void (*GameStateRestore)(int version); +void (*GameHBIRQHook)(void); + +readfunc ARead[0x10000]; +writefunc BWrite[0x10000]; +static readfunc *AReadG; +static writefunc *BWriteG; +static int RWWrap=0; + +DECLFW(BNull) +{ + +} + +DECLFR(ANull) +{ + return(X.DB); +} + +int AllocGenieRW(void) +{ + if(!(AReadG=FCEU_malloc(0x8000*sizeof(readfunc)))) + return 0; + if(!(BWriteG=FCEU_malloc(0x8000*sizeof(writefunc)))) + return 0; + RWWrap=1; + return 1; +} + +void FlushGenieRW(void) +{ + int32 x; + + if(RWWrap) + { + for(x=0;x<0x8000;x++) + { + ARead[x+0x8000]=AReadG[x]; + BWrite[x+0x8000]=BWriteG[x]; + } + free(AReadG); + free(BWriteG); + AReadG=0; + BWriteG=0; + RWWrap=0; + } +} + +readfunc FASTAPASS(1) GetReadHandler(int32 a) +{ + if(a>=0x8000 && RWWrap) + return AReadG[a-0x8000]; + else + return ARead[a]; +} + +void FASTAPASS(3) SetReadHandler(int32 start, int32 end, readfunc func) +{ + int32 x; + + if(!func) + func=ANull; + + if(RWWrap) + for(x=end;x>=start;x--) + { + if(x>=0x8000) + AReadG[x-0x8000]=func; + else + ARead[x]=func; + } + else + + for(x=end;x>=start;x--) + ARead[x]=func; +} + +writefunc FASTAPASS(1) GetWriteHandler(int32 a) +{ + if(RWWrap && a>=0x8000) + return BWriteG[a-0x8000]; + else + return BWrite[a]; +} + +void FASTAPASS(3) SetWriteHandler(int32 start, int32 end, writefunc func) +{ + int32 x; + + if(!func) + func=BNull; + + if(RWWrap) + for(x=end;x>=start;x--) + { + if(x>=0x8000) + BWriteG[x-0x8000]=func; + else + BWrite[x]=func; + } + else + for(x=end;x>=start;x--) + BWrite[x]=func; +} + +uint8 vtoggle=0; +uint8 XOffset=0; + +uint32 TempAddr,RefreshAddr; + +static int maxsprites=8; + +/* scanline is equal to the current visible scanline we're on. */ + +int scanline; +static uint32 scanlines_per_frame; + +uint8 PPU[4]; +uint8 PPUSPL; + +uint8 GameMemBlock[131072]; +uint8 NTARAM[0x800],PALRAM[0x20],SPRAM[0x100],SPRBUF[0x100]; +uint8 RAM[0x800]; + +uint8 PAL=0; + +#define MMC5SPRVRAMADR(V) &MMC5SPRVPage[(V)>>10][(V)] +#define MMC5BGVRAMADR(V) &MMC5BGVPage[(V)>>10][(V)] +#define VRAMADR(V) &VPage[(V)>>10][(V)] + +static DECLFW(BRAML) +{ + RAM[A]=V; +} + +static DECLFW(BRAMH) +{ + RAM[A&0x7FF]=V; +} + +static DECLFR(ARAML) +{ + return RAM[A]; +} + +static DECLFR(ARAMH) +{ + return RAM[A&0x7FF]; +} + + +static DECLFR(A2002) +{ + uint8 ret; + ret = PPU_status; + vtoggle=0; + PPU_status&=0x7F; + return ret|(PPUGenLatch&0x1F); +} + +static DECLFR(A200x) +{ + return PPUGenLatch; +} + +static DECLFR(A2007) +{ + uint8 ret; + uint32 tmp=RefreshAddr&0x3FFF; + + PPUGenLatch=ret=VRAMBuffer; + if(PPU_hook) PPU_hook(tmp); + if(tmp<0x2000) + { + VRAMBuffer=VPage[tmp>>10][tmp]; + } + else + { + VRAMBuffer=vnapage[(tmp>>10)&0x3][tmp&0x3FF]; + } + + if (INC32) RefreshAddr+=32; + else RefreshAddr++; + if(PPU_hook) PPU_hook(RefreshAddr&0x3fff); + return ret; +} + +static DECLFW(B2000) +{ + PPUGenLatch=V; + PPU[0]=V; + TempAddr&=0xF3FF; + TempAddr|=(V&3)<<10; +} + +static DECLFW(B2001) +{ + PPUGenLatch=V; + PPU[1]=V; + if(V&0xE0) + deemp=V>>5; + //printf("$%04x:$%02x, %d\n",X.PC,V,scanline); +} + +static DECLFW(B2002) +{ + PPUGenLatch=V; +} + +static DECLFW(B2003) +{ + PPUGenLatch=V; + PPU[3]=V; + PPUSPL=V&0x7; +} + +static DECLFW(B2004) +{ + PPUGenLatch=V; + //SPRAM[PPU[3]++]=V; + if(PPUSPL&8) + { + if(PPU[3]>=8) + SPRAM[PPU[3]]=V; + } + else + { + //printf("$%02x:$%02x\n",PPUSPL,V); + SPRAM[PPUSPL]=V; + PPUSPL++; + } + PPU[3]++; +} + +static DECLFW(B2005) +{ + uint32 tmp=TempAddr; + + PPUGenLatch=V; + if (!vtoggle) + { + tmp&=0xFFE0; + tmp|=V>>3; + XOffset=V&7; + } + else + { + tmp&=0x8C1F; + tmp|=((V&~0x7)<<2); + tmp|=(V&7)<<12; + } + + TempAddr=tmp; + vtoggle^=1; +} + +static DECLFW(B2006) +{ + PPUGenLatch=V; + if(!vtoggle) + { + TempAddr&=0x00FF; + TempAddr|=(V&0x3f)<<8; + } + else + { + TempAddr&=0xFF00; + TempAddr|=V; + RefreshAddr=TempAddr; + + if(PPU_hook) + PPU_hook(RefreshAddr); + } + vtoggle^=1; +} + +static DECLFW(B2007) +{ + uint32 tmp=RefreshAddr&0x3FFF; + + PPUGenLatch=V; + if(tmp>=0x3F00) + { + // hmmm.... + if(!(tmp&0xf)) + PALRAM[0x00]=PALRAM[0x04]=PALRAM[0x08]=PALRAM[0x0C]= + PALRAM[0x10]=PALRAM[0x14]=PALRAM[0x18]=PALRAM[0x1c]=V&0x3f; + else if(tmp&3) 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; + } + if (INC32) RefreshAddr+=32; + else RefreshAddr++; + if(PPU_hook) PPU_hook(RefreshAddr&0x3fff); +} + +static DECLFW(B4014) +{ + uint32 t=V<<8; + int x; + for(x=0;x<256;x++) + B2004(0x2004,X.DB=ARead[t+x](t+x)); + X6502_AddCycles(512); +} + +static void FASTAPASS(1) BGRender(uint8 *target) +{ + uint32 tem; + RefreshLine(target); + if(!(PPU[1]&2)) + { + tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24); + tem|=0x40404040; + *(uint32 *)target=*(uint32 *)(target+4)=tem; + } +} + +#ifdef FRAMESKIP +static int FSkip=0; +void FCEUI_FrameSkip(int x) +{ + FSkip=x; +} +#endif + +/* This is called at the beginning of each visible scanline */ +static void Loop6502(void) +{ + uint32 tem; + int x; + uint8 *target=XBuf+(scanline<<8)+(scanline<<4)+8; + + if(ScreenON || SpriteON) + { + /* PRefreshLine() will not get called on skipped frames. This + could cause a problem, but the solution would be rather complex, + due to the current sprite 0 hit code. + */ + #ifdef FRAMESKIP + if(!FSkip) + { + #endif + if(ScreenON) + { + if(scanline>=FSettings.FirstSLine && scanline<=FSettings.LastSLine) + BGRender(target); + else + { + if(PPU_hook) + PRefreshLine(); + } + } + else + { + tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24); + tem|=0x40404040; + FCEU_dwmemset(target,tem,264); + } + #ifdef FRAMESKIP + } + #endif + if (SpriteON && scanline) + RefreshSprite(target); + #ifdef FRAMESKIP + if(!FSkip) + { + #endif + if(PPU[1]&0x01) + { + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0xF0F0F0F0; + } + if((PPU[1]>>5)==0x7) + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=((*(uint32*)&target[x<<2])&0x3f3f3f3f)|0x40404040; + else if(PPU[1]&0xE0) + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])|0xC0C0C0C0; + else + for(x=63;x>=0;x--) + *(uint32 *)&target[x<<2]=(*(uint32*)&target[x<<2])&0x3f3f3f3f; + + #ifdef FRAMESKIP + } + #endif + } + else + { + tem=Pal[0]|(Pal[0]<<8)|(Pal[0]<<16)|(Pal[0]<<24); + FCEU_dwmemset(target,tem,256); + } + if(InputScanlineHook) + InputScanlineHook(target, scanline); +} + +#define PAL(c) ((c)+cc) + + +static void PRefreshLine(void) +{ + uint32 vofs; + uint8 X1; + + vofs = 0; + if (BGAdrHI) vofs = 0x1000; + + vofs+=(RefreshAddr>>12)&7; + + for(X1=33;X1;X1--) + { + register uint8 no; + register uint8 zz2; + zz2=(uint8)((RefreshAddr>>10)&3); + PPU_hook(0x2000|(RefreshAddr&0xFFF)); + no = vnapage[zz2][(RefreshAddr&0x3ff)]; + PPU_hook((no<<4)+vofs); + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } +} + +/* Total of 33 tiles(32 + 1 extra) */ +static void FASTAPASS(1) RefreshLine(uint8 *target) +{ + uint32 vofs; + int X1; + register uint8 *P=target; + + vofs=0; + + Pal[0]|=64; + Pal[4]|=64; + Pal[8]|=64; + Pal[0xC]|=64; + + vofs=((PPU[0]&0x10)<<8) | ((RefreshAddr>>12)&7); + P-=XOffset; + + /* This high-level graphics MMC5 emulation code was written + for MMC5 carts in "CL" mode. It's probably not totally + correct for carts in "SL" mode. + */ + if(MMC5Hack && geniestage!=1) + { + if(MMC5HackCHRMode==0 && (MMC5HackSPMode&0x80)) + { + int8 tochange; + + tochange=MMC5HackSPMode&0x1F; + + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + if((tochange<=0 && MMC5HackSPMode&0x40) || + (tochange>0 && !(MMC5HackSPMode&0x40))) + { + uint8 xs,ys; + + xs=33-X1; + ys=((scanline>>3)+MMC5HackSPScroll)&0x1F; + if(ys>=0x1E) ys-=0x1E; + vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7); + + C = MMC5HackVROMPTR+vadr; + C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12); + + cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)]; + cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2; + } + else + { + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = MMC5BGVRAMADR(vadr); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + } + #include "fceline.h" + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + tochange--; + } + } + else if(MMC5HackCHRMode==1 && (MMC5HackSPMode&0x80)) + { + int8 tochange; + + tochange=MMC5HackSPMode&0x1F; + + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc; + register uint8 zz2; + uint32 vadr; + + if((tochange<=0 && MMC5HackSPMode&0x40) || + (tochange>0 && !(MMC5HackSPMode&0x40))) + { + uint8 xs,ys; + + xs=33-X1; + ys=((scanline>>3)+MMC5HackSPScroll)&0x1F; + if(ys>=0x1E) ys-=0x1E; + vadr=(MMC5HackExNTARAMPtr[xs|(ys<<5)]<<4)+(vofs&7); + + C = MMC5HackVROMPTR+vadr; + C += ((MMC5HackSPPage & 0x3f & MMC5HackVROMMask) << 12); + + cc=MMC5HackExNTARAMPtr[0x3c0+(xs>>2)+((ys&0x1C)<<1)]; + cc=((cc >> ((xs&2) + ((ys&0x2)<<1))) &3) <<2; + } + else + { + C=MMC5HackVROMPTR; + zz2=(RefreshAddr>>10)&3; + vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs; + C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f & + MMC5HackVROMMask) << 12) + (vadr & 0xfff); + vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4; + cc = vadr; + } + #include "fceline.h" + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + tochange--; + } + } + + else if(MMC5HackCHRMode==1) + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc; + register uint8 zz2; + uint32 vadr; + + C=MMC5HackVROMPTR; + zz2=(RefreshAddr>>10)&3; + vadr = (vnapage[zz2][RefreshAddr & 0x3ff] << 4) + vofs; + C += (((MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff]) & 0x3f & + MMC5HackVROMMask) << 12) + (vadr & 0xfff); + vadr = (MMC5HackExNTARAMPtr[RefreshAddr & 0x3ff] & 0xC0)>> 4; + cc = vadr; + + #include "fceline.h" + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + else + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = MMC5BGVRAMADR(vadr); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + + #include "fceline.h" + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + } // End if(MMC5Hack) + + else if(PPU_hook) + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + PPU_hook(0x2000|(RefreshAddr&0xFFF)); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = VRAMADR(vadr); + + #include "fceline.h" + + PPU_hook(vadr); + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + else + { + for(X1=33;X1;X1--,P+=8) + { + uint8 *C; + register uint8 cc,zz,zz2; + uint32 vadr; + + zz=RefreshAddr&0x1F; + zz2=(RefreshAddr>>10)&3; + vadr=(vnapage[zz2][RefreshAddr&0x3ff]<<4)+vofs; + C = VRAMADR(vadr); + cc=vnapage[zz2][0x3c0+(zz>>2)+((RefreshAddr&0x380)>>4)]; + cc=((cc >> ((zz&2) + ((RefreshAddr&0x40)>>4))) &3) <<2; + #include "fceline.h" + + if((RefreshAddr&0x1f)==0x1f) + RefreshAddr^=0x41F; + else + RefreshAddr++; + } + } + + #undef vofs + + Pal[0]&=63; + Pal[4]&=63; + Pal[8]&=63; + Pal[0xC]&=63; +} + +static INLINE void Fixit2(void) +{ + if(ScreenON || SpriteON) + { + uint32 rad=RefreshAddr; + rad&=0xFBE0; + rad|=TempAddr&0x041f; + RefreshAddr=rad; + //PPU_hook(RefreshAddr,-1); + } +} + +static INLINE void Fixit1(void) +{ + if(ScreenON || SpriteON) + { + uint32 rad=RefreshAddr; + + if((rad&0x7000)==0x7000) + { + rad^=0x7000; + if((rad&0x3E0)==0x3A0) + { + rad^=0x3A0; + rad^=0x800; + } + else + { + if((rad&0x3E0)==0x3e0) + rad^=0x3e0; + else rad+=0x20; + } + } + else + rad+=0x1000; + RefreshAddr=rad; + //PPU_hook(RefreshAddr,-1); + } +} + +/* This is called at the beginning of all h-blanks on visible lines. */ +static void DoHBlank(void) +{ + if(ScreenON || SpriteON) + FetchSpriteData(); + if(GameHBIRQHook && (ScreenON || SpriteON)) + { + X6502_Run(12); + GameHBIRQHook(); + X6502_Run(25-12); + Fixit2(); + X6502_Run(85-25); + } + else + { + X6502_Run(25); // Tried 65, caused problems with Slalom(maybe others) + Fixit2(); + X6502_Run(85-25); + } + //PPU_hook(0,-1); + //fprintf(stderr,"%3d: $%04x\n",scanline,RefreshAddr); +} + +#define V_FLIP 0x80 +#define H_FLIP 0x40 +#define SP_BACK 0x20 + +typedef struct { + uint8 y,no,atr,x; +} SPR; + +typedef struct { + uint8 ca[2],atr,x; +} SPRB; + +uint8 sprlinebuf[256+8]; + +void FCEUI_DisableSpriteLimitation(int a) +{ + maxsprites=a?64:8; +} + +static uint8 nosprites,SpriteBlurp; + +static void FetchSpriteData(void) +{ + SPR *spr; + uint8 H; + int n,vofs; + + spr=(SPR *)SPRAM; + H=8; + + nosprites=SpriteBlurp=0; + + vofs=(unsigned int)(PPU[0]&0x8&(((PPU[0]&0x20)^0x20)>>2))<<9; + H+=(PPU[0]&0x20)>>2; + + if(!PPU_hook) + for(n=63;n>=0;n--,spr++) + { + if((unsigned int)(scanline-spr->y)>=H) continue; + + if(nospritesy); + + if (Sprite16) + vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4); + else + vadr = (spr->no<<4)+vofs; + + if (spr->atr&V_FLIP) + { + vadr+=7; + vadr-=t; + vadr+=(PPU[0]&0x20)>>1; + vadr-=t&8; + } + else + { + vadr+=t; + vadr+=t&8; + } + + /* Fix this geniestage hack */ + if(MMC5Hack && geniestage!=1) C = MMC5SPRVRAMADR(vadr); + else C = VRAMADR(vadr); + + + dst.ca[0]=C[0]; + dst.ca[1]=C[8]; + dst.x=spr->x; + dst.atr=spr->atr; + + + *(uint32 *)&SPRBUF[nosprites<<2]=*(uint32 *)&dst; + } + + nosprites++; + } + else + { + PPU_status|=0x20; + break; + } + } + else + for(n=63;n>=0;n--,spr++) + { + if((unsigned int)(scanline-spr->y)>=H) continue; + + if(nospritesy); + + if (Sprite16) + vadr = ((spr->no&1)<<12) + ((spr->no&0xFE)<<4); + else + vadr = (spr->no<<4)+vofs; + + if (spr->atr&V_FLIP) + { + vadr+=7; + vadr-=t; + vadr+=(PPU[0]&0x20)>>1; + vadr-=t&8; + } + else + { + vadr+=t; + vadr+=t&8; + } + + if(MMC5Hack) C = MMC5SPRVRAMADR(vadr); + else C = VRAMADR(vadr); + dst.ca[0]=C[0]; + PPU_hook(vadr); + dst.ca[1]=C[8]; + PPU_hook(vadr|8); + dst.x=spr->x; + dst.atr=spr->atr; + + + *(uint32 *)&SPRBUF[nosprites<<2]=*(uint32 *)&dst; + } + + nosprites++; + } + else + { + PPU_status|=0x20; + break; + } + } +} + +static void FASTAPASS(1) RefreshSprite(uint8 *target) +{ + int n; + SPRB *spr; + uint8 *P=target; + + if(!nosprites) return; + #ifdef FRAMESKIP + if(FSkip) + { + if(!SpriteBlurp) + { + nosprites=0; + return; + } + else + nosprites=1; + } + #endif + + FCEU_dwmemset(sprlinebuf,0x80808080,256); + nosprites--; + spr = (SPRB*)SPRBUF+nosprites; + + for(n=nosprites;n>=0;n--,spr--) + { + register uint8 J,atr,c1,c2; + int x=spr->x; + uint8 *C; + uint8 *VB; + + P+=x; + + c1=((spr->ca[0]>>1)&0x55)|(spr->ca[1]&0xAA); + c2=(spr->ca[0]&0x55)|((spr->ca[1]<<1)&0xAA); + + J=spr->ca[0]|spr->ca[1]; + atr=spr->atr; + + if(J) + { + if(n==0 && SpriteBlurp && !(PPU_status&0x40)) + { + int z,ze=x+8; + if(ze>256) {ze=256;} + if(ScreenON && (scanlineFSettings.LastSLine + #ifdef FRAMESKIP + || FSkip + #endif + )) + BGRender(target); + + if(!(atr&H_FLIP)) + { + for(z=x;z>(z-x))) + { + if(!(target[z]&64)) + tosprite=z; + } + } + } + else + { + for(z=x;z>=2;c2>>=2; + if (J&0x08) C[3]=VB[c1&3]|0x40;; + if (J&0x04) C[2]=VB[c2&3]|0x40;; + c1>>=2;c2>>=2; + if (J&0x20) C[5]=VB[c1&3]|0x40;; + if (J&0x10) C[4]=VB[c2&3]|0x40;; + c1>>=2;c2>>=2; + if (J&0x80) C[7]=VB[c1]|0x40;; + if (J&0x40) C[6]=VB[c2]|0x40;; + } else { + if (J&0x02) C[6]=VB[c1&3]|0x40; + if (J&0x01) C[7]=VB[c2&3]|0x40; + c1>>=2;c2>>=2; + if (J&0x08) C[4]=VB[c1&3]|0x40; + if (J&0x04) C[5]=VB[c2&3]|0x40; + c1>>=2;c2>>=2; + if (J&0x20) C[2]=VB[c1&3]|0x40; + if (J&0x10) C[3]=VB[c2&3]|0x40; + c1>>=2;c2>>=2; + if (J&0x80) *C=VB[c1]|0x40; + if (J&0x40) C[1]=VB[c2]|0x40; + } + } else { + if (atr&H_FLIP) + { + if (J&0x02) C[1]=VB[(c1&3)]; + if (J&0x01) *C=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x08) C[3]=VB[(c1&3)]; + if (J&0x04) C[2]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x20) C[5]=VB[(c1&3)]; + if (J&0x10) C[4]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x80) C[7]=VB[c1]; + if (J&0x40) C[6]=VB[c2]; + }else{ + if (J&0x02) C[6]=VB[(c1&3)]; + if (J&0x01) C[7]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x08) C[4]=VB[(c1&3)]; + if (J&0x04) C[5]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x20) C[2]=VB[(c1&3)]; + if (J&0x10) C[3]=VB[(c2&3)]; + c1>>=2;c2>>=2; + if (J&0x80) *C=VB[c1]; + if (J&0x40) C[1]=VB[c2]; + } + } + } + P-=x; + } + + nosprites=0; + #ifdef FRAMESKIP + if(FSkip) return; + #endif + + { + uint8 n=((PPU[1]&4)^4)<<1; + loopskie: + { + uint32 t=*(uint32 *)(sprlinebuf+n); + if(t!=0x80808080) + { + #ifdef LSB_FIRST + if(!(t&0x80)) + { + if(!(t&0x40)) // Normal sprite + P[n]=sprlinebuf[n]; + else if(P[n]&64) // behind bg sprite + P[n]=sprlinebuf[n]; + } + + if(!(t&0x8000)) + { + if(!(t&0x4000)) // Normal sprite + P[n+1]=(sprlinebuf+1)[n]; + else if(P[n+1]&64) // behind bg sprite + P[n+1]=(sprlinebuf+1)[n]; + } + + if(!(t&0x800000)) + { + if(!(t&0x400000)) // Normal sprite + P[n+2]=(sprlinebuf+2)[n]; + else if(P[n+2]&64) // behind bg sprite + P[n+2]=(sprlinebuf+2)[n]; + } + + if(!(t&0x80000000)) + { + if(!(t&0x40000000)) // Normal sprite + P[n+3]=(sprlinebuf+3)[n]; + else if(P[n+3]&64) // behind bg sprite + P[n+3]=(sprlinebuf+3)[n]; + } + #else + if(!(t&0x80000000)) + { + if(!(t&0x40)) // Normal sprite + P[n]=sprlinebuf[n]; + else if(P[n]&64) // behind bg sprite + P[n]=sprlinebuf[n]; + } + + if(!(t&0x800000)) + { + if(!(t&0x4000)) // Normal sprite + P[n+1]=(sprlinebuf+1)[n]; + else if(P[n+1]&64) // behind bg sprite + P[n+1]=(sprlinebuf+1)[n]; + } + + if(!(t&0x8000)) + { + if(!(t&0x400000)) // Normal sprite + P[n+2]=(sprlinebuf+2)[n]; + else if(P[n+2]&64) // behind bg sprite + P[n+2]=(sprlinebuf+2)[n]; + } + + if(!(t&0x80)) + { + if(!(t&0x40000000)) // Normal sprite + P[n+3]=(sprlinebuf+3)[n]; + else if(P[n+3]&64) // behind bg sprite + P[n+3]=(sprlinebuf+3)[n]; + } + #endif + } + } + n+=4; + if(n) goto loopskie; + } +} + +void ResetMapping(void) +{ + int x; + + SetReadHandler(0x0000,0xFFFF,ANull); + SetWriteHandler(0x0000,0xFFFF,BNull); + + SetReadHandler(0,0x7FF,ARAML); + SetWriteHandler(0,0x7FF,BRAML); + + SetReadHandler(0x800,0x1FFF,ARAMH); /* Part of a little */ + SetWriteHandler(0x800,0x1FFF,BRAMH); /* hack for a small speed boost. */ + + for(x=0x2000;x<0x4000;x+=8) + { + ARead[x]=A200x; + BWrite[x]=B2000; + ARead[x+1]=A200x; + BWrite[x+1]=B2001; + ARead[x+2]=A2002; + BWrite[x+2]=B2002; + ARead[x+3]=A200x; + BWrite[x+3]=B2003; + ARead[x+4]=A200x; + BWrite[x+4]=B2004; + ARead[x+5]=A200x; + BWrite[x+5]=B2005; + ARead[x+6]=A200x; + BWrite[x+6]=B2006; + ARead[x+7]=A2007; + BWrite[x+7]=B2007; + } + + BWrite[0x4014]=B4014; + SetNESSoundMap(); + InitializeInput(); +} + +int GameLoaded=0; +void CloseGame(void) +{ + if(GameLoaded) + { + if(FCEUGameInfo.type!=GIT_NSF) + FlushGameCheats(); + #ifdef NETWORK + if(FSettings.NetworkPlay) KillNetplay(); + #endif + GameInterface(GI_CLOSE); + CloseGenie(); + GameLoaded=0; + } +} + +void ResetGameLoaded(void) +{ + if(GameLoaded) CloseGame(); + GameStateRestore=0; + PPU_hook=0; + GameHBIRQHook=0; + GameExpSound.Fill=0; + GameExpSound.RChange=0; + if(GameExpSound.Kill) + GameExpSound.Kill(); + GameExpSound.Kill=0; + MapIRQHook=0; + MMC5Hack=0; + PAL&=1; + pale=0; + + FCEUGameInfo.name=0; + FCEUGameInfo.type=GIT_CART; + FCEUGameInfo.vidsys=GIV_USER; + FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=-1; + FCEUGameInfo.inputfc=-1; +} + +FCEUGI *FCEUI_LoadGame(char *name) +{ + int fp; + + Exit=1; + ResetGameLoaded(); + + fp=FCEU_fopen(name,"rb"); + if(!fp) + { + FCEU_PrintError("Error opening \"%s\"!",name); + return 0; + } + + GetFileBase(name); + if(iNESLoad(name,fp)) + goto endlseq; + if(NSFLoad(fp)) + goto endlseq; + if(FDSLoad(name,fp)) + goto endlseq; + if(UNIFLoad(name,fp)) + goto endlseq; + + FCEU_PrintError("An error occurred while loading the file."); + FCEU_fclose(fp); + return 0; + + endlseq: + FCEU_fclose(fp); + GameLoaded=1; + + FCEU_ResetVidSys(); + if(FCEUGameInfo.type!=GIT_NSF) + if(FSettings.GameGenie) + OpenGenie(); + + PowerNES(); + #ifdef NETWORK + if(FSettings.NetworkPlay) InitNetplay(); + #endif + SaveStateRefresh(); + if(FCEUGameInfo.type!=GIT_NSF) + { + LoadGamePalette(); + LoadGameCheats(); + } + + FCEU_ResetPalette(); + Exit=0; + return(&FCEUGameInfo); +} + + +void FCEU_ResetVidSys(void) +{ + int w; + + if(FCEUGameInfo.vidsys==GIV_NTSC) + w=0; + else if(FCEUGameInfo.vidsys==GIV_PAL) + w=1; + else + w=FSettings.PAL; + + if(w) + { + PAL=1; + scanlines_per_frame=312; + FSettings.FirstSLine=FSettings.UsrFirstSLine[1]; + FSettings.LastSLine=FSettings.UsrLastSLine[1]; + } + else + { + PAL=0; + scanlines_per_frame=262; + FSettings.FirstSLine=FSettings.UsrFirstSLine[0]; + FSettings.LastSLine=FSettings.UsrLastSLine[0]; + } + SetSoundVariables(); +} + +int FCEUI_Initialize(void) +{ + if(!InitVirtualVideo()) + return 0; + memset(&FSettings,0,sizeof(FSettings)); + FSettings.UsrFirstSLine[0]=8; + FSettings.UsrFirstSLine[1]=0; + FSettings.UsrLastSLine[0]=FSettings.UsrLastSLine[1]=239; + FSettings.SoundVolume=65536; // 100% + return 1; +} + +#define harko 0xe //0x9 +static INLINE void Thingo(void) +{ + Loop6502(); + + if(tosprite>=256) + { + X6502_Run(256-harko); + Fixit1(); + X6502_Run(harko); + } + else + { + if(tosprite<=240) + { + X6502_Run(tosprite); + PPU[2]|=0x40; + X6502_Run(256-tosprite-harko); + Fixit1(); + X6502_Run(harko); + } + else + { + X6502_Run(256-harko); + Fixit1(); + X6502_Run(tosprite-(256-harko)); + PPU[2]|=0x40; + X6502_Run(256-tosprite); + } + tosprite=256; + } + DoHBlank(); +} +#undef harko + +void EmLoop(void) +{ + for(;;) + { + ApplyPeriodicCheats(); + X6502_Run(256+85); + + PPU[2]|=0x80; + PPU[3]=PPUSPL=0; /* Not sure if this is correct. According + to Matt Conte and my own tests, it is. Timing is probably + off, though. NOTE: Not having this here + breaks a Super Donkey Kong game. */ + + X6502_Run(12); /* I need to figure out the true nature and length + of this delay. + */ + if(FCEUGameInfo.type==GIT_NSF) + TriggerNMINSF(); + else if(VBlankON) + TriggerNMI(); + + X6502_Run((scanlines_per_frame-242)*(256+85)-12); + + PPU_status&=0x1f; + + X6502_Run(256); + { + static int kook=0; + if(ScreenON || SpriteON) + if(GameHBIRQHook) + GameHBIRQHook(); + + X6502_Run(85-kook); + kook=(kook+1)&1; + } + + if(ScreenON || SpriteON) + { + RefreshAddr=TempAddr; + if(PPU_hook) PPU_hook(RefreshAddr&0x3fff); + } + if(FCEUGameInfo.type==GIT_NSF) + X6502_Run((256+85)*240); + else + { + int x,max,maxref; + + deemp=PPU[1]>>5; + for(scanline=0;scanline<240;scanline++) + { + deempcnt[deemp]++; + Thingo(); + } + for(x=1,max=0,maxref=0;x<7;x++) + { + if(deempcnt[x]>max) + { + max=deempcnt[x]; + maxref=x; + } + deempcnt[x]=0; + } + //FCEU_DispMessage("%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x %d",deempcnt[0],deempcnt[1],deempcnt[2],deempcnt[3],deempcnt[4],deempcnt[5],deempcnt[6],deempcnt[7],maxref); + //memset(deempcnt,0,sizeof(deempcnt)); + SetNESDeemph(maxref,0); + } + + { + int ssize; + + ssize=FlushEmulateSound(); + + #ifdef FRAMESKIP + if(FSkip) + { + FCEU_PutImageDummy(); + FSkip--; + FCEUD_Update(0,WaveFinal,ssize); + } + else + #endif + { + FCEU_PutImage(); + FCEUD_Update(XBuf+8,WaveFinal,ssize); + } + UpdateInput(); + } + + if(Exit) + { + CloseGame(); + break; + } + + } +} + +#ifdef FPS +#include +uint64 frcount; +#endif +void FCEUI_Emulate(void) +{ + #ifdef FPS + uint64 starttime,end; + struct timeval tv; + frcount=0; + gettimeofday(&tv,0); + starttime=((uint64)tv.tv_sec*1000000)+tv.tv_usec; + #endif + EmLoop(); + + #ifdef FPS + // Probably won't work well on Windows port; for + // debugging/speed testing. + { + uint64 w; + int i,frac; + gettimeofday(&tv,0); + end=((uint64)tv.tv_sec*1000000)+tv.tv_usec; + w=frcount*10000000000LL/(end-starttime); + i=w/10000; + frac=w-i*10000; + printf("Average FPS: %d.%04d\n",i,frac); + } + #endif + +} + +void FCEUI_CloseGame(void) +{ + Exit=1; +} + +static void ResetPPU(void) +{ + VRAMBuffer=PPU[0]=PPU[1]=PPU[2]=PPU[3]=0; + PPUSPL=0; + PPUGenLatch=0; + RefreshAddr=TempAddr=0; + vtoggle = 0; +} + +static void PowerPPU(void) +{ + memset(NTARAM,0x00,0x800); + memset(PALRAM,0x00,0x20); + memset(SPRAM,0x00,0x100); + ResetPPU(); +} + +void ResetNES(void) +{ + if(!GameLoaded || (FCEUGameInfo.type==GIT_NSF)) return; + GameInterface(GI_RESETM2); + ResetSound(); + ResetPPU(); + X6502_Reset(); +} + +void PowerNES(void) +{ + if(!GameLoaded) return; + + FCEU_CheatResetRAM(); + FCEU_CheatAddRAM(2,0,RAM); + + GeniePower(); + + memset(RAM,0x00,0x800); + ResetMapping(); + GameInterface(GI_POWER); + PowerSound(); + PowerPPU(); + timestampbase=0; + X6502_Power(); +} + diff --git a/fce.h b/fce.h new file mode 100644 index 0000000..ba90d75 --- /dev/null +++ b/fce.h @@ -0,0 +1,81 @@ +#ifndef _FCEH +extern int GameLoaded; +void ResetGameLoaded(void); + +#define DECLFR(x) uint8 FP_FASTAPASS(1) x (uint32 A) +#define DECLFW(x) void FP_FASTAPASS(2) x (uint32 A, uint8 V) + +void FASTAPASS(3) SetReadHandler(int32 start, int32 end, readfunc func); +void FASTAPASS(3) SetWriteHandler(int32 start, int32 end, writefunc func); +writefunc FASTAPASS(1) GetWriteHandler(int32 a); +readfunc FASTAPASS(1) GetReadHandler(int32 a); + +int AllocGenieRW(void); +void FlushGenieRW(void); + +void FCEU_ResetVidSys(void); + +void ResetMapping(void); +void ResetNES(void); +void PowerNES(void); + + +extern uint64 timestampbase; +extern uint32 MMC5HackVROMMask; +extern uint8 *MMC5HackExNTARAMPtr; +extern int MMC5Hack; +extern uint8 *MMC5HackVROMPTR; +extern uint8 MMC5HackCHRMode; +extern uint8 MMC5HackSPMode; +extern uint8 MMC5HackSPScroll; +extern uint8 MMC5HackSPPage; + +extern uint8 RAM[0x800],SPRAM[0x100],NTARAM[0x800],PALRAM[0x20],SPRAM[0x100],SPRBUF[0x100]; +extern uint8 GameMemBlock[131072]; + +extern uint32 RefreshAddr,TempAddr; +extern uint8 vtoggle,XOffset,VRAMBuffer,PPUGenLatch; +extern uint8 PPU[4]; + +extern int scanline; +extern uint8 *vnapage[4]; + +extern uint8 PPUNTARAM; +extern uint8 PPUCHRRAM; +extern uint8 VPAL[8]; +extern uint8 PAL; + +extern readfunc ARead[0x10000]; +extern writefunc BWrite[0x10000]; + +#define VBlankON (PPU[0]&0x80) /* Generate VBlank NMI */ +#define SpHitON (PPU[0]&0x40) +#define Sprite16 (PPU[0]&0x20) /* Sprites 8x16/8x8 */ +#define BGAdrHI (PPU[0]&0x10) /* BG pattern adr $0000/$1000 */ +#define SpAdrHI (PPU[0]&0x08) /* Sprite pattern adr $0000/$1000 */ +#define INC32 (PPU[0]&0x04) /* auto increment 1/32 */ +#define NameTable (PPU[0]&0x3) /* name table $2000/$2400/$2800/$2C00 */ + +#define SpriteON (PPU[1]&0x10) /* Show Sprite */ +#define ScreenON (PPU[1]&0x08) /* Show screen */ +#define PPU_status (PPU[2]) + + +extern void (*GameInterface)(int h); +extern void FP_FASTAPASS(1) (*PPU_hook)(uint32 A); +extern void (*GameHBIRQHook)(void); +extern void (*GameStateRestore)(int version); + +#define GI_RESETM2 1 +#define GI_POWER 2 +#define GI_CLOSE 3 + +#include "git.h" +extern FCEUGI FCEUGameInfo; +extern int GameAttributes; + + +#endif + +#define _FCEH + diff --git a/fceline.h b/fceline.h new file mode 100644 index 0000000..5ba0ab7 --- /dev/null +++ b/fceline.h @@ -0,0 +1,112 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifdef C80x86 +{ + int dummy,dummy1,dummy2; + __asm__ __volatile__( + "xorl %%edx,%%edx\n\t" + "movb (%%esi),%%cl\n\t" + "movb 8(%%esi),%%dl\n\t" + "movl %%ebx,%%esi\n\t" + "addl %%eax,%%esi\n\t" + "xorl %%ebx,%%ebx\n\t" + "movb %%cl,%%bl\n\t" + "movb %%dl,%%al\n\t" + "shrb $1,%%bl\n\t" + "andb $0xaa,%%al\n\t" + "andb $0x55,%%bl\n\t" + + "andb $0x55,%%cl\n\t" + "shlb $1,%%dl\n\t" + "andb $0xaa,%%dl\n\t" + "orb %%al, %%bl\n\t" // Stick c1 into bl + "orb %%cl, %%dl\n\t" // Stick c2 into dl + "xorl %%eax, %%eax\n\t" + "xorl %%ecx, %%ecx\n\t" + /* At this point, bl contains c1, and dl contains c2 */ + /* and edi contains P, esi contains VRAM[] */ + /* al will be used for zz, cl will be used for zz2 */ + "movb %%bl,%%al\n\t" + "movb %%dl,%%cl\n\t" + "andb $3,%%al\n\t" + "andb $3,%%cl\n\t" + "movb (%%esi,%%eax),%%bh\n\t" + "movb (%%esi,%%ecx),%%dh\n\t" + "movb %%bh,6(%%edi)\n\t" + "movb %%dh,7(%%edi)\n\t" + + "movb %%bl,%%al\n\t" + "movb %%dl,%%cl\n\t" + "shrb $2,%%al\n\t" + "shrb $2,%%cl\n\t" + "andb $3,%%al\n\t" + "andb $3,%%cl\n\t" + "movb (%%esi,%%eax),%%bh\n\t" + "movb (%%esi,%%ecx),%%dh\n\t" + "movb %%bh,4(%%edi)\n\t" + "movb %%dh,5(%%edi)\n\t" + + "movb %%bl,%%al\n\t" + "movb %%dl,%%cl\n\t" + "shrb $4,%%al\n\t" + "shrb $4,%%cl\n\t" + "andb $3,%%al\n\t" + "andb $3,%%cl\n\t" + "movb (%%esi,%%eax),%%bh\n\t" + "movb (%%esi,%%ecx),%%dh\n\t" + "movb %%bh,2(%%edi)\n\t" + "movb %%dh,3(%%edi)\n\t" + +// "movb %%bl,%%al\n\t" +// "movb %%dl,%%cl\n\t" + "xorb %%bh,%%bh\n\t" + "xorb %%dh,%%dh\n\t" + "shrb $6,%%bl\n\t" + "shrb $6,%%dl\n\t" + "movb (%%esi,%%ebx),%%al\n\t" + "movb (%%esi,%%edx),%%cl\n\t" + "movb %%al,0(%%edi)\n\t" + "movb %%cl,1(%%edi)\n\t" + : "=S" (dummy), "=a" (dummy1), "=b" (dummy2) + : "D" (P), "S" (C), "a" (cc), "b" (PALRAM) + : "%ecx", "%edx" + ); + +} +#else + { + uint8 *S=PALRAM+cc; + register uint8 c1,c2; + + c1=((C[0]>>1)&0x55)|(C[8]&0xAA); + c2=(C[0]&0x55)|((C[8]<<1)&0xAA); + + P[6]=S[c1&3]; + P[7]=S[c2&3]; + P[4]=S[(c1>>2)&3]; + P[5]=S[(c2>>2)&3]; + P[2]=S[(c1>>4)&3]; + P[3]=S[(c2>>4)&3]; + + P[0]=S[c1>>6]; + P[1]=S[c2>>6]; + } +#endif diff --git a/fds.c b/fds.c new file mode 100644 index 0000000..07445e7 --- /dev/null +++ b/fds.c @@ -0,0 +1,709 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "x6502.h" +#include "version.h" +#include "fce.h" +#include "fds.h" +#include "svga.h" +#include "sound.h" +#include "general.h" +#include "state.h" +#include "file.h" +#include "memory.h" +#include "cart.h" + +/* TODO: Add code to put a delay in between the time a disk is inserted + and the when it can be successfully read/written to. This should + prevent writes to wrong places OR add code to prevent disk ejects + when the virtual motor is on(mmm...virtual motor). +*/ + +static DECLFR(FDSRead4030); +static DECLFR(FDSRead4031); +static DECLFR(FDSRead4032); +static DECLFR(FDSRead4033); +static DECLFW(FDSWrite4020); +static DECLFW(FDSWrite4021); +static DECLFW(FDSWrite4022); +static DECLFW(FDSWrite4023); +static DECLFW(FDSWrite4024); +static DECLFW(FDSWrite4025); +static DECLFW(FDSWaveWrite); +static DECLFR(FDSWaveRead); + +static DECLFR(FDSSRead); +static DECLFW(FDSSWrite); +static DECLFR(FDSBIOSRead); +static DECLFR(FDSRAMRead); +static DECLFW(FDSRAMWrite); +static void FDSInit(void); +static void FDSReset(void); +static void FP_FASTAPASS(1) FDSFix(int a); + +#define FDSRAM GameMemBlock +#define mapbyte1 (GameMemBlock+32768) +#define mapbyte2 (GameMemBlock+32768+8) +#define mapbyte3 (GameMemBlock+32768+16) +#define mapbyte4 (GameMemBlock+32768+24) // 8 bytes +#define CHRRAM (GameMemBlock+32768+32+64) + +#define IRQLatch (*(int32*)(CHRRAM+8192)) +#define IRQCount (*(int32*)(CHRRAM+8192+4)) +#define IRQa (*(CHRRAM+8192+8)) + +static void FDSClose(void); +static uint8 header[16]; +#define writeskip mapbyte2[0] + +static char FDSSaveName[2048]; + +uint8 FDSBIOS[8192]; +uint8 *diskdata[4]={0,0,0,0}; + +#define SideWrite mapbyte2[1] +#define DiskPtr (*(uint32*)(mapbyte2+4)) +#define dsr0 mapbyte2[2] +#define dsr1 mapbyte2[3] + +#define DC_INC 1 + +#define DiskSeekIRQ (*(int32*)(mapbyte3+4)) +#define SelectDisk mapbyte3[0] +#define InDisk mapbyte3[1] + +static void FDSReset(void) +{ + memset(mapbyte1,0,8); + memset(mapbyte2,0,8); + memset(mapbyte3+4,0,4); + memset(mapbyte4,0,8); +} + +void FDSGI(int h) +{ + switch(h) + { + case GI_CLOSE: FDSClose();break; + case GI_POWER: FDSReset();FDSInit();break; + } +} + +static void FDSStateRestore(int version) +{ + setmirror(((mapbyte1[5]&8)>>3)^1); +} + +void FDSSound(); +void FDSSoundReset(void); +void FDSSoundStateAdd(void); +static void RenderSound(void); + +static void FDSInit(void) +{ + dsr0=0; + dsr1=0x41; + setmirror(1); + if(InDisk!=255) + dsr1&=0xFE; + + setprg8r(0,0xe000,0); // BIOS + setprg32r(1,0x6000,0); // 32KB RAM + setchr8(0); // 8KB CHR RAM + + MapIRQHook=FDSFix; + GameStateRestore=FDSStateRestore; + + SetReadHandler(0x4030,0x4030,FDSRead4030); + SetReadHandler(0x4031,0x4031,FDSRead4031); + SetReadHandler(0x4032,0x4032,FDSRead4032); + SetReadHandler(0x4033,0x4033,FDSRead4033); + + SetWriteHandler(0x4020,0x4020,FDSWrite4020); + SetWriteHandler(0x4021,0x4021,FDSWrite4021); + SetWriteHandler(0x4022,0x4022,FDSWrite4022); + SetWriteHandler(0x4023,0x4023,FDSWrite4023); + SetWriteHandler(0x4024,0x4024,FDSWrite4024); + SetWriteHandler(0x4025,0x4025,FDSWrite4025); + + SetWriteHandler(0x6000,0xdfff,FDSRAMWrite); + SetReadHandler(0x6000,0xdfff,FDSRAMRead); + SetReadHandler(0xE000,0xFFFF,FDSBIOSRead); + + IRQCount=IRQLatch=IRQa=0; + + FDSSoundReset(); +} + +void FDSControl(int what) +{ + switch(what) + { + case FDS_IDISK:dsr1&=0xFE; + if(InDisk==255) + { + FCEU_DispMessage("Disk %d Side %s Inserted", + SelectDisk>>1,(SelectDisk&1)?"B":"A"); + InDisk=SelectDisk; + } + else + FCEU_DispMessage("Jamming disks is a BAD IDEA"); + break; + case FDS_EJECT: + if(InDisk!=255) + FCEU_DispMessage("Disk Ejected"); + else + FCEU_DispMessage("Cannot Eject Air"); + dsr1|=1; + InDisk=255; + break; + case FDS_SELECT: + if(InDisk!=255) + { + FCEU_DispMessage("Eject disk before selecting."); + break; + } + SelectDisk=((SelectDisk+1)%header[4])&3; + FCEU_DispMessage("Disk %d Side %s Selected", + SelectDisk>>1,(SelectDisk&1)?"B":"A"); + break; + } +} + +static void FP_FASTAPASS(1) FDSFix(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0) + { + IRQa=0; + dsr0|=1; + dsr0&=~2; + IRQCount=0xFFFF; + X6502_IRQBegin(FCEU_IQEXT); + } + } + if(DiskSeekIRQ>0) + { + DiskSeekIRQ-=a; + if(DiskSeekIRQ<=0) + { + if(mapbyte1[5]&0x80) + { + dsr0&=~1; + dsr0|=2; + TriggerIRQ(); + } + } + } +} + +void DiskControl(int which) +{ +if(mapbyte1[5]&1) + { + switch(which) + { + case DC_INC: + if(DiskPtr<64999) DiskPtr++; + //DiskSeekIRQ=160+100; + //DiskSeekIRQ=140; + //DiskSeekIRQ=160; + DiskSeekIRQ=150; + break; + } + } +} +static DECLFR(FDSRead4030) +{ + X6502_IRQEnd(FCEU_IQEXT); + return dsr0; +} + +static DECLFR(FDSRead4031) +{ + static uint8 z=0; + if(InDisk!=255) + { + z=diskdata[InDisk][DiskPtr]; + DiskControl(DC_INC); + } + return z; +} + +static DECLFR(FDSRead4032) +{ + return dsr1; +} + +static DECLFR(FDSRead4033) +{ + return 0x80; // battery +} + +static DECLFW(FDSRAMWrite) +{ + (FDSRAM-0x6000)[A]=V; +} + +static DECLFR(FDSBIOSRead) +{ + return (FDSBIOS-0xE000)[A]; +} + +static DECLFR(FDSRAMRead) +{ + return (FDSRAM-0x6000)[A]; +} + +/* Begin FDS sound */ + +#define FDSClock (1789772.7272727272727272/8) + +typedef struct { + int64 cycles; // Cycles per PCM sample + int64 count; // Cycle counter + int64 envcount; // Envelope cycle counter + uint32 b19shiftreg60; + uint32 b24adder66; + uint32 b24latch68; + uint32 b17latch76; + int32 clockcount; // Counter to divide frequency by 8. + uint8 b8shiftreg88; // Modulation register. + uint8 amplitude[2]; // Current amplitudes. + uint8 mwave[0x20]; // Modulation waveform + uint8 cwave[0x40]; // Game-defined waveform(carrier) + uint8 SPSG[0xB]; +} FDSSOUND; + +static FDSSOUND fdso; + +#define SPSG fdso.SPSG +#define b19shiftreg60 fdso.b19shiftreg60 +#define b24adder66 fdso.b24adder66 +#define b24latch68 fdso.b24latch68 +#define b17latch76 fdso.b17latch76 +#define b8shiftreg88 fdso.b8shiftreg88 +#define clockcount fdso.clockcount +#define amplitude fdso.amplitude + +void FDSSoundStateAdd(void) +{ + AddExState(fdso.cwave,64,0,"WAVE"); + AddExState(fdso.mwave,32,0,"MWAV"); + AddExState(amplitude,2,0,"AMPL"); + AddExState(SPSG,0xB,0,"SPSG"); + + AddExState(&b8shiftreg88,1,0,"B88"); + + AddExState(&clockcount, 4, 1, "CLOC"); + AddExState(&b19shiftreg60,4,1,"B60"); + AddExState(&b24adder66,4,1,"B66"); + AddExState(&b24latch68,4,1,"B68"); + AddExState(&b17latch76,4,1,"B76"); + +} + +static DECLFR(FDSSRead) +{ + switch(A&0xF) + { + case 0x0:return(amplitude[0]|(X.DB&0xC0)); + case 0x2:return(amplitude[1]|(X.DB&0xC0)); + } + return(X.DB); +} + +static DECLFW(FDSSWrite) +{ + if(FSettings.SndRate) + RenderSound(); + A-=0x4080; + switch(A) + { + case 0x0: + case 0x4: + if(!(V&0x80)) + { + // if(V&0x40) amplitude[(A&0xF)>>2]=0; + // else amplitude[(A&0xF)>>2]=0x3F; + } + else + amplitude[(A&0xF)>>2]=V&0x3F; + break; + case 0x7: b17latch76=0;SPSG[0x5]=0;break; + case 0x8: + //printf("%d:$%02x\n",SPSG[0x5],V); + fdso.mwave[SPSG[0x5]&0x1F]=V&0x7; + SPSG[0x5]=(SPSG[0x5]+1)&0x1F; + break; + } + //if(A>=0x7 && A!=0x8 && A<=0xF) + //if(A==0xA || A==0x9) printf("$%04x:$%02x\n",A,V); + SPSG[A]=V; +} + +// $4080 - Fundamental wave amplitude data register 92 +// $4082 - Fundamental wave frequency data register 58 +// $4083 - Same as $4082($4083 is the upper 4 bits). + +// $4084 - Modulation amplitude data register 78 +// $4086 - Modulation frequency data register 72 +// $4087 - Same as $4086($4087 is the upper 4 bits) + + +static void DoEnv() +{ + int x; + + for(x=0;x<2;x++) + if(!(SPSG[x<<2]&0x80) && !(SPSG[0x9+x]&0x80)) + { + static int counto[2]={0,0}; + + if(counto[x]<=0) + { + if(SPSG[x<<2]&0x40) + { + if(amplitude[x]<0x3F) + amplitude[x]++; + } + else + { + if(amplitude[x]>0) + amplitude[x]--; + } + counto[x]=(SPSG[x<<2]&0x3F); + } + else + counto[x]--; + } +} + +static DECLFR(FDSWaveRead) +{ + return(fdso.cwave[A&0x3f]|(X.DB&0xC0)); +} + +static DECLFW(FDSWaveWrite) +{ + if(SPSG[0x9]&0x80) + fdso.cwave[A&0x3f]=V&0x3F; +} + +static INLINE void ClockRise(void) +{ + if(!clockcount) + { + b19shiftreg60=(SPSG[0x2]|((SPSG[0x3]&0xF)<<8)); + b17latch76=(SPSG[0x6]|((SPSG[0x07]&0x3)<<8))+b17latch76; + + if(!(SPSG[0x7]&0x80)) + { + b8shiftreg88=(amplitude[1]*((fdso.mwave[(b17latch76>>11)&0x1F]&7))); + //b8shiftreg88=((fdso.mwave[(b17latch76>>11)&0x1F]&7))|(amplitude[1]<<3); + } + else + { b8shiftreg88=0;} + } + else + { + b19shiftreg60<<=1; + b8shiftreg88>>=1; + } + b24adder66=(b24latch68+b19shiftreg60)&0xFFFFFF; +} + +static INLINE void ClockFall(void) +{ +// if(!(SPSG[0x7]&0x80)) + { + if(!(b8shiftreg88&1)) + b24latch68=b24adder66; + } + clockcount=(clockcount+1)&7; +} + +static INLINE int32 FDSDoSound(void) +{ + fdso.count+=fdso.cycles; + if(fdso.count>=((int64)1<<40)) + { + dogk: + fdso.count-=(int64)1<<40; + ClockRise(); + ClockFall(); + } + if(fdso.count>=32768) goto dogk; + + fdso.envcount-=fdso.cycles; + if(fdso.envcount<=0) + { + // Fix this? + fdso.envcount+=((int64)1<<40)*FDSClock/1024; + DoEnv(); + } + + // Might need to emulate applying the amplitude to the waveform a bit better... + return (fdso.cwave[b24latch68>>18]*amplitude[0]); +} + +static int32 FBC=0; + +static void RenderSound(void) +{ + int32 end, start; + int32 x; + + start=FBC; + end=(timestamp<<16)/soundtsinc; + if(end<=start) + return; + FBC=end; + + if(!(SPSG[0x9]&0x80)) + for(x=start;x>4]+=t>>3; + } +} + +void FDSSound(int c) +{ + RenderSound(); + FBC=c; +} + +static void FDS_ESI(void) +{ + if(FSettings.SndRate) + { + fdso.cycles=((int64)1<<40)*FDSClock; + fdso.cycles/=FSettings.SndRate OVERSAMPLE; + } +// fdso.cycles=(int64)32768*FDSClock/(FSettings.SndRate OVERSAMPLE); + SetReadHandler(0x4040,0x407f,FDSWaveRead); + SetWriteHandler(0x4040,0x407f,FDSWaveWrite); + SetWriteHandler(0x4080,0x408A,FDSSWrite); + SetReadHandler(0x4090,0x4092,FDSSRead); +} + +void FDSSoundReset(void) +{ + memset(&fdso,0,sizeof(fdso)); + FDS_ESI(); + GameExpSound.Fill=FDSSound; + GameExpSound.RChange=FDS_ESI; +} + + +static DECLFW(FDSWrite4020) +{ + X6502_IRQEnd(FCEU_IQEXT); + IRQLatch&=0xFF00; + IRQLatch|=V; + mapbyte1[0]=V; +} +static DECLFW(FDSWrite4021) +{ + X6502_IRQEnd(FCEU_IQEXT); + IRQLatch&=0xFF; + IRQLatch|=V<<8; + mapbyte1[1]=V; +} +static DECLFW(FDSWrite4022) +{ + X6502_IRQEnd(FCEU_IQEXT); + IRQCount=IRQLatch; + IRQa=V&2; + mapbyte1[2]=V; +} +static DECLFW(FDSWrite4023) +{ + mapbyte1[3]=V; +} +static DECLFW(FDSWrite4024) +{ + if(InDisk!=255 && !(mapbyte1[5]&0x4) && mapbyte1[3]&0x1) + { + if(DiskPtr>=0 && DiskPtr<65000) + { + if(writeskip) writeskip--; + else if(DiskPtr>=2) + { + SideWrite|=1<>3)&1)^1); +} +static void FreeFDSMemory(void) +{ + int x; + + for(x=0;x4) header[4]=4; + if(!header[4]) header[4]|=1; + for(x=0;x +#include +#include + +#ifdef ZLIB + #include + #include "zlib/unzip.h" +#endif + +#include "types.h" +#include "file.h" +#include "endian.h" +#include "memory.h" +#include "driver.h" + +static void *desctable[8]={0,0,0,0,0,0,0,0}; +static int x; + +#ifdef ZLIB + +typedef struct { + uint8 *data; + uint32 size; + uint32 location; +} ZIPWRAP; + +void *MakeZipWrap(void *tz) +{ + unz_file_info ufo; + ZIPWRAP *tmp; + + if(!(tmp=FCEU_malloc(sizeof(ZIPWRAP)))) + goto doret; + + unzGetCurrentFileInfo(tz,&ufo,0,0,0,0,0,0); + + tmp->location=0; + tmp->size=ufo.uncompressed_size; + if(!(tmp->data=FCEU_malloc(ufo.uncompressed_size))) + { + tmp=0; + goto doret; + } + + unzReadCurrentFile(tz,tmp->data,ufo.uncompressed_size); + + doret: + + unzCloseCurrentFile(tz); + unzClose(tz); + + return tmp; +} +#endif + +#ifndef __GNUC__ + #define strcasecmp strcmp +#endif + +int FASTAPASS(2) FCEU_fopen(char *path, char *mode) +{ + void *t; + + #ifdef ZLIB + unzFile tz; + if((tz=unzOpen(path))) // If it's not a zip file, use regular file handlers. + // Assuming file type by extension usually works, + // but I don't like it. :) + { + if(unzGoToFirstFile(tz)==UNZ_OK) + { + for(;;) + { + char tempu[512]; // Longer filenames might be possible, but I don't + // think people would name files that long in zip files... + unzGetCurrentFileInfo(tz,0,tempu,512,0,0,0,0); + tempu[511]=0; + if(strlen(tempu)>=4) + { + char *za=tempu+strlen(tempu)-4; + if(!strcasecmp(za,".nes") || !strcasecmp(za,".fds") || + !strcasecmp(za,".nsf") || !strcasecmp(za,".unf") || + !strcasecmp(za,".nez")) + break; + } + if(strlen(tempu)>=5) + { + if(!strcasecmp(tempu+strlen(tempu)-5,".unif")) + break; + } + if(unzGoToNextFile(tz)!=UNZ_OK) + { + if(unzGoToFirstFile(tz)!=UNZ_OK) goto zpfail; + break; + } + } + if(unzOpenCurrentFile(tz)!=UNZ_OK) + goto zpfail; + } + else + { + zpfail: + unzClose(tz); + return 0; + } + + for(x=0;x<8;x++) + if(!desctable[x]) + { + if(!(desctable[x]=MakeZipWrap(tz))) + return(0); + return((x+1)|0x8000); + } + } +#endif + + #ifdef ZLIB + if((t=fopen(path,"rb"))) + { + uint32 magic; + + magic=fgetc(t); + magic|=fgetc(t)<<8; + magic|=fgetc(t)<<16; + + fclose(t); + + if(magic==0x088b1f) + { + if((t=gzopen(path,mode))) + for(x=0;x<8;x++) + if(!desctable[x]) + { + desctable[x]=t; + return((x+1)|0x4000); + } + } + } + #endif + + if((t=fopen(path,mode))) + { + fseek(t,0,SEEK_SET); + for(x=0;x<8;x++) + if(!desctable[x]) + { + desctable[x]=t; + return(x+1); + } + } + return 0; +} + +int FASTAPASS(1) FCEU_fclose(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + gzclose(desctable[(stream&255)-1]); + desctable[(stream&255)-1]=0; + } + else if(stream&0x8000) + { + free(((ZIPWRAP*)desctable[(stream&255)-1])->data); + free(desctable[(stream&255)-1]); + desctable[(stream&255)-1]=0; + } + else // close zip file + { + #endif + fclose(desctable[stream-1]); + desctable[stream-1]=0; + #ifdef ZLIB + } + #endif + return 1; +} + +size_t FASTAPASS(3) FCEU_fread(void *ptr, size_t size, size_t nmemb, int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gzread(desctable[(stream&255)-1],ptr,size*nmemb); + } + else if(stream&0x8000) + { + ZIPWRAP *wz; + uint32 total=size*nmemb; + + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + if(wz->location>=wz->size) return 0; + + if((wz->location+total)>wz->size) + { + int ak=wz->size-wz->location; + memcpy((uint8*)ptr,wz->data+wz->location,ak); + wz->location=wz->size; + return(ak/size); + } + else + { + memcpy((uint8*)ptr,wz->data+wz->location,total); + wz->location+=total; + return nmemb; + } + } + else + { + #endif + return fread(ptr,size,nmemb,desctable[stream-1]); + #ifdef ZLIB + } + #endif +} + +size_t FASTAPASS(3) FCEU_fwrite(void *ptr, size_t size, size_t nmemb, int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gzwrite(desctable[(stream&255)-1],ptr,size*nmemb); + } + else if(stream&0x8000) + { + return 0; + } + else + #endif + return fwrite(ptr,size,nmemb,desctable[stream-1]); +} + +int FASTAPASS(3) FCEU_fseek(int stream, long offset, int whence) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gzseek(desctable[(stream&255)-1],offset,whence); + } + else if(stream&0x8000) + { + ZIPWRAP *wz; + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + + switch(whence) + { + case SEEK_SET:if(offset>=wz->size) + return(-1); + wz->location=offset;break; + case SEEK_CUR:if(offset+wz->location>wz->size) + return (-1); + wz->location+=offset; + break; + } + return 0; + } + else + #endif + return fseek(desctable[stream-1],offset,whence); +} + +long FASTAPASS(1) FCEU_ftell(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + return gztell(desctable[(stream&255)-1]); + } + else if(stream&0x8000) + { + return (((ZIPWRAP *)desctable[(stream&255)-1])->location); + } + else + #endif + return ftell(desctable[stream-1]); +} + +void FASTAPASS(1)FCEU_rewind(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + gzrewind(desctable[(stream&255)-1]); + } + else if(stream&0x8000) + { + ((ZIPWRAP *)desctable[(stream&255)-1])->location=0; + } + else + #endif + #ifdef _WIN32_WCE + fseek(desctable[stream-1],0,SEEK_SET); + #else + rewind(desctable[stream-1]); + #endif +} + +int FASTAPASS(2) FCEU_read32(void *Bufo, int stream) +{ + #ifdef ZLIB + if(stream&0xC000) + { + uint8 t[4]; + #ifndef LSB_FIRST + uint8 x[4]; + #endif + if(stream&0x8000) + { + ZIPWRAP *wz; + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + if(wz->location+4>wz->size) + {return 0;} + *(uint32 *)t=*(uint32 *)(wz->data+wz->location); + wz->location+=4; + } + else if(stream&0x4000) + gzread(desctable[(stream&255)-1],&t,4); + #ifndef LSB_FIRST + x[0]=t[3]; + x[1]=t[2]; + x[2]=t[1]; + x[3]=t[0]; + *(uint32*)Bufo=*(uint32*)x; + #else + *(uint32*)Bufo=*(uint32*)t; + #endif + return 1; + } + else + #endif + { + return read32(Bufo,desctable[stream-1]); + } +} + +int FASTAPASS(1) FCEU_fgetc(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + return gzgetc(desctable[(stream&255)-1]); + else if(stream&0x8000) + { + ZIPWRAP *wz; + wz=(ZIPWRAP*)desctable[(stream&255)-1]; + if(wz->locationsize) + return wz->data[wz->location++]; + return EOF; + } + else +#endif + return fgetc(desctable[stream-1]); +} + +long FASTAPASS(1) FCEU_fgetsize(int stream) +{ + #ifdef ZLIB + if(stream&0x4000) + { + int x,t; + t=gztell(desctable[(stream&255)-1]); + gzrewind(desctable[(stream&255)-1]); + for(x=0;gzgetc(desctable[(stream&255)-1]) != EOF; x++); + gzseek(desctable[(stream&255)-1],t,SEEK_SET); + return(x); + } + else if(stream&0x8000) + return ((ZIPWRAP*)desctable[(stream&255)-1])->size; + else + #endif + { + long t,r; + t=ftell(desctable[stream-1]); + fseek(desctable[stream-1],0,SEEK_END); + r=ftell(desctable[stream-1]); + fseek(desctable[stream-1],t,SEEK_SET); + return r; + } +} + +int FASTAPASS(1) FCEU_fisarchive(int stream) +{ + #ifdef ZLIB + if(stream&0x8000) + return 1; + #endif + return 0; +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..3c70f97 --- /dev/null +++ b/file.h @@ -0,0 +1,12 @@ +int FASTAPASS(2) FCEU_fopen(char *path, char *mode); +int FASTAPASS(1) FCEU_fclose(int stream); +size_t FASTAPASS(3) FCEU_fread(void *ptr, size_t size, size_t nmemb, int stream); +size_t FASTAPASS(3) FCEU_fwrite(void *ptr, size_t size, size_t nmemb, int stream); +int FASTAPASS(3) FCEU_fseek(int stream, long offset, int whence); +long FASTAPASS(1) FCEU_ftell(int stream); +void FASTAPASS(1) FCEU_rewind(int stream); +int FASTAPASS(2) FCEU_read32(void *Bufo, int fp); +int FASTAPASS(1) FCEU_fgetc(int stream); +long FASTAPASS(1) FCEU_fgetsize(int stream); +int FASTAPASS(1) FCEU_fisarchive(int stream); + diff --git a/general.c b/general.c new file mode 100644 index 0000000..b0c1942 --- /dev/null +++ b/general.c @@ -0,0 +1,169 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" + +#include "general.h" +#include "state.h" +#include "version.h" +#include "svga.h" +#include "driver.h" + +char *marray[1]={"Error allocating memory!"}; + +static char BaseDirectory[2048]; +static char FileBase[2048]; +static char FileExt[2048]; +static char FileBaseDirectory[2048]; + +void FCEUI_SetBaseDirectory(char *dir) +{ + strncpy(BaseDirectory,dir,2047); + BaseDirectory[2047]=0; +} + +static char *odirs[FCEUIOD__COUNT]={0,0,0,0,0}; // odirs, odors. ^_^ + +void FCEUI_SetDirOverride(int which, char *n) +{ + odirs[which]=n; + if(which==FCEUIOD_STATE) + SaveStateRefresh(); +} + +/* We should probably use snprintf(), but many C libraries don't seem to + have this function. +*/ +char *FCEU_MakeFName(int type, int id1, char *cd1) +{ + static uint8 ret[2048]; + + ret[0]=0; + switch(type) + { + case FCEUMKF_STATE:if(odirs[FCEUIOD_STATE]) + sprintf(ret,"%s"PSS"%s.fc%d",odirs[FCEUIOD_STATE],FileBase,id1); + else + sprintf(ret,"%s"PSS"fcs"PSS"%s.fc%d",BaseDirectory,FileBase,id1); + break; + case FCEUMKF_SNAP: + if(FSettings.SnapName) + { + if(odirs[FCEUIOD_SNAPS]) + sprintf(ret,"%s"PSS"%s-%d.%s",odirs[FCEUIOD_SNAPS],FileBase,id1,cd1); + else + sprintf(ret,"%s"PSS"snaps"PSS"%s-%d.%s",BaseDirectory,FileBase,id1,cd1); + } + else + { + if(odirs[FCEUIOD_SNAPS]) + sprintf(ret,"%s"PSS"%d.%s",odirs[FCEUIOD_SNAPS],id1,cd1); + else + sprintf(ret,"%s"PSS"snaps"PSS"%d.%s",BaseDirectory,id1,cd1); + } + break; + case FCEUMKF_SAV:if(odirs[FCEUIOD_NV]) + { + sprintf(ret,"%s"PSS"%s.%s",odirs[FCEUIOD_NV],FileBase,cd1); + } + else + { + if(FSettings.SUnderBase) + sprintf(ret,"%s"PSS"sav"PSS"%s.%s",BaseDirectory,FileBase,cd1); + else + sprintf(ret,"%s"PSS"%s.%s",FileBaseDirectory,FileBase,cd1); + } + break; + case FCEUMKF_CHEAT: + if(odirs[FCEUIOD_CHEATS]) + sprintf(ret,"%s"PSS"%s.cht",odirs[FCEUIOD_CHEATS],FileBase); + else + sprintf(ret,"%s"PSS"cheats"PSS"%s.cht",BaseDirectory,FileBase); + break; + case FCEUMKF_GGROM:sprintf(ret,"%s"PSS"gg.rom",BaseDirectory);break; + case FCEUMKF_FDSROM:sprintf(ret,"%s"PSS"disksys.rom",BaseDirectory);break; + case FCEUMKF_PALETTE: + if(odirs[FCEUIOD_MISC]) + sprintf(ret,"%s"PSS"%s.pal",odirs[FCEUIOD_MISC],FileBase); + else + sprintf(ret,"%s"PSS"gameinfo"PSS"%s.pal",BaseDirectory,FileBase); + break; + } + return(ret); +} + +void GetFileBase(char *f) +{ + char *tp1,*tp3; + + #if PSS_STYLE==4 + tp1=((char *)strrchr(f,':')); + #elif PSS_STYLE==1 + tp1=((char *)strrchr(f,'/')); + #else + tp1=((char *)strrchr(f,'\\')); + #if PSS_STYLE!=3 + tp3=((char *)strrchr(f,'/')); + if(tp1=0;x--) + if(n&(1< +#include +#include + +#include "types.h" +#include "x6502.h" +#include "fce.h" +#define INESPRIV +#include "ines.h" +#include "version.h" +#include "svga.h" +#include "general.h" +#include "state.h" +#include "file.h" +#include "memory.h" +#include "cart.h" +#include "crc32.h" +#include "cheat.h" + +static DECLFR(VSRead); + +static uint8 *trainerpoo=0; +static uint8 *ROM=NULL; +uint8 *VROM=NULL; + + +static uint32 ROM_size; +uint32 VROM_size; + +static void CheckVSUni(void); +static int MMC_init(int type); +void (*MapClose)(void); +void (*MapperReset)(void); + +static int MapperNo; +static int SaveGame=0; + +static iNES_HEADER head; + +/* MapperReset() is called when the NES is reset(with the reset button). + Mapperxxx_init is called when the NES has been powered on. +*/ + +static void iNESGI(int h) +{ + switch(h) + { + case GI_RESETM2: + if(MapperReset) + MapperReset(); + break; + case GI_POWER: + SetReadHandler(0x8000,0xFFFF,CartBR); + MMC_init(MapperNo); + break; + case GI_CLOSE: + { + FILE *sp; + + if(ROM) {free(ROM);ROM=0;} + if(VROM) {free(VROM);VROM=0;} + + if(SaveGame) + { + char *soot; + SaveGame=0; + soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav"); + sp=fopen(soot,"wb"); + if (sp==NULL) + FCEU_PrintError("WRAM file \"%s\" cannot be written to.\n",soot); + else + { + void *ptr; + uint32 amount; + ptr=WRAM; + amount=8192; + + if(MapperNo==1) + { + extern uint8 MMC1WRAMsize; + if(MMC1WRAMsize==2) ptr=WRAM+8192; + } + else if(MapperNo==5) + { + extern uint8 MMC5WRAMsize; + if(MMC5WRAMsize==4) + amount=32768; + } + + fwrite(ptr,1,amount,sp); + fclose(sp); + } + } + if(MapClose) MapClose(); + if(trainerpoo) {free(trainerpoo);trainerpoo=0;} + } + break; + } +} + +uint32 iNESGameCRC32; + +struct CRCMATCH { + uint32 crc; + char *name; +}; + +static void CheckBad(void) +{ + int x; + #define CRCNT 7 + struct CRCMATCH tab[CRCNT]={ + {0x28d183ac,"Antarctic Adventure"}, + {0x7095ac65,"Akumajo Densetsu"}, + {0x1bd7ed5a,"Gradius 2"}, + {0x81c3aa66,"Crisis Force"}, + {0xfe3088df,"Fire Emblem Gaiden"}, + {0xfa8339a5,"Bucky O'Hare"}, + {0x3c539d78,"Ganbare Goemon 2"}, + }; + for(x=0;x=0 || moo[x].input2>=0 || moo[x].inputfc>=0) + { + if(moo[x].crc32==iNESGameCRC32) + { + FCEUGameInfo.input[0]=moo[x].input1; + FCEUGameInfo.input[1]=moo[x].input2; + FCEUGameInfo.inputfc=moo[x].inputfc; + break; + } + x++; + } +} + +struct CHINF { + uint32 crc32; + int32 mapper; + int32 mirror; +}; + +#define SAVCNT 14 +static void CheckHInfo(void) +{ + /* ROM images that have the battery-backed bit set in the header that really + don't have battery-backed RAM is not that big of a problem, so I'll + treat this differently. + */ + + static uint32 savie[SAVCNT]= + { + 0x7cab2e9b,0x3ee43cda,0xe1383deb, /* Mouryou Senki Madara */ + 0x3b3f88f0,0x2545214c, /* Dragon Warrior PRG 0 and 1 */ + 0x8c5a784e, /* DW 2 */ + 0xa86a5318, /* DW 3 */ + 0x506e259d, /* DW 4 */ + 0xcebd2a31,0xb8b88130, /* Final Fantasy */ + 0x466efdc2, /* FF(J) */ + 0xc9556b36, /* FF1+2*/ + 0xd29db3c7, /* FF2 */ + 0x57e220d0, /* FF3 */ + }; + + static struct CHINF moo[]= + { + {0xc68363f6,180,0}, /* Crazy Climber */ + {0xbe939fce,9,1}, /* Punchout*/ + {0x5e66eaea,13,1}, /* Videomation */ + {0xaf5d7aa2,-1,0}, /* Clu Clu Land */ + + {0xc2730c30,34,0}, /* Deadly Towers */ + {0x932ff06e,34,1}, /* Classic Concentration */ + {0x4c7c1af3,34,1}, /* Caesar's Palace */ + {0x9ea1dc76,2,0}, /* Rainbow Islands */ + + {0x9eefb4b4,4,8}, /* Pachi Slot Adventure 2 */ + {0x5337f73c,4,8}, /* Niji no Silk Road */ + {0x7474ac92,4,8}, /* Kabuki: Quantum Fighter */ + + {0x970bd9c2,1,8}, /* Hanjuku Hero */ + + {0xbb7c5f7a,89,8}, /* Mito Koumon or something similar */ + /* Probably a Namco MMC3-workalike */ + {0xa5e6baf9,4,1|4}, /* Dragon Slayer 4 */ + {0xe40b4973,4,1|4}, /* Metro Cross */ + {0xd97c31b0,4,1|4}, /* Rasaaru Ishii no Childs Quest */ + + {0x84382231,9,0}, /* Punch Out (J) */ + + {0xfcdaca80,0,0}, /* Elevator Action */ + {0xe492d45a,0,0}, /* Zippy Race */ + {0x32fa246f,0,0}, /* Tag Team Pro Wrestling */ + {0x6d65cac6,2,0}, /* Terra Cresta */ + {0x28c11d24,2,1}, /* Sukeban Deka */ + {0x02863604,2,1}, /* Sukeban Deka */ + {0x2bb6a0f8,2,1}, /* Sherlock Holmes */ + {0x55773880,2,1}, /* Gilligan's Island */ + {0x419461d0,2,1}, /* Super Cars */ + {0x6e0eb43e,2,1}, /* Puss n Boots */ + {0xfc3e5c86,2,1}, /* Trojan */ + + {0x291bcd7d,1,8}, /* Pachio Kun 2 */ + {0xf74dfc91,1,-1}, /* Win, Lose, or Draw */ + + {0x59280bec,4,8}, /* Jackie Chan */ + + {0xfe364be5,1,8}, /* Deep Dungeon 4 */ + {0xd8ee7669,1,8}, /* Adventures of Rad Gravity */ + {0xa5e8d2cd,1,8}, /* Breakthru */ + {0xf6fa4453,1,8}, /* Bigfoot */ + {0x37ba3261,1,8}, /* Back to the Future 2 and 3 */ + {0x934db14a,1,-1}, /* All-Pro Basketball */ + {0xe94d5181,1,8}, /* Mirai Senshi - Lios */ + {0x7156cb4d,1,8}, /* Muppet Adventure Carnival thingy */ + {0x5b6ca654,1,8}, /* Barbie rev X*/ + {0x57c12280,1,8}, /* Demon Sword */ + + {0xcf322bb3,3,1}, /* John Elway's Quarterback */ + {0x9bde3267,3,1}, /* Adventures of Dino Riki */ + {0x02cc3973,3,8}, /* Ninja Kid */ + {0xb5d28ea2,3,1}, /* Mystery Quest - mapper 3?*/ + {0xbc065fc3,3,1}, /* Pipe Dream */ + + {0x5b837e8d,1,8}, /* Alien Syndrome */ + {0x283ad224,32,8}, /* Ai Sensei no Oshiete */ + {0x5555fca3,32,8}, /* "" "" */ + {0x243a8735,32,0x10|4}, /* Major League */ + + {0x6bc65d7e,66,1}, /* Youkai Club*/ + {0xc2df0a00,66,1}, /* Bio Senshi Dan(hacked) */ + {0xbde3ae9b,66,1}, /* Doraemon */ + {0xd26efd78,66,1}, /* SMB Duck Hunt */ + {0x811f06d9,66,1}, /* Dragon Power */ + + {0x3293afea,66,1}, /* Mississippi Satsujin Jiken */ + {0xe84274c5,66,1}, /* "" "" */ + + + {0x6e68e31a,16,8}, /* Dragon Ball 3*/ + + {0xba51ac6f,78,2}, + {0x3d1c3137,78,8}, + + {0xbda8f8e4,152,8}, /* Gegege no Kitarou 2 */ + {0x026c5fca,152,8}, /* Saint Seiya Ougon Densetsu */ + {0x0f141525,152,8}, /* Arkanoid 2 (Japanese) */ + {0xb1a94b82,152,8}, /* Pocket Zaurus */ + + {0x3f15d20d,153,8}, /* Famicom Jump 2 */ + + {0xbba58be5,70,-1}, /* Family Trainer - Manhattan Police */ + {0x370ceb65,70,-1}, /* Family Trainer - Meiro Dai Sakusen */ + {0xdd8ed0f7,70,1}, /* Kamen Rider Club */ + + {0x90c773c1,118,-1}, /* Goal! 2 */ + {0xb9b4d9e0,118,-1}, /* NES Play Action Football */ + {0x78b657ac,118,-1}, /* Armadillo */ + {0x37b62d04,118,-1}, /* Ys 3 */ + {0x07d92c31,118,-1}, /* RPG Jinsei Game */ + {0x2705eaeb,234,-1}, /* Maxi 15 */ + {0x404b2e8b,4,2}, /* Rad Racer 2 */ + + {0x1356f6a6,4,8}, /* "Cattou Ninden Teyandee" English tranlation. + Should I have even bothered to do this? */ + {0x50fd4fd6,4,8}, /* "" "" */ + + {0xa912b064,51|0x800,8}, /* 11-in-1 Ball Games(has CHR ROM when it shouldn't) */ + {0,-1,-1} + }; + int tofix=0; + int x=0; + + do + { + if(moo[x].crc32==iNESGameCRC32) + { + if(moo[x].mapper>=0) + { + if(moo[x].mapper&0x800 && VROM_size) + { + VROM_size=0; + free(VROM); + tofix|=8; + } + if(MapperNo!=(moo[x].mapper&0xFF)) + { + tofix|=1; + MapperNo=moo[x].mapper&0xFF; + } + } + if(moo[x].mirror>=0) + { + if(moo[x].mirror==8) + { + if(Mirroring==2) /* Anything but hard-wired(four screen). */ + { + tofix|=2; + Mirroring=0; + } + } + else if(Mirroring!=moo[x].mirror) + { + if(Mirroring!=(moo[x].mirror&~4)) + if((moo[x].mirror&~4)<=2) /* Don't complain if one-screen mirroring + needs to be set(the iNES header can't + hold this information). + */ + tofix|=2; + Mirroring=moo[x].mirror; + } + } + break; + } + x++; + } while(moo[x].mirror>=0 || moo[x].mapper>=0); + + for(x=0;x>4); + MapperNo|=(head.ROM_type2&0xF0); + + Mirroring = (head.ROM_type&1); + if(head.ROM_type&8) Mirroring=2; + + if(!(ROM=(uint8 *)FCEU_malloc(ROM_size<<14))) + return 0; + + if (VROM_size) + if(!(VROM=(uint8 *)FCEU_malloc(VROM_size<<13))) + { + free(ROM); + return 0; + } + + memset(ROM,0xFF,ROM_size<<14); + if(VROM_size) memset(VROM,0xFF,VROM_size<<13); + if(head.ROM_type&4) /* Trainer */ + { + if(!(trainerpoo=FCEU_malloc(512))) + return(0); + FCEU_fread(trainerpoo,512,1,fp); + } + ResetCartMapping(); + SetupCartPRGMapping(0,ROM,ROM_size*0x4000,0); + SetupCartPRGMapping(1,WRAM,8192,1); + + FCEU_fread(ROM,0x4000,head.ROM_size,fp); + + if(VROM_size) + FCEU_fread(VROM,0x2000,head.VROM_size,fp); + + printf("File %s loaded.\n",name); + + iNESGameCRC32=CalcCRC32(0,ROM,ROM_size<<14); + if(VROM_size) + iNESGameCRC32=CalcCRC32(iNESGameCRC32,VROM,VROM_size<<13); + printf("\n PRG ROM: %3d x 16k\n CHR ROM: %3d x 8k\n ROM CRC32: %08lx\n Mapper: %d\n Mirroring: %s\n",head.ROM_size,head.VROM_size,iNESGameCRC32,MapperNo,Mirroring==2?"None(Four-screen)":Mirroring?"Vertical":"Horizontal"); + if(head.ROM_type&2) puts(" Battery-backed."); + if(head.ROM_type&4) puts(" Trained."); + puts(""); + + CheckBad(); + SetInput(); + CheckHInfo(); + CheckVSUni(); + //if(MapperNo!=4 && MapperNo!=118 && MapperNo!=119) exit(1); // Testing + + /* Must remain here because above functions might change value of + VROM_size and free(VROM). + */ + if(VROM_size) + SetupCartCHRMapping(0,VROM,VROM_size*0x2000,0); + + if(Mirroring==2) + SetupCartMirroring(4,1,ExtraNTARAM); + else if(Mirroring>=0x10) + SetupCartMirroring(2+(Mirroring&1),1,0); + else + SetupCartMirroring(Mirroring&1,(Mirroring&4)>>2,0); + + if(MapperNo==5) DetectMMC5WRAMSize(); + else if(MapperNo==1) DetectMMC1WRAMSize(); + + if(head.ROM_type&2) + { + char *soot; + + SaveGame=1; + soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav"); + sp=fopen(soot,"rb"); + if(sp!=NULL) + { + void *ptr; + uint32 amount; + + ptr=WRAM; + amount=8192; + if(MapperNo==1) + { + extern uint8 MMC1WRAMsize; + if(MMC1WRAMsize==2) ptr=WRAM+8192; + } + else if(MapperNo==5) + { + extern uint8 MMC5WRAMsize; + if(MMC5WRAMsize==4) + amount=32768; + } + if(fread(ptr,1,amount,sp)==amount) + printf(" WRAM Save File \"%s\" loaded...\n",soot); + fclose(sp); + } + + } + GameInterface=iNESGI; + return 1; +} + +#include "banksw.h" + + +void FASTAPASS(1) onemir(uint8 V) +{ + if(Mirroring==2) return; + if(V>1) + V=1; + Mirroring=0x10|V; + setmirror(MI_0+V); +} + +void FASTAPASS(1) MIRROR_SET2(uint8 V) +{ + if(Mirroring==2) return; + Mirroring=V; + setmirror(V); +} + +void FASTAPASS(1) MIRROR_SET(uint8 V) +{ + if(Mirroring==2) return; + V^=1; + Mirroring=V; + setmirror(V); +} + +static void NONE_init(void) +{ + ROM_BANK16(0x8000,0); + ROM_BANK16(0xC000,~0); + + if(VROM_size) + VROM_BANK8(0); + else + setvram8(CHRRAM); +} + +static uint8 secdata[2][32]= +{ + { + 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, + 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, + 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, + 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 + }, + { + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + } +}; + +static uint8 *secptr; + +static void CheckVSUni(void) +{ + FCEUGameInfo.type=GIT_VSUNI; + + /* FCE Ultra doesn't complain when headers for these games are bad. */ + secptr=0; + switch(iNESGameCRC32) + { + default:FCEUGameInfo.type=0;break; + + case 0xffbef374: pale=1;break; /* Castlevania */ + + case 0x98e3c75a: + case 0x7cff0f84: pale=2;break; /* SMB */ + + case 0xef7af338: pale=2;break; /* Ice Climber */ + case 0xabe1a0c2: FCEUGameInfo.input[0]=SI_ZAPPER;break; /*Duck hunt */ + case 0x16aa4e2d: pale=7;FCEUGameInfo.input[0]=SI_ZAPPER;break; /* hoganal ^_^ */ + case 0x2b85420e: pale=3;break; /* Dr Mario */ + + case 0xfb0ddde7: pale=2;break; + case 0xc95321a8: pale=6;break; /* Excitebike */ + case 0xb1c4c508: pale=1;break; /* Golf */ + + case 0x66471efe: break; + case 0xca3e9b1a: pale=7;break; /* Pinball*/ + + case 0xf735d926: pale=7; break; /* Gradius */ + + case 0x2019fe65: + case 0x31678411:MapperNo=68;pale=7;break; /* Platoon */ + + case 0x74f713b4:pale=4;break; /* Goonies */ + case 0x9ae2baa0:pale=5;break; /* Slalom */ + case 0xe45485a5:secptr=secdata[1];vsdip=0x20;MapperNo=4;break; /* RBI Baseball */ + case 0x21a653c7:vsdip=0x20;MapperNo=4;break; /* Super Sky Kid */ + case 0xe9a6f17d:vsdip=0x20;break; /* Tetris */ + + case 0x159ef3c1:break; /* Star Luster */ + case 0x9768e5e0:break; /* Stroke and Match Golf */ + + /* FCE Ultra doesn't have correct palettes for the following games. */ + case 0x0fa322c2:pale=2;break; /* Clu Clu Land */ + + case 0x766c2cac: /* Soccer */ + case 0x01357944: /* Battle City */ + case 0xb2816bf9:break; /* Battle City (Bootleg) */ + + case 0x832cf592:FCEUGameInfo.input[0]=SI_ZAPPER;break; /* Freedom Force */ + case 0x63abf889:break; /* Ladies Golf */ + case 0x8c0c2df5:pale=2;MapperNo=1;Mirroring=1;break; /* Top Gun */ + case 0x52c501d0:vsdip=0x80;MapperNo=4;secptr=secdata[0];break; + /* TKO Boxing */ + } + + if(MapperNo==99) + Mirroring=2; +} + + +static int VSindex; + +static DECLFR(VSRead) +{ + //printf("$%04x, $%04x\n",A,X.PC); + switch(A) + { + default: + case 0x5e00: VSindex=0;return 0xFF; + case 0x5e01: return(secptr[(VSindex++)&0x1F]); + } +} + +static void DoVSHooks(void) +{ + if(secptr) + SetReadHandler(0x5e00,0x5e01,VSRead); +} + +void (*MapInitTab[256])(void)= +{ + 0, + Mapper1_init,Mapper2_init,Mapper3_init,Mapper4_init, + Mapper5_init,Mapper6_init,Mapper7_init,Mapper8_init, + Mapper9_init,Mapper10_init,Mapper11_init,0, + Mapper13_init,0,Mapper15_init,Mapper16_init, + Mapper17_init,Mapper18_init,Mapper19_init,0, + Mapper21_init,Mapper22_init,Mapper23_init,Mapper24_init, + Mapper25_init,Mapper26_init,0,0, + 0,0,0,Mapper32_init, + Mapper33_init,Mapper34_init,0,0, + 0,0,0,Mapper40_init, + Mapper41_init,Mapper42_init,Mapper43_init,Mapper44_init, + Mapper45_init,Mapper46_init,Mapper47_init,Mapper33_init,Mapper49_init,0,Mapper51_init,Mapper52_init, + 0,0,0,0,0,0,0,0, + 0,0,0,Mapper64_init, + Mapper65_init,Mapper66_init,Mapper67_init,Mapper68_init, + Mapper69_init,Mapper70_init,Mapper71_init,Mapper72_init, + Mapper73_init,0,Mapper75_init,Mapper76_init, + Mapper77_init,Mapper78_init,Mapper79_init,Mapper80_init, + 0,Mapper82_init,Mapper83_init,0, + Mapper85_init,Mapper86_init,Mapper87_init,Mapper88_init, + Mapper89_init,Mapper90_init,0,Mapper92_init, + Mapper93_init,Mapper94_init,Mapper95_init,Mapper96_init, + Mapper97_init,0,Mapper99_init,0, + 0,0,0,0,Mapper105_init,0,0,0, + 0,0,0,Mapper112_init,Mapper113_init,0,0,0, + Mapper117_init,Mapper118_init,Mapper119_init,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,Mapper140_init, + 0,0,0,0,0,0,0,0, + 0,0,Mapper151_init,Mapper152_init,Mapper153_init,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,Mapper180_init, + 0,Mapper182_init,0,Mapper184_init,Mapper185_init,0,0,0, + Mapper189_init,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,Mapper225_init,Mapper226_init,Mapper227_init,Mapper228_init, + Mapper229_init,0,0,Mapper232_init,0,Mapper234_init,Mapper43_init,0, + 0,0,0,Mapper240_init,0,Mapper242_init,0,0, + Mapper245_init,Mapper246_init,0,Mapper248_init,Mapper249_init,Mapper250_init,0,0,0,0,0 +}; + +static DECLFW(BWRAM) +{ + WRAM[A-0x6000]=V; +} + +static DECLFR(AWRAM) +{ + return WRAM[A-0x6000]; +} + +void (*MapStateRestore)(int version); +void iNESStateRestore(int version) +{ + int x; + + if(!MapperNo) return; + + for(x=0;x<4;x++) + setprg8(0x8000+x*8192,PRGBankList[x]); + + if(VROM_size) + for(x=0;x<8;x++) + setchr1(0x400*x,CHRBankList[x]); + + switch(Mirroring) + { + case 0:setmirror(MI_H);break; + case 1:setmirror(MI_V);break; + case 0x12: + case 0x10:setmirror(MI_0);break; + case 0x13: + case 0x11:setmirror(MI_1);break; + } + if(MapStateRestore) MapStateRestore(version); +} + +static int MMC_init(int type) +{ + int x; + + GameStateRestore=iNESStateRestore; + MapClose=0; + MapperReset=0; + MapStateRestore=0; + + setprg8r(1,0x6000,0); + + SetReadHandler(0x6000,0x7FFF,AWRAM); + SetWriteHandler(0x6000,0x7FFF,BWRAM); + FCEU_CheatAddRAM(8,0x6000,WRAM); + + /* This statement represents atrocious code. I need to rewrite + all of the iNES mapper code... */ + IRQCount=IRQLatch=IRQa=0; + if(head.ROM_type&2) + { + extern uint8 MMC5WRAMsize,MMC1WRAMsize; + if(type==5 && MMC5WRAMsize==4) + memset(GameMemBlock+32768,0,sizeof(GameMemBlock)-32768); + else if(type==1 && MMC1WRAMsize==2) + { + memset(GameMemBlock,0,8192); + memset(GameMemBlock+16384,0,sizeof(GameMemBlock)-16384); + } + else + memset(GameMemBlock+8192,0,sizeof(GameMemBlock)-8192); + } + else + memset(GameMemBlock,0,sizeof(GameMemBlock)); + if(head.ROM_type&4) + memcpy(WRAM+4096,trainerpoo,512); + + NONE_init(); + + if(FCEUGameInfo.type==GIT_VSUNI) + DoVSHooks(); + if(type==5) + { + MMC5HackVROMMask=CHRmask4[0]; + MMC5HackExNTARAMPtr=MapperExRAM+0x6000; + MMC5Hack=1; + MMC5HackVROMPTR=VROM; + MMC5HackCHRMode=0; + } + ResetExState(); + AddExState(WRAM, 8192, 0, "WRAM"); + if(type==19 || type==5 || type==6 || type==69 || type==85) + AddExState(MapperExRAM, 32768, 0, "MEXR"); + if((!VROM_size || type==6 || type==19 || type==119) && + (type!=13 && type!=96)) + AddExState(CHRRAM, 8192, 0, "CHRR"); + if(head.ROM_type&8) + AddExState(ExtraNTARAM, 2048, 0, "EXNR"); + + /* Exclude some mappers whose emulation code handle save state stuff + themselves. */ + if(type && type!=13 && type!=96) + { + AddExState(mapbyte1, 32, 0, "MPBY"); + AddExState(&Mirroring, 1, 0, "MIRR"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQLatch, 4, 1, "IQL1"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(PRGBankList, 4, 0, "PBL"); + for(x=0;x<8;x++) + { + char tak[8]; + sprintf(tak,"CBL%d",x); + AddExState(&CHRBankList[x], 2, 1,tak); + } + } + + if(MapInitTab[type]) MapInitTab[type](); + else if(type) + { + FCEU_PrintError("iNES mapper #%d is not supported at all.",type); + } + return 1; +} diff --git a/ines.h b/ines.h new file mode 100644 index 0000000..a27cf74 --- /dev/null +++ b/ines.h @@ -0,0 +1,366 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 Bero + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef INESPRIV + +void iNESStateRestore(int version); +extern uint32 iNESGameCRC32; + +extern uint8 *VROM; +extern uint32 VROM_size; + +extern void (*MapStateRestore)(int version); +extern void (*MapClose)(void); +extern void (*MapperReset)(void); + +/* This order is necessary */ +#define WRAM (GameMemBlock) +#define sizeofWRAM 8192 + +#define MapperExRAM (GameMemBlock+sizeofWRAM) +#define sizeofMapperExRAM 32768 +/* for the MMC5 code to work properly. It might be fixed later... */ + + +#define CHRRAM (GameMemBlock+sizeofWRAM+sizeofMapperExRAM) +#define sizeofCHRRAM 8192 + +#define ExtraNTARAM (GameMemBlock+sizeofWRAM+sizeofMapperExRAM+sizeofCHRRAM) +#define sizeofExtraNTARAM 2048 + +#define PRGBankList (ExtraNTARAM+sizeofExtraNTARAM) + +#define mapbyte1 (PRGBankList+4) +#define mapbyte2 (mapbyte1+8) +#define mapbyte3 (mapbyte2+8) +#define mapbyte4 (mapbyte3+8) +uint16 iNESCHRBankList[8]; +int32 iNESIRQLatch,iNESIRQCount; +uint8 iNESMirroring; +uint8 iNESIRQa; + +#define IRQa iNESIRQa +#define Mirroring iNESMirroring +#define IRQCount iNESIRQCount +#define IRQLatch iNESIRQLatch +#define CHRBankList iNESCHRBankList +#else +int iNESLoad(char *name, int fp); +#endif + + typedef struct { + char ID[4]; /*NES^Z*/ + uint8 ROM_size; + uint8 VROM_size; + uint8 ROM_type; + uint8 ROM_type2; + uint8 reserve[8]; + } iNES_HEADER; + +void FASTAPASS(2) VRAM_BANK1(uint32 A, uint8 V); +void FASTAPASS(2) VRAM_BANK4(uint32 A,uint32 V); + +void FASTAPASS(2) VROM_BANK1(uint32 A,uint32 V); +void FASTAPASS(2) VROM_BANK2(uint32 A,uint32 V); +void FASTAPASS(2) VROM_BANK4(uint32 A, uint32 V); +void FASTAPASS(1) VROM_BANK8(uint32 V); +void FASTAPASS(2) ROM_BANK8(uint32 A, uint32 V); +void FASTAPASS(2) ROM_BANK16(uint32 A, uint32 V); +void FASTAPASS(2) ROM_BANK32(uint32 V); + +extern uint8 vmask; +extern uint32 vmask1; +extern uint32 vmask2; +extern uint32 vmask4; +extern uint32 pmask8; +extern uint8 pmask16; +extern uint8 pmask32; + +void FASTAPASS(1) onemir(uint8 V); +void FASTAPASS(1) MIRROR_SET2(uint8 V); +void FASTAPASS(1) MIRROR_SET(uint8 V); + + +void DetectMMC1WRAMSize(void); +void DetectMMC5WRAMSize(void); + +void Mapper0_init(void); +void Mapper1_init(void); +void Mapper2_init(void); +void Mapper3_init(void); +void Mapper4_init(void); +void Mapper5_init(void); +void Mapper6_init(void); +void Mapper7_init(void); +void Mapper8_init(void); +void Mapper9_init(void); +void Mapper10_init(void); +void Mapper11_init(void); +void Mapper12_init(void); +void Mapper13_init(void); +void Mapper14_init(void); +void Mapper15_init(void); +void Mapper16_init(void); +void Mapper17_init(void); +void Mapper18_init(void); +void Mapper19_init(void); +void Mapper20_init(void); +void Mapper21_init(void); +void Mapper22_init(void); +void Mapper23_init(void); +void Mapper24_init(void); +void Mapper25_init(void); +void Mapper26_init(void); +void Mapper27_init(void); +void Mapper28_init(void); +void Mapper29_init(void); +void Mapper30_init(void); +void Mapper31_init(void); +void Mapper32_init(void); +void Mapper33_init(void); +void Mapper34_init(void); +void Mapper35_init(void); +void Mapper36_init(void); +void Mapper37_init(void); +void Mapper38_init(void); +void Mapper39_init(void); +void Mapper40_init(void); +void Mapper41_init(void); +void Mapper42_init(void); +void Mapper43_init(void); +void Mapper44_init(void); +void Mapper45_init(void); +void Mapper46_init(void); +void Mapper47_init(void); +void Mapper48_init(void); +void Mapper49_init(void); +void Mapper50_init(void); +void Mapper51_init(void); +void Mapper52_init(void); +void Mapper53_init(void); +void Mapper54_init(void); +void Mapper55_init(void); +void Mapper56_init(void); +void Mapper57_init(void); +void Mapper58_init(void); +void Mapper59_init(void); +void Mapper60_init(void); +void Mapper61_init(void); +void Mapper62_init(void); +void Mapper63_init(void); +void Mapper64_init(void); +void Mapper65_init(void); +void Mapper66_init(void); +void Mapper67_init(void); +void Mapper68_init(void); +void Mapper69_init(void); +void Mapper70_init(void); +void Mapper71_init(void); +void Mapper72_init(void); +void Mapper73_init(void); +void Mapper74_init(void); +void Mapper75_init(void); +void Mapper76_init(void); +void Mapper77_init(void); +void Mapper78_init(void); +void Mapper79_init(void); +void Mapper80_init(void); +void Mapper81_init(void); +void Mapper82_init(void); +void Mapper83_init(void); +void Mapper84_init(void); +void Mapper85_init(void); +void Mapper86_init(void); +void Mapper87_init(void); +void Mapper88_init(void); +void Mapper89_init(void); +void Mapper90_init(void); +void Mapper91_init(void); +void Mapper92_init(void); +void Mapper93_init(void); +void Mapper94_init(void); +void Mapper95_init(void); +void Mapper96_init(void); +void Mapper97_init(void); +void Mapper98_init(void); +void Mapper99_init(void); +void Mapper100_init(void); +void Mapper101_init(void); +void Mapper102_init(void); +void Mapper103_init(void); +void Mapper104_init(void); +void Mapper105_init(void); +void Mapper106_init(void); +void Mapper107_init(void); +void Mapper108_init(void); +void Mapper109_init(void); +void Mapper110_init(void); +void Mapper111_init(void); +void Mapper112_init(void); +void Mapper113_init(void); +void Mapper114_init(void); +void Mapper115_init(void); +void Mapper116_init(void); +void Mapper117_init(void); +void Mapper118_init(void); +void Mapper119_init(void); +void Mapper120_init(void); +void Mapper121_init(void); +void Mapper122_init(void); +void Mapper123_init(void); +void Mapper124_init(void); +void Mapper125_init(void); +void Mapper126_init(void); +void Mapper127_init(void); +void Mapper128_init(void); +void Mapper129_init(void); +void Mapper130_init(void); +void Mapper131_init(void); +void Mapper132_init(void); +void Mapper133_init(void); +void Mapper134_init(void); +void Mapper135_init(void); +void Mapper136_init(void); +void Mapper137_init(void); +void Mapper138_init(void); +void Mapper139_init(void); +void Mapper140_init(void); +void Mapper141_init(void); +void Mapper142_init(void); +void Mapper143_init(void); +void Mapper144_init(void); +void Mapper145_init(void); +void Mapper146_init(void); +void Mapper147_init(void); +void Mapper148_init(void); +void Mapper149_init(void); +void Mapper150_init(void); +void Mapper151_init(void); +void Mapper152_init(void); +void Mapper153_init(void); +void Mapper154_init(void); +void Mapper155_init(void); +void Mapper156_init(void); +void Mapper157_init(void); +void Mapper158_init(void); +void Mapper159_init(void); +void Mapper160_init(void); +void Mapper161_init(void); +void Mapper162_init(void); +void Mapper163_init(void); +void Mapper164_init(void); +void Mapper165_init(void); +void Mapper166_init(void); +void Mapper167_init(void); +void Mapper168_init(void); +void Mapper169_init(void); +void Mapper170_init(void); +void Mapper171_init(void); +void Mapper172_init(void); +void Mapper173_init(void); +void Mapper174_init(void); +void Mapper175_init(void); +void Mapper176_init(void); +void Mapper177_init(void); +void Mapper178_init(void); +void Mapper179_init(void); +void Mapper180_init(void); +void Mapper181_init(void); +void Mapper182_init(void); +void Mapper183_init(void); +void Mapper184_init(void); +void Mapper185_init(void); +void Mapper186_init(void); +void Mapper187_init(void); +void Mapper188_init(void); +void Mapper189_init(void); +void Mapper190_init(void); +void Mapper191_init(void); +void Mapper192_init(void); +void Mapper193_init(void); +void Mapper194_init(void); +void Mapper195_init(void); +void Mapper196_init(void); +void Mapper197_init(void); +void Mapper198_init(void); +void Mapper199_init(void); +void Mapper200_init(void); +void Mapper201_init(void); +void Mapper202_init(void); +void Mapper203_init(void); +void Mapper204_init(void); +void Mapper205_init(void); +void Mapper206_init(void); +void Mapper207_init(void); +void Mapper208_init(void); +void Mapper209_init(void); +void Mapper210_init(void); +void Mapper211_init(void); +void Mapper212_init(void); +void Mapper213_init(void); +void Mapper214_init(void); +void Mapper215_init(void); +void Mapper216_init(void); +void Mapper217_init(void); +void Mapper218_init(void); +void Mapper219_init(void); +void Mapper220_init(void); +void Mapper221_init(void); +void Mapper222_init(void); +void Mapper223_init(void); +void Mapper224_init(void); +void Mapper225_init(void); +void Mapper226_init(void); +void Mapper227_init(void); +void Mapper228_init(void); +void Mapper229_init(void); +void Mapper230_init(void); +void Mapper231_init(void); +void Mapper232_init(void); +void Mapper233_init(void); +void Mapper234_init(void); +void Mapper235_init(void); +void Mapper236_init(void); +void Mapper237_init(void); +void Mapper238_init(void); +void Mapper239_init(void); +void Mapper240_init(void); +void Mapper241_init(void); +void Mapper242_init(void); +void Mapper243_init(void); +void Mapper244_init(void); +void Mapper245_init(void); +void Mapper246_init(void); +void Mapper247_init(void); +void Mapper248_init(void); +void Mapper249_init(void); +void Mapper250_init(void); +void Mapper251_init(void); +void Mapper252_init(void); +void Mapper253_init(void); +void Mapper254_init(void); +void Mapper255_init(void); + +void VRC6_ESI(int t); +void VRC7_ESI(void); +void Mapper5_ESI(void); +void Mapper69_ESI(void); +void Mapper19_ESI(void); diff --git a/input.c b/input.c new file mode 100644 index 0000000..be6b360 --- /dev/null +++ b/input.c @@ -0,0 +1,330 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "x6502.h" + +#include "fce.h" +#include "sound.h" +#include "netplay.h" +#include "svga.h" + +#include "input.h" + +extern INPUTC *FCEU_InitZapper(int w); +extern INPUTC *FCEU_InitPowerpad(int w); +extern INPUTC *FCEU_InitArkanoid(int w); + +extern INPUTCFC *FCEU_InitArkanoidFC(void); +extern INPUTCFC *FCEU_InitSpaceShadow(void); +extern INPUTCFC *FCEU_InitFKB(void); +static uint8 joy_readbit[2]; +static uint16 joy[2]={0,0}; + +extern int coinon; + +static int FSDisable=0; /* Set to 1 if NES-style four-player adapter is disabled. */ +static int JPAttrib[2]={0,0}; +static int JPType[2]={0,0}; +static void *InputDataPtr[2]; + +static int JPAttribFC=0; +static int JPTypeFC=0; +static void *InputDataPtrFC; + +void (*InputScanlineHook)(uint8 *buf, int line); + +static INPUTC DummyJPort={0,0,0,0,0}; +static INPUTC *JPorts[2]={&DummyJPort,&DummyJPort}; +static INPUTCFC *FCExp=0; + +static uint8 FP_FASTAPASS(1) ReadGPVS(int w) +{ + uint8 ret=0; + + if(joy_readbit[w]>=8) + ret=1; + else + { + ret = ((joy[w]>>(joy_readbit[w]))&1); + joy_readbit[w]++; + } + return ret; +} + +static uint8 FP_FASTAPASS(1) ReadGP(int w) +{ + uint8 ret; + //if(JoyMulti) + //{ + //ret = ((joy[w]>>(joy_readbit[w]))&1)| + //(((joy[w]>>(joy_readbit[w]+8))&1)<<1); + //if(joy_readbit[w]>8) ret=0; + //} + ret = ((joy[w]>>(joy_readbit[w]))&1); + if(FSDisable) + { + if(joy_readbit[w]>=8) ret|=1; + } + else + { + if(joy_readbit[w]==19-w) ret|=1; + } + joy_readbit[w]++; + return ret; +} + +static DECLFR(JPRead) +{ + uint8 ret=0; + + if(JPorts[A&1]->Read) + ret|=JPorts[A&1]->Read(A&1); + + if(FCExp) + if(FCExp->Read) + ret=FCExp->Read(A&1,ret); + + ret|=X.DB&0xC0; + return(ret); +} + +static DECLFW(B4016) +{ + if(FCExp) + if(FCExp->Write) + FCExp->Write(V&7); + + if(JPorts[0]->Write) + JPorts[0]->Write(V&1); + if(JPorts[1]->Write) + JPorts[1]->Write(V&1); + + if((PSG[0x16]&1) && (!(V&1))) + { + /* This strobe code is just for convenience. If it were + with the code in input / *.c, it would more accurately represent + what's really going on. But who wants accuracy? ;) + Seriously, though, this shouldn't be a problem. + */ + if(JPorts[0]->Strobe) + JPorts[0]->Strobe(0); + if(JPorts[1]->Strobe) + JPorts[1]->Strobe(1); + if(FCExp) + if(FCExp->Strobe) + FCExp->Strobe(); + } + PSG[0x16]=V; +} + +static void FP_FASTAPASS(1) StrobeGP(int w) +{ + joy_readbit[w]=0; +} + +static INPUTC GPC={ReadGP,0,StrobeGP,0,0,0}; +static INPUTC GPCVS={ReadGPVS,0,StrobeGP,0,0,0}; + +void DrawInput(uint8 *buf) +{ + int x; + + for(x=0;x<2;x++) + if(JPorts[x]->Draw) + JPorts[x]->Draw(x,buf,JPAttrib[x]); + if(FCExp) + if(FCExp->Draw) + FCExp->Draw(buf,JPAttribFC); +} + +void UpdateInput(void) +{ + int x; + + for(x=0;x<2;x++) + { + switch(JPType[x]) + { + case SI_GAMEPAD: + if(!x) joy[0]=*(uint16 *)InputDataPtr[0]; + else joy[1]=*(uint16 *)InputDataPtr[1]; + break; + default: + if(JPorts[x]->Update) + JPorts[x]->Update(x,InputDataPtr[x],JPAttrib[x]); + break; + } + } + if(FCExp) + if(FCExp->Update) + FCExp->Update(InputDataPtrFC,JPAttribFC); + + if(FCEUGameInfo.type==GIT_VSUNI) + { + uint16 t=joy[0]; + joy[0]=(joy[0]&0xC)|(joy[1]&0xF3); + joy[1]=(joy[1]&0xC)|(t&0xF3); + if(coinon) coinon--; + } + #ifdef NETWORK + if(netplay) NetplayUpdate(&joy[0],&joy[1]); + #endif + FlushCommandQueue(); +} + +static DECLFR(VSUNIRead0) +{ + uint8 ret=0; + + if(JPorts[0]->Read) + ret|=(JPorts[0]->Read(0))&1; + + ret|=(vsdip&3)<<3; + if(coinon) + ret|=0x4; + return ret; +} + +static DECLFR(VSUNIRead1) +{ + uint8 ret=0; + + if(JPorts[1]->Read) + ret|=(JPorts[1]->Read(1))&1; + ret|=vsdip&0xFC; + return ret; +} + +static void SLHLHook(uint8 *buf, int line) +{ + int x; + + for(x=0;x<2;x++) + if(JPorts[x]->SLHook) + JPorts[x]->SLHook(x,buf,line); + if(FCExp) + if(FCExp->SLHook) + FCExp->SLHook(buf,line); +} + +static void CheckSLHook(void) +{ + InputScanlineHook=0; + if(JPorts[0]->SLHook || JPorts[1]->SLHook) + InputScanlineHook=SLHLHook; + if(FCExp) + if(FCExp->SLHook) + InputScanlineHook=SLHLHook; +} + +static void FASTAPASS(1) SetInputStuff(int x) +{ + switch(JPType[x]) + { + case SI_GAMEPAD: + if(FCEUGameInfo.type==GIT_VSUNI) + JPorts[x]=&GPCVS; + else + JPorts[x]=&GPC; + break; + case SI_ARKANOID:JPorts[x]=FCEU_InitArkanoid(x);break; + case SI_ZAPPER:JPorts[x]=FCEU_InitZapper(x);break; + case SI_POWERPAD:JPorts[x]=FCEU_InitPowerpad(x);break; + case SI_NONE:JPorts[x]=&DummyJPort;break; + } + + CheckSLHook(); +} + +static uint8 F4ReadBit[2]; +static void StrobeFami4(void) +{ + F4ReadBit[0]=F4ReadBit[1]=0; +} + +static uint8 FP_FASTAPASS(2) ReadFami4(int w, uint8 ret) +{ + ret&=1; + + ret |= ((joy[w]>>(F4ReadBit[w]+8))&1)<<1; + if(F4ReadBit[w]>=8) ret|=2; + else F4ReadBit[w]++; + + return(ret); +} + +static INPUTCFC FAMI4C={ReadFami4,0,StrobeFami4,0,0,0}; +static void SetInputStuffFC(void) +{ + switch(JPTypeFC) + { + case SIFC_NONE:FCExp=0;break; + case SIFC_ARKANOID:FCExp=FCEU_InitArkanoidFC();break; + case SIFC_SHADOW:FCExp=FCEU_InitSpaceShadow();break; + case SIFC_4PLAYER:FCExp=&FAMI4C;memset(&F4ReadBit,0,sizeof(F4ReadBit));break; + case SIFC_FKB:FCExp=FCEU_InitFKB();break; + } + CheckSLHook(); +} + +// VS Unisystem code called after SetInputMap() hooks B4016. Need to +// rewrite code to make this more sane? + +void InitializeInput(void) +{ + memset(joy_readbit,0,sizeof(joy_readbit)); + memset(joy,0,sizeof(joy)); + + if(FCEUGameInfo.type==GIT_VSUNI) + { + SetReadHandler(0x4016,0x4016,VSUNIRead0); + SetReadHandler(0x4017,0x4017,VSUNIRead1); + } + else + SetReadHandler(0x4016,0x4017,JPRead); + SetWriteHandler(0x4016,0x4016,B4016); + + SetInputStuff(0); + SetInputStuff(1); + SetInputStuffFC(); +} + +void FCEUI_SetInput(int port, int type, void *ptr, int attrib) +{ + JPAttrib[port]=attrib; + JPType[port]=type; + InputDataPtr[port]=ptr; + SetInputStuff(port); +} + +void FCEUI_DisableFourScore(int s) +{ + FSDisable=s; +} + +void FCEUI_SetInputFC(int type, void *ptr, int attrib) +{ + JPAttribFC=attrib; + JPTypeFC=type; + InputDataPtrFC=ptr; + SetInputStuffFC(); +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..8cec68a --- /dev/null +++ b/input.h @@ -0,0 +1,23 @@ +typedef struct { + uint8 FP_FASTAPASS(1) (*Read)(int w); + void FP_FASTAPASS(1) (*Write)(uint8 v); + void FP_FASTAPASS(1) (*Strobe)(int w); + void FP_FASTAPASS(3) (*Update)(int w, void *data, int arg); + void FP_FASTAPASS(3) (*SLHook)(int w, uint8 *buf, int line); + void FP_FASTAPASS(3) (*Draw)(int w, uint8 *buf, int arg); +} INPUTC; + +typedef struct { + uint8 FP_FASTAPASS(2) (*Read)(int w, uint8 ret); + void FP_FASTAPASS(1) (*Write)(uint8 v); + void (*Strobe)(void); + void FP_FASTAPASS(2) (*Update)(void *data, int arg); + void FP_FASTAPASS(2) (*SLHook)(uint8 *buf, int line); + void FP_FASTAPASS(2) (*Draw)(uint8 *buf, int arg); +} INPUTCFC; + +void DrawInput(uint8 *buf); +void UpdateInput(void); +void InitializeInput(void); +extern void (*PStrobe[2])(void); +extern void (*InputScanlineHook)(uint8 *buf, int line); diff --git a/input/Makefile b/input/Makefile new file mode 100644 index 0000000..0327a9f --- /dev/null +++ b/input/Makefile @@ -0,0 +1,8 @@ +INPOBJS = input/cursor.o input/powerpad.o input/zapper.o input/arkanoid.o input/shadow.o input/fkb.o + +input/cursor.o: input/cursor.c +input/zapper.o: input/zapper.c +input/powerpad.o: input/powerpad.c +input/arkanoid.o: input/arkanoid.c +input/shadow.o: input/shadow.c +input/fkb.o: input/fkb.c input/fkb.h diff --git a/input/arkanoid.c b/input/arkanoid.c new file mode 100644 index 0000000..b53fd3a --- /dev/null +++ b/input/arkanoid.c @@ -0,0 +1,117 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "share.h" + +typedef struct { + uint32 mzx,mzb; + uint32 readbit; +} ARK; + +static ARK NESArk[2]; +static ARK FCArk; + +static void StrobeARKFC(void) +{ + FCArk.readbit=0; +} + + +static uint8 FP_FASTAPASS(2) ReadARKFC(int w,uint8 ret) +{ + ret&=~2; + + if(w) + { + if(FCArk.readbit>=8) + ret|=2; + else + { + ret|=((FCArk.mzx>>(7-FCArk.readbit))&1)<<1; + FCArk.readbit++; + } + } + else + ret|=FCArk.mzb<<1; + return(ret); +} + +static uint32 FixX(uint32 x) +{ + x=98+x*144/240; + if(x>242) x=242; + x=~x; + return(x); +} + +static void FP_FASTAPASS(2) UpdateARKFC(void *data, int arg) +{ + uint32 *ptr=data; + FCArk.mzx=FixX(ptr[0]); + FCArk.mzb=ptr[2]?1:0; +} + +static INPUTCFC ARKCFC={ReadARKFC,0,StrobeARKFC,UpdateARKFC,0,0}; + +INPUTCFC *FCEU_InitArkanoidFC(void) +{ + FCArk.mzx=98; + FCArk.mzb=0; + return(&ARKCFC); +} + +static uint8 FP_FASTAPASS(1) ReadARK(int w) +{ + uint8 ret=0; + + if(NESArk[w].readbit>=8) + ret|=1<<4; + else + { + ret|=((NESArk[w].mzx>>(7-NESArk[w].readbit))&1)<<4; + NESArk[w].readbit++; + } + ret|=(NESArk[w].mzb&1)<<3; + return(ret); +} + + +static void FP_FASTAPASS(1) StrobeARK(int w) +{ + NESArk[w].readbit=0; +} + +static void FP_FASTAPASS(3) UpdateARK(int w, void *data, int arg) +{ + uint32 *ptr=data; + NESArk[w].mzx=FixX(ptr[0]); + NESArk[w].mzb=ptr[2]?1:0; +} + +static INPUTC ARKC={ReadARK, 0, StrobeARK, UpdateARK, 0, 0}; + +INPUTC *FCEU_InitArkanoid(int w) +{ + NESArk[w].mzx=98; + NESArk[w].mzb=0; + return(&ARKC); +} diff --git a/input/cursor.c b/input/cursor.c new file mode 100644 index 0000000..cb78d73 --- /dev/null +++ b/input/cursor.c @@ -0,0 +1,45 @@ +#include "share.h" + +static uint8 FCEUcursor[11*19]= +{ + 1,0,0,0,0,0,0,0,0,0,0, + 1,1,0,0,0,0,0,0,0,0,0, + 1,2,1,0,0,0,0,0,0,0,0, + 1,2,2,1,0,0,0,0,0,0,0, + 1,2,2,2,1,0,0,0,0,0,0, + 1,2,2,2,2,1,0,0,0,0,0, + 1,2,2,2,2,2,1,0,0,0,0, + 1,2,2,2,2,2,2,1,0,0,0, + 1,2,2,2,2,2,2,2,1,0,0, + 1,2,2,2,2,2,2,2,2,1,0, + 1,2,2,2,2,2,1,1,1,1,1, + 1,2,2,1,2,2,1,0,0,0,0, + 1,2,1,0,1,2,2,1,0,0,0, + 1,1,0,0,1,2,2,1,0,0,0, + 1,0,0,0,0,1,2,2,1,0,0, + 0,0,0,0,0,1,2,2,1,0,0, + 0,0,0,0,0,0,1,2,2,1,0, + 0,0,0,0,0,0,1,2,2,1,0, + 0,0,0,0,0,0,0,1,1,0,0, +}; + +void FCEU_DrawCursor(uint8 *buf, int xc, int yc) +{ + int x,y; + int c,d; + + if(xc<256 && yc<240) + for(y=0;y<19;y++) + for(x=0;x<11;x++) + { + uint8 a; + a=FCEUcursor[y*11+x]; + if(a) + { + c=(yc+y); + d=(xc+x); + if(d<256 && c<240) + buf[c*272+d]=a+127; + } + } +} diff --git a/input/fkb.c b/input/fkb.c new file mode 100644 index 0000000..c70e73f --- /dev/null +++ b/input/fkb.c @@ -0,0 +1,102 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "share.h" +#include "fkb.h" +#define AK2(x,y) ( (FKB_##x) | (FKB_##y <<8) ) +#define AK(x) FKB_##x + +static uint8 bufit[0x49]; +static uint8 ksmode; +static uint8 ksindex; + + +static uint16 matrix[9][2][4]= +{ +{{AK(F8),AK(RETURN),AK(BRACKETLEFT),AK(BRACKETRIGHT)}, + {AK(KANA),AK(RIGHTSHIFT),AK(BACKSLASH),AK(STOP)}}, +{{AK(F7),AK(AT),AK(COLON),AK(SEMICOLON)}, + {AK(UNDERSCORE),AK(SLASH),AK(MINUS),AK(CARET)}}, +{{AK(F6),AK(O),AK(L),AK(K)}, + {AK(PERIOD),AK(COMMA),AK(P),AK(0)}}, +{{AK(F5),AK(I),AK(U),AK(J)}, + {AK(M),AK(N),AK(9),AK(8)}}, +{{AK(F4),AK(Y),AK(G),AK(H)}, + {AK(B),AK(V),AK(7),AK(6)}}, +{{AK(F3),AK(T),AK(R),AK(D)}, + {AK(F),AK(C),AK(5),AK(4)}}, +{{AK(F2),AK(W),AK(S),AK(A)}, + {AK(X),AK(Z),AK(E),AK(3)}}, +{{AK(F1),AK(ESCAPE),AK(Q),AK(CONTROL)}, + {AK(LEFTSHIFT),AK(GRAPH),AK(1),AK(2)}}, +{{AK(CLEAR),AK(UP),AK(RIGHT),AK(LEFT)}, + {AK(DOWN),AK(SPACE),AK(DELETE),AK(INSERT)}}, +}; + +static void FP_FASTAPASS(1) FKB_Write(uint8 v) +{ + v>>=1; + if(v&2) + { + if((ksmode&1) && !(v&1)) + ksindex=(ksindex+1)%9; + } + ksmode=v; +} + +static uint8 FP_FASTAPASS(2) FKB_Read(int w, uint8 ret) +{ + //printf("$%04x, %d, %d\n",w+0x4016,ksindex,ksmode&1); + if(w) + { + int x; + + ret&=~0x1E; + for(x=0;x<4;x++) + if(bufit[ matrix[ksindex][ksmode&1][x]&0xFF ] || bufit[ matrix[ksindex][ksmode&1][x]>>8]) + { + ret|=1<<(x+1); + } + ret^=0x1E; + } + return(ret); +} + +static void FKB_Strobe(void) +{ + ksmode=0; + ksindex=0; + //printf("strobe\n"); +} + +static void FP_FASTAPASS(2) FKB_Update(void *data, int arg) +{ + memcpy(bufit+1,data,0x48); +} + +static INPUTCFC FKB={FKB_Read,FKB_Write,FKB_Strobe,FKB_Update,0,0}; + +INPUTCFC *FCEU_InitFKB(void) +{ + memset(bufit,0,sizeof(bufit)); + ksmode=ksindex=0; + return(&FKB); +} diff --git a/input/fkb.h b/input/fkb.h new file mode 100644 index 0000000..ca27b34 --- /dev/null +++ b/input/fkb.h @@ -0,0 +1,72 @@ +#define FKB_F1 0x01 +#define FKB_F2 0x02 +#define FKB_F3 0x03 +#define FKB_F4 0x04 +#define FKB_F5 0x05 +#define FKB_F6 0x06 +#define FKB_F7 0x07 +#define FKB_F8 0x08 +#define FKB_1 0x09 +#define FKB_2 0x0A +#define FKB_3 0x0B +#define FKB_4 0x0C +#define FKB_5 0x0D +#define FKB_6 0x0E +#define FKB_7 0x0F +#define FKB_8 0x10 +#define FKB_9 0x11 +#define FKB_0 0x12 +#define FKB_MINUS 0x13 +#define FKB_CARET 0x14 +#define FKB_BACKSLASH 0x15 +#define FKB_STOP 0x16 +#define FKB_ESCAPE 0x17 +#define FKB_Q 0x18 +#define FKB_W 0x19 +#define FKB_E 0x1A +#define FKB_R 0x1B +#define FKB_T 0x1C +#define FKB_Y 0x1D +#define FKB_U 0x1E +#define FKB_I 0x1F +#define FKB_O 0x20 +#define FKB_P 0x21 +#define FKB_AT 0x22 +#define FKB_BRACKETLEFT 0x23 +#define FKB_RETURN 0x24 +#define FKB_CONTROL 0x25 +#define FKB_A 0x26 +#define FKB_S 0x27 +#define FKB_D 0x28 +#define FKB_F 0x29 +#define FKB_G 0x2A +#define FKB_H 0x2B +#define FKB_J 0x2C +#define FKB_K 0x2D +#define FKB_L 0x2E +#define FKB_SEMICOLON 0x2F +#define FKB_COLON 0x30 +#define FKB_BRACKETRIGHT 0x31 +#define FKB_KANA 0x32 +#define FKB_LEFTSHIFT 0x33 +#define FKB_Z 0x34 +#define FKB_X 0x35 +#define FKB_C 0x36 +#define FKB_V 0x37 +#define FKB_B 0x38 +#define FKB_N 0x39 +#define FKB_M 0x3A +#define FKB_COMMA 0x3B +#define FKB_PERIOD 0x3C +#define FKB_SLASH 0x3D +#define FKB_UNDERSCORE 0x3E +#define FKB_RIGHTSHIFT 0x3F +#define FKB_GRAPH 0x40 +#define FKB_SPACE 0x41 +#define FKB_CLEAR 0x42 +#define FKB_INSERT 0x43 +#define FKB_DELETE 0x44 +#define FKB_UP 0x45 +#define FKB_LEFT 0x46 +#define FKB_RIGHT 0x47 +#define FKB_DOWN 0x48 diff --git a/input/powerpad.c b/input/powerpad.c new file mode 100644 index 0000000..3c613f9 --- /dev/null +++ b/input/powerpad.c @@ -0,0 +1,60 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "share.h" + + +static uint32 pprsb[2]; +static uint32 pprdata[2]; + +static uint8 FP_FASTAPASS(1) ReadPP(int w) +{ + uint8 ret=0; + ret|=((pprdata[w]>>pprsb[w])&1)<<3; + ret|=((pprdata[w]>>(pprsb[w]+8))&1)<<4; + if(pprsb[w]>=4) + { + ret|=0x10; + if(pprsb[w]>=8) + ret|=0x08; + } + pprsb[w]++; + return ret; +} + +static void FP_FASTAPASS(1) StrobePP(int w) +{ + pprsb[w]=0; +} + +void FP_FASTAPASS(3) UpdatePP(int w, void *data, int arg) +{ + pprdata[w]=*(uint32 *)data; +} + +static INPUTC PPC={ReadPP,0,StrobePP,UpdatePP,0,0}; + +INPUTC *FCEU_InitPowerpad(int w) +{ + pprsb[w]=pprdata[w]=0; + return(&PPC); +} diff --git a/input/shadow.c b/input/shadow.c new file mode 100644 index 0000000..499e640 --- /dev/null +++ b/input/shadow.c @@ -0,0 +1,127 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "share.h" + +typedef struct { + uint32 mzx,mzy,mzb; + int zap_readbit; + int bogo; + uint32 colok; + uint32 coloklast; +} ZAPPER; + +static ZAPPER ZD; + +static void FP_FASTAPASS(2) ZapperThingy(uint8 *buf, int line) +{ + int mzx=ZD.mzx; + + if(line==0) ZD.colok=1<<16; /* Disable it. */ + ZD.coloklast=ZD.colok; + + if((line>=ZD.mzy-3 && line<=ZD.mzy+3) && mzx<256) + { + int a,sum,x; + + for(x=-4;x<4;x++) + { + if((mzx+x)<0 || (mzx+x)>255) continue; + a=buf[mzx+x]&63; + sum=palo[a].r+palo[a].g+palo[a].b; + + if(sum>=100*3) + { + ZD.colok=timestamp+mzx/3; + break; + } + } + } +} + +static INLINE int CheckColor(void) +{ + if( (timestamp>=ZD.coloklast && timestamp<=(ZD.coloklast+10)) || + (timestamp>=ZD.colok && timestamp<=(ZD.colok+10)) ) + return 0; + return 1; +} + +static uint8 FP_FASTAPASS(2) ReadZapper(int w, uint8 ret) +{ + if(w) + { + ret&=~0x18; + if(ZD.bogo) + ret|=0x10; + if(CheckColor()) + ret|=0x8; + } + else + { + //printf("Kayo: %d\n",ZD.zap_readbit); + ret&=~2; + //if(ZD.zap_readbit==4) ret|=ZD.mzb&2; + ret|=(ret&1)<<1; + //ZD.zap_readbit++; + } + return ret; +} + +static void FP_FASTAPASS(2) DrawZapper(uint8 *buf, int arg) +{ + if(arg) + FCEU_DrawCursor(buf, ZD.mzx, ZD.mzy); +} + +static void FP_FASTAPASS(2) UpdateZapper(void *data, int arg) +{ + uint32 *ptr=data; + + if(ZD.bogo) + ZD.bogo--; + if(ptr[2]&1 && (!(ZD.mzb&1))) + ZD.bogo=5; + + ZD.mzx=ptr[0]; + ZD.mzy=ptr[1]; + ZD.mzb=ptr[2]; + + if(ZD.mzx>=256 || ZD.mzy>=240) + ZD.colok=0; +} + +static void StrobeShadow(void) +{ + ZD.zap_readbit=0; +} + +static INPUTCFC SHADOWC={ReadZapper,0,StrobeShadow,UpdateZapper,ZapperThingy,DrawZapper}; + +INPUTCFC *FCEU_InitSpaceShadow(void) +{ + memset(&ZD,0,sizeof(ZAPPER)); + return(&SHADOWC); +} + + diff --git a/input/share.h b/input/share.h new file mode 100644 index 0000000..8e8c333 --- /dev/null +++ b/input/share.h @@ -0,0 +1,7 @@ +#include "../types.h" +#include "../input.h" +#include "../fce.h" +#include "../svga.h" +#include "../x6502.h" + +void FCEU_DrawCursor(uint8 *buf, int xc, int yc); diff --git a/input/zapper.c b/input/zapper.c new file mode 100644 index 0000000..16f9e15 --- /dev/null +++ b/input/zapper.c @@ -0,0 +1,143 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "share.h" + +typedef struct { + uint32 mzx,mzy,mzb; + int zap_readbit; + int bogo; + uint32 colok; + uint32 coloklast; +} ZAPPER; + +static ZAPPER ZD[2]; + +static void FP_FASTAPASS(3) ZapperThingy(int w, uint8 *buf, int line) +{ + int mzx=ZD[w].mzx; + + if(line==0) ZD[w].colok=1<<16; /* Disable it. */ + + ZD[w].coloklast=ZD[w].colok; + + if(ZD[w].mzb&2) return; + if((line>=ZD[w].mzy-3 && line<=ZD[w].mzy+3) && mzx<256) + { + int a,sum,x; + + for(x=-4;x<4;x++) + { + if((mzx+x)<0 || (mzx+x)>255) continue; + a=buf[mzx+x]&63; + sum=palo[a].r+palo[a].g+palo[a].b; + + if(sum>=100*3) + { + ZD[w].colok=timestamp+mzx/3; + break; + } + } + } + +} + +static INLINE int CheckColor(int w) +{ + if( (timestamp>=ZD[w].coloklast && timestamp<=(ZD[w].coloklast+100)) || + (timestamp>=ZD[w].colok && timestamp<=(ZD[w].colok+100)) ) + return 0; + return 1; +} + +static uint8 FP_FASTAPASS(1) ReadZapperVS(int w) +{ + uint8 ret=0; + + if(ZD[w].zap_readbit==4) ret=1; + + if(ZD[w].zap_readbit==7) + { + if(ZD[w].bogo) + ret|=0x1; + } + if(ZD[w].zap_readbit==6) + { + if(!CheckColor(w)) + ret|=0x1; + } + ZD[w].zap_readbit++; + return ret; +} + +static void FP_FASTAPASS(1) StrobeZapperVS(int w) +{ + ZD[w].zap_readbit=0; +} + +static uint8 FP_FASTAPASS(1) ReadZapper(int w) +{ + uint8 ret=0; + if(ZD[w].bogo) + ret|=0x10; + if(CheckColor(w)) + ret|=0x8; + return ret; +} + +static void FASTAPASS(3) DrawZapper(int w, uint8 *buf, int arg) +{ + if(arg) + FCEU_DrawCursor(buf, ZD[w].mzx,ZD[w].mzy); +} + +static void FP_FASTAPASS(3) UpdateZapper(int w, void *data, int arg) +{ + uint32 *ptr=data; + + if(ZD[w].bogo) + ZD[w].bogo--; + if(ptr[2]&3 && (!(ZD[w].mzb&3))) + ZD[w].bogo=5; + + ZD[w].mzx=ptr[0]; + ZD[w].mzy=ptr[1]; + ZD[w].mzb=ptr[2]; + + if(ZD[w].mzb&2 || ZD[w].mzx>=256 || ZD[w].mzy>=240) + ZD[w].colok=0; +} + +static INPUTC ZAPC={ReadZapper,0,0,UpdateZapper,ZapperThingy,DrawZapper}; +static INPUTC ZAPVSC={ReadZapperVS,0,StrobeZapperVS,UpdateZapper,ZapperThingy,DrawZapper}; + +INPUTC *FCEU_InitZapper(int w) +{ + memset(&ZD[w],0,sizeof(ZAPPER)); + if(FCEUGameInfo.type==GIT_VSUNI) + return(&ZAPVSC); + else + return(&ZAPC); +} + + diff --git a/mappers/105.c b/mappers/105.c new file mode 100644 index 0000000..977e9f2 --- /dev/null +++ b/mappers/105.c @@ -0,0 +1,131 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define MMC1_reg mapbyte1 +#define MMC1_buf mapbyte2[0] +#define MMC1_sft mapbyte3[0] +#define lastn mapbyte2[1] + +#define NWCDIP 0xE + +static void FP_FASTAPASS(1) NWCIRQHook(int a) +{ + if(!(MMC1_reg[1]&0x10)) + { + IRQCount+=a; + if((IRQCount|(NWCDIP<<25))>=0x3e000000) + { + IRQCount=0; + X6502_IRQBegin(FCEU_IQEXT); + } + } +} + + +static void MMC1PRG(void) +{ + if(MMC1_reg[1]&8) + { + switch(MMC1_reg[0]&0xC) + { + case 0xC: ROM_BANK16(0x8000,8+(MMC1_reg[3]&7)); + ROM_BANK16(0xC000,15); + break; + case 0x8: ROM_BANK16(0xC000,8+((MMC1_reg[3])&7)); + ROM_BANK16(0x8000,8); + break; + case 0x0: + case 0x4: + ROM_BANK16(0x8000,8+(MMC1_reg[3]&6)); + ROM_BANK16(0xc000,8+((MMC1_reg[3]&6)+1)); + break; + } + } + else + { + ROM_BANK32((MMC1_reg[1]>>1)&3); + } +} + +DECLFW(Mapper105_write) +{ + int n=(A>>13)-4; + + if (V&0x80) + { + MMC1_sft=MMC1_buf=0; + return; + } + + if(lastn!=n) + { + MMC1_sft=MMC1_buf=0; + } + lastn=n; + + //MMC1_reg[n]&=~((1)<<(MMC1_sft)); + MMC1_buf|=(V&1)<<(MMC1_sft++); + + if (MMC1_sft==5) + { + if(n==3) V&=0xF; + else V&=0x1F; + + MMC1_reg[n]=V=MMC1_buf; + MMC1_sft = MMC1_buf=0; + + switch(n){ + case 0: + switch(MMC1_reg[0]&3) + { + case 2: MIRROR_SET(0);break; + case 3: MIRROR_SET(1);break; + case 0: onemir(0);break; + case 1: onemir(1);break; + } + MMC1PRG(); + break; + case 1: + if(MMC1_reg[1]&0x10) + {IRQCount=0;X6502_IRQEnd(FCEU_IQEXT);} + MMC1PRG(); + break; + case 3: + MMC1PRG(); + break; + } + } +} + + +void Mapper105_init(void) +{ + int i; + for(i=0;i<4;i++) MMC1_reg[i]=0; + MMC1_sft = MMC1_buf =0; + MMC1_reg[0]=0xC; + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper105_write); + MapIRQHook=NWCIRQHook; +} + diff --git a/mappers/112.c b/mappers/112.c new file mode 100644 index 0000000..bfa4595 --- /dev/null +++ b/mappers/112.c @@ -0,0 +1,52 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper112_write) +{ +switch(A) +{ + case 0xe000:MIRROR_SET(V&1);break; + case 0x8000:mapbyte1[0]=V;break; + case 0xa000:switch(mapbyte1[0]) + { + case 0:ROM_BANK8(0x8000,V);break; + case 1:ROM_BANK8(0xA000,V);break; + case 2: V&=0xFE;VROM_BANK1(0,V); + VROM_BANK1(0x400,(V+1));break; + case 3: V&=0xFE;VROM_BANK1(0x800,V); + VROM_BANK1(0xC00,(V+1));break; + case 4:VROM_BANK1(0x1000,V);break; + case 5:VROM_BANK1(0x1400,V);break; + case 6:VROM_BANK1(0x1800,V);break; + case 7:VROM_BANK1(0x1c00,V);break; + } + break; + } +} + +void Mapper112_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper112_write); +} + diff --git a/mappers/113.c b/mappers/113.c new file mode 100644 index 0000000..66548b5 --- /dev/null +++ b/mappers/113.c @@ -0,0 +1,47 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +/* I'm getting the feeling this is another "jam two different bank + switching hardwares into one mapper". +*/ + +/* HES 4-in-1 */ +DECLFW(Mapper113_write) +{ + ROM_BANK32((V>>3)&7); + VROM_BANK8(V&7); +} + + +/* Deathbots */ +DECLFW(Mapper113_writeh) +{ + ROM_BANK32(V&0xF); +} + + +void Mapper113_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x4020,0x7fff,Mapper113_write); + SetWriteHandler(0x8000,0xffff,Mapper113_writeh); +} diff --git a/mappers/117.c b/mappers/117.c new file mode 100644 index 0000000..aa0ea5f --- /dev/null +++ b/mappers/117.c @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper117_write) +{ + switch(A) + { + case 0xc003:IRQCount=V;break; + case 0xc001:IRQa=V;break; + case 0xa000:VROM_BANK1(0x0000,V);break; + case 0xa001:VROM_BANK1(0x0400,V);break; + case 0xa002:VROM_BANK1(0x0800,V);break; + case 0xa003:VROM_BANK1(0x0c00,V);break; + case 0xa004:VROM_BANK1(0x1000,V);break; + case 0xa005:VROM_BANK1(0x1400,V);break; + case 0xa006:VROM_BANK1(0x1800,V);break; + case 0xa007:VROM_BANK1(0x1c00,V);break; + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0x8001:ROM_BANK8(0xa000,V);break; + case 0x8002:ROM_BANK8(0xc000,V);break; + case 0x8003:ROM_BANK8(0xe000,V);break; + } +} + +static void Mapper117_hb(void) +{ + if(IRQa) + { + if(IRQCount<=0) + { + IRQa=0; + TriggerIRQ(); + } + else + { + IRQCount--; + } + } +} + +void Mapper117_init(void) +{ + GameHBIRQHook=Mapper117_hb; + SetWriteHandler(0x8000,0xffff,Mapper117_write); +} + diff --git a/mappers/15.c b/mappers/15.c new file mode 100644 index 0000000..05146af --- /dev/null +++ b/mappers/15.c @@ -0,0 +1,87 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + + +DECLFW(Mapper15_write) +{ +switch(A) + { + case 0x8000: + if(V&0x80) + { + ROM_BANK8(0x8000,(V<<1)+1); + ROM_BANK8(0xA000,(V<<1)); + ROM_BANK8(0xC000,(V<<1)+2); + ROM_BANK8(0xE000,(V<<1)+1); + } + else + { + ROM_BANK16(0x8000,V); + ROM_BANK16(0xC000,V+1); + } + MIRROR_SET((V>>6)&1); + break; + case 0x8001: + MIRROR_SET(0); + ROM_BANK16(0x8000,V); + ROM_BANK16(0xc000,~0); + break; + case 0x8002: + if(V&0x80) + { + ROM_BANK8(0x8000,((V<<1)+1)); + ROM_BANK8(0xA000,((V<<1)+1)); + ROM_BANK8(0xC000,((V<<1)+1)); + ROM_BANK8(0xE000,((V<<1)+1)); + } + else + { + ROM_BANK8(0x8000,(V<<1)); + ROM_BANK8(0xA000,(V<<1)); + ROM_BANK8(0xC000,(V<<1)); + ROM_BANK8(0xE000,(V<<1)); + } + break; + case 0x8003: + MIRROR_SET((V>>6)&1); + if(V&0x80) + { + ROM_BANK8(0xC000,(V<<1)+1); + ROM_BANK8(0xE000,(V<<1)); + } + else + { + ROM_BANK16(0xC000,V); + } + break; + } +} + +void Mapper15_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper15_write); +} + diff --git a/mappers/151.c b/mappers/151.c new file mode 100644 index 0000000..cc59c6c --- /dev/null +++ b/mappers/151.c @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper151_write) +{ + switch(A&0xF000) + { + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0xA000:ROM_BANK8(0xA000,V);break; + case 0xC000:ROM_BANK8(0xC000,V);break; + case 0xe000:VROM_BANK4(0x0000,V);break; + case 0xf000:VROM_BANK4(0x1000,V);break; + } +} + +void Mapper151_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper151_write); +} + diff --git a/mappers/16.c b/mappers/16.c new file mode 100644 index 0000000..3742c34 --- /dev/null +++ b/mappers/16.c @@ -0,0 +1,129 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static void FP_FASTAPASS(1) BandaiIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<0) + { + X6502_IRQBegin(FCEU_IQEXT); + //printf("IRQ: %d, %d\n",scanline,timestamp); + IRQa=0; + IRQCount=0xFFFF; + } + } +} + +static DECLFW(Mapper16_write) +{ + A&=0xF; + + if(A<=0x7) + VROM_BANK1(A<<10,V); + else if(A==0x8) + ROM_BANK16(0x8000,V); + else switch(A) { + case 0x9: switch(V&3) { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + case 0xA:X6502_IRQEnd(FCEU_IQEXT); + IRQa=V&1; + IRQCount=IRQLatch; + break; + case 0xB:IRQLatch&=0xFF00; + IRQLatch|=V; + break; + case 0xC:IRQLatch&=0xFF; + IRQLatch|=V<<8; + break; + case 0xD: break;/* Serial EEPROM control port */ + } +} + +// 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 PRGO(void) +{ + uint32 base=(mapbyte1[0]&1)<<4; + ROM_BANK16(0x8000,(mapbyte2[0]&0xF)|base); + ROM_BANK16(0xC000,base|0xF); +} + +static DECLFW(Mapper153_write) +{ + A&=0xF; + if(A<=0x7) + { + mapbyte1[A&7]=V; + PRGO(); + } + else if(A==0x8) + { + mapbyte2[0]=V; + PRGO(); + } + else switch(A) { + case 0x9: switch(V&3) { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + case 0xA:X6502_IRQEnd(FCEU_IQEXT); + IRQa=V&1; + IRQCount=IRQLatch; + break; + case 0xB:IRQLatch&=0xFF00; + IRQLatch|=V; + break; + case 0xC:IRQLatch&=0xFF; + IRQLatch|=V<<8; + break; + } +} + +void Mapper16_init(void) +{ + MapIRQHook=BandaiIRQHook; + SetWriteHandler(0x6000,0xFFFF,Mapper16_write); +} + +void Mapper153_init(void) +{ + MapIRQHook=BandaiIRQHook; + SetWriteHandler(0x8000,0xFFFF,Mapper153_write); + /* This mapper/board seems to have WRAM at $6000-$7FFF, so I'll let the + main ines code take care of that memory region. */ +} diff --git a/mappers/17.c b/mappers/17.c new file mode 100644 index 0000000..66b85d4 --- /dev/null +++ b/mappers/17.c @@ -0,0 +1,74 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +static void FP_FASTAPASS(1) FFEIRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + + if(IRQCount>=0x10000) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0; + } + } +} + + +DECLFW(Mapper17_write) +{ + switch(A){ + default: + break; + case 0x42FE: + onemir((V>>4)&1); + break; + case 0x42FF: + MIRROR_SET((V>>4)&1); + break; + case 0x4501:IRQa=V&1;break; + case 0x4502:IRQCount&=0xFF00;IRQCount|=V;break; + case 0x4503:IRQCount&=0x00FF;IRQCount|=V<<8;IRQa=1;break; + case 0x4504: ROM_BANK8(0x8000,V);break; + case 0x4505: ROM_BANK8(0xA000,V);break; + case 0x4506: ROM_BANK8(0xC000,V);break; + case 0x4507: ROM_BANK8(0xE000,V);break; + case 0x4510: VROM_BANK1(0x0000,V);break; + case 0x4511: VROM_BANK1(0x0400,V);break; + case 0x4512: VROM_BANK1(0x0800,V);break; + case 0x4513: VROM_BANK1(0x0C00,V);break; + case 0x4514: VROM_BANK1(0x1000,V);break; + case 0x4515: VROM_BANK1(0x1400,V);break; + case 0x4516: VROM_BANK1(0x1800,V);break; + case 0x4517: VROM_BANK1(0x1C00,V);break; + } +} + +void Mapper17_init(void) +{ +MapIRQHook=FFEIRQHook; +SetWriteHandler(0x4020,0x5fff,Mapper17_write); +} diff --git a/mappers/18.c b/mappers/18.c new file mode 100644 index 0000000..f77e831 --- /dev/null +++ b/mappers/18.c @@ -0,0 +1,80 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define K4buf mapbyte2 +#define K4buf2 mapbyte3 + +void FP_FASTAPASS(1) JalecoIRQHook(int a) +{ + if(IRQa && IRQCount) + { + IRQCount-=a; + if(IRQCount<=0) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount=0; + IRQa=0; + } + } +} + +DECLFW(Mapper18_write) +{ + A&=0xF003; + if(A>=0x8000 && A<=0x9001) + { + int x=((A>>1)&1)|((A-0x8000)>>11); + + K4buf2[x]&=(0xF0)>>((A&1)<<2); + K4buf2[x]|=(V&0xF)<<((A&1)<<2); + ROM_BANK8(0x8000+(x<<13),K4buf2[x]); + } + else if(A>=0xa000 && A<=0xd003) + { + int x=((A>>1)&1)|((A-0xA000)>>11); + + K4buf[x]&=(0xF0)>>((A&1)<<2); + K4buf[x]|=(V&0xF)<<((A&1)<<2); + VROM_BANK1(x<<10,K4buf[x]); + } + else switch(A) + { + case 0xe000:IRQLatch&=0xFFF0;IRQLatch|=(V&0x0f);break; + case 0xe001:IRQLatch&=0xFF0F;IRQLatch|=(V&0x0f)<<4;break; + case 0xe002:IRQLatch&=0xF0FF;IRQLatch|=(V&0x0f)<<8;break; + case 0xe003:IRQLatch&=0x0FFF;IRQLatch|=(V&0x0f)<<12;break; + case 0xf000:IRQCount=IRQLatch; + break; + case 0xf001:IRQa=V&1; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0xf002:MIRROR_SET2(V&1); + if(V&2) onemir(0); + break; + } +} + +void Mapper18_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper18_write); + MapIRQHook=JalecoIRQHook; +} diff --git a/mappers/180.c b/mappers/180.c new file mode 100644 index 0000000..bd877c8 --- /dev/null +++ b/mappers/180.c @@ -0,0 +1,14 @@ +#include "mapinc.h" + + + +DECLFW(Mapper180_write) +{ +ROM_BANK16(0xC000,V); +} + +void Mapper180_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper180_write); +} + diff --git a/mappers/182.c b/mappers/182.c new file mode 100644 index 0000000..5fe0750 --- /dev/null +++ b/mappers/182.c @@ -0,0 +1,48 @@ +#include "mapinc.h" + +DECLFW(Mapper182_write) +{ + switch(A&0xf003) + { + case 0xe003:IRQCount=V;IRQa=1;break; + case 0x8001:MIRROR_SET(V&1);break; + case 0xA000:mapbyte1[0]=V;break; + case 0xC000: + switch(mapbyte1[0]&7) + { + case 0:VROM_BANK2(0x0000,V>>1);break; + case 1:VROM_BANK1(0x1400,V);break; + case 2:VROM_BANK2(0x0800,V>>1);break; + case 3:VROM_BANK1(0x1c00,V);break; + case 4:ROM_BANK8(0x8000,V);break; + case 5:ROM_BANK8(0xA000,V);break; + case 6:VROM_BANK1(0x1000,V);break; + case 7:VROM_BANK1(0x1800,V);break; + } + break; + + + } +} + +void blop(void) +{ + if(IRQa) + { + if(IRQCount) + { + IRQCount--; + if(!IRQCount) + { + IRQa=0; + TriggerIRQ(); + } + } + } +} +void Mapper182_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper182_write); + GameHBIRQHook=blop; +} + diff --git a/mappers/184.c b/mappers/184.c new file mode 100644 index 0000000..70fd6b1 --- /dev/null +++ b/mappers/184.c @@ -0,0 +1,15 @@ +#include "mapinc.h" + + + +DECLFW(Mapper184_write) +{ +VROM_BANK4(0x0000,V); +VROM_BANK4(0x1000,(V>>4)); +} + +void Mapper184_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper184_write); +} + diff --git a/mappers/189.c b/mappers/189.c new file mode 100644 index 0000000..7d8bcff --- /dev/null +++ b/mappers/189.c @@ -0,0 +1,37 @@ +/* Is this an MMC3 workalike piece of hardware, with the addition of + a register at $4120 or does it have only partial MMC3 functionality? + A good test would be to see if commands 6 and 7 can change PRG banks + and of course test the regs >=$c000, on the real cart. +*/ +#include "mapinc.h" + +#define cmd mapbyte1[0] +static DECLFW(Mapper189_write) +{ + if(A==0x4120) ROM_BANK32(V>>4); + else switch(A&0xE001) + { + case 0xa000:MIRROR_SET(V&1);break; + case 0x8000:cmd=V;break; + case 0x8001:switch(cmd&7) + { + case 0:VROM_BANK2(0x0000,V>>1);break; + case 1:VROM_BANK2(0x0800,V>>1);break; + case 2:VROM_BANK1(0x1000,V);break; + case 3:VROM_BANK1(0x1400,V);break; + case 4:VROM_BANK1(0x1800,V);break; + case 5:VROM_BANK1(0x1C00,V);break; + } + break; + + } +} + +void Mapper189_init(void) +{ + SetWriteHandler(0x4120,0xFFFF,Mapper189_write); + SetReadHandler(0x6000,0x7FFF,0); + ROM_BANK32(0); +} + + diff --git a/mappers/19.c b/mappers/19.c new file mode 100644 index 0000000..53de232 --- /dev/null +++ b/mappers/19.c @@ -0,0 +1,286 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define dopol mapbyte1[0] +#define gorfus mapbyte1[1] +#define gorko mapbyte1[2] + +static void NamcoSound(int Count); +static void NamcoSoundHack(void); + +static int32 inc; +void FP_FASTAPASS(1) NamcoIRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + if(IRQCount>=0x7fff) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQa=0; + IRQCount=0x7fff; + } + } +} + +DECLFR(Namco_Read4800) +{ + uint8 ret=MapperExRAM[dopol&0x7f]; + if(dopol&0x80) + dopol=(dopol&0x80)|((dopol+1)&0x7f); + return ret; +} + +DECLFR(Namco_Read5000) +{ + return(IRQCount); +} + +DECLFR(Namco_Read5800) +{ + return(IRQCount>>8); +} + +static void FASTAPASS(2) DoNTARAMROM(int w, uint8 V) +{ + mapbyte2[w]=V; + //if(V>=0xE0) + // setntamem(NTARAM+((V&1)<<10), 1, w); + if((V>=0xE0)) // || ((gorko>>(6+(w>>1)))&1) ) + setntamem(NTARAM+((V&1)<<10), 1, w); + else + { + V&=CHRmask1[0]; + setntamem(VROM+(V<<10), 0, w); + } +// else +// setntamem(NTARAM+((V&1)<<10), 1, w); +} + +static void FixNTAR(void) +{ + int x; + + for(x=0;x<4;x++) + DoNTARAMROM(x,mapbyte2[x]); +} + +static void FASTAPASS(2) DoCHRRAMROM(int x, uint8 V) +{ + mapbyte3[x]=V; + if(!((gorfus>>((x>>2)+6))&1) && (V>=0xE0)) + VRAM_BANK1(x<<10,V&7); + else + VROM_BANK1(x<<10,V); +} + +static void FixCRR(void) +{ + int x; + for(x=0;x<8;x++) + DoCHRRAMROM(x,mapbyte3[x]); +} + +static DECLFW(Mapper19_write) +{ + A&=0xF800; + + if(A>=0x8000 && A<=0xb800) + DoCHRRAMROM((A-0x8000)>>11,V); + else if(A>=0xC000 && A<=0xd800) + DoNTARAMROM((A-0xC000)>>11,V); + else switch(A) + { + case 0x4800: + if(dopol&0x40) + { + if(FSettings.SndRate) + { + NamcoSoundHack(); + GameExpSound.Fill=NamcoSound; + } + } + MapperExRAM[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:gorko=V&0xC0; + //FixNTAR(); + ROM_BANK8(0x8000,V); + break; + case 0xE800:gorfus=V&0xC0; + FixCRR(); + ROM_BANK8(0xA000,V); + break; + case 0xF000: + ROM_BANK8(0xC000,V); + break; + } +} + +static int dwave=0; +static void DoNamcoSound(uint32 *Wave, int Count); + +static void NamcoSoundHack(void) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + if(a) + DoNamcoSound(&Wave[dwave], a); + dwave+=a; +} + +static void NamcoSound(int Count) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + if(a) + DoNamcoSound(&Wave[dwave], a); + dwave=0; +} + +static uint8 PlayIndex[8]; +static int32 vcount[8]; + +static void DoNamcoSound(uint32 *Wave, int Count) +{ + int P,V; + + + //FCEU_DispMessage("%d",MapperExRAM[0x7F]>>4); + for(P=7;P>=7-((MapperExRAM[0x7F]>>4)&7);P--) + { + if((MapperExRAM[0x44+(P<<3)]&0xE0) && (MapperExRAM[0x47+(P<<3)]&0xF)) + { + uint32 freq; + int32 vco; + uint32 duff,duff2,lengo,envelope; + //uint64 ta; + + vco=vcount[P]; + freq=MapperExRAM[0x40+(P<<3)]; + freq|=MapperExRAM[0x42+(P<<3)]<<8; + freq|=(MapperExRAM[0x44+(P<<3)]&3)<<16; + + if(!freq) continue; + + { + int c=((MapperExRAM[0x7F]>>4)&7)+1; + + inc=(long double)(FSettings.SndRate<<15)/((long double)freq* + 21477272/((long double)0x400000*c*45)); + } + + envelope=((MapperExRAM[0x47+(P<<3)]&0xF)<<18)/15; + duff=MapperExRAM[(((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])>>1)&0xFF)]; + if((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])&1) + duff>>=4; + duff&=0xF; + duff2=(duff*envelope)>>14; + + lengo=((8-((MapperExRAM[0x44+(P<<3)]>>2)&7)))<<2; + for(V=0;V=inc) + { + PlayIndex[P]++; + if(PlayIndex[P]>=lengo) + PlayIndex[P]=0; + vco-=inc; + duff=MapperExRAM[(((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])&0xFF)>>1)]; + if((MapperExRAM[0x46+(P<<3)]+PlayIndex[P])&1) + duff>>=4; + duff&=0xF; + duff2=(duff*envelope)>>14; + } + Wave[V>>4]+=duff2; + vco+=0x8000; + } + vcount[P]=vco; + } + } +} + +static void Mapper19_StateRestore(int version) +{ + FixNTAR(); + if(version>=80) + FixCRR(); +} + +static void M19SC(void) +{ + if(FSettings.SndRate) + Mapper19_ESI(); +} + +void Mapper19_ESI(void) +{ + GameExpSound.RChange=M19SC; + SetWriteHandler(0xf800,0xffff,Mapper19_write); + SetWriteHandler(0x4800,0x4fff,Mapper19_write); + SetReadHandler(0x4800,0x4fff,Namco_Read4800); +} + +void Mapper19_init(void) +{ + if(!Mirroring) + { + DoNTARAMROM(0,0xE0); + DoNTARAMROM(1,0xE0); + DoNTARAMROM(2,0xE1); + DoNTARAMROM(3,0xE1); + } + else + { + DoNTARAMROM(0,0xE0); + DoNTARAMROM(2,0xE0); + DoNTARAMROM(1,0xE1); + DoNTARAMROM(3,0xE1); + } + VROM_BANK8(~0); + SetWriteHandler(0x8000,0xffff,Mapper19_write); + SetWriteHandler(0x4020,0x5fff,Mapper19_write); + SetReadHandler(0x4800,0x4fff,Namco_Read4800); + SetReadHandler(0x5000,0x57ff,Namco_Read5000); + SetReadHandler(0x5800,0x5fff,Namco_Read5800); + + MapIRQHook=NamcoIRQHook; + MapStateRestore=Mapper19_StateRestore; + GameExpSound.RChange=M19SC; + if(FSettings.SndRate) + Mapper19_ESI(); + gorfus=0xFF; +} + diff --git a/mappers/21.c b/mappers/21.c new file mode 100644 index 0000000..bda50a5 --- /dev/null +++ b/mappers/21.c @@ -0,0 +1,102 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define K4buf mapbyte2 +#define K4IRQ mapbyte1[1] +#define K4sel mapbyte1[0] + +static int acount=0; + +DECLFW(Mapper21_write) +{ + A|=((A>>5)&0xF); + + if((A&0xF000)==0xA000) + ROM_BANK8(0xA000,V); + else if((A&0xF000)==0x8000) + { + if(K4sel&2) + ROM_BANK8(0xC000,V); + else + ROM_BANK8(0x8000,V); + } + else if(A>=0xb000 && A<=0xefff) + { + A&=0xF006; + { + int x=((A>>2)&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&2)<<1); + K4buf[x]|=(V&0xF)<<((A&2)<<1); + VROM_BANK1(x<<10,K4buf[x]); + } + + } + else switch(A&0xF006) + { + case 0x9000: + switch(V&0x3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0x9006: + case 0x9004: + case 0x9002:if((K4sel&2)!=(V&2)) + { + uint8 swa; + swa=PRGBankList[0]; + ROM_BANK8(0x8000,PRGBankList[2]); + ROM_BANK8(0xc000,swa); + } + K4sel=V; + break; + case 0xf000:IRQLatch&=0xF0;IRQLatch|=V&0xF;break; + case 0xf002:IRQLatch&=0x0F;IRQLatch|=V<<4;break; + case 0xf004:IRQCount=IRQLatch; + IRQa=V&2;K4IRQ=V&1;break; + case 0xf006:IRQa=K4IRQ;break; + } +} +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + #define LCYCS 114 + if(IRQa) + { + acount+=a; + if(acount>=LCYCS) + { + doagainbub:acount-=LCYCS;IRQCount++; + if(IRQCount&0x100) {TriggerIRQ();IRQCount=IRQLatch;} + if(acount>=LCYCS) goto doagainbub; + } + } +} + +void Mapper21_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper21_write); + MapIRQHook=KonamiIRQHook; +} diff --git a/mappers/22.c b/mappers/22.c new file mode 100644 index 0000000..44ebe25 --- /dev/null +++ b/mappers/22.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define K4buf mapbyte2 + + + +DECLFW(Mapper22_write) +{ + if(A<=0xAFFF) + { + switch(A&0xF000) + { + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0xa000:ROM_BANK8(0xA000,V);break; + case 0x9000:switch(V&3) + { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + } + } + else + { + A&=0xF003; + if(A>=0xb000 && A<=0xe003) + { + int x=(A&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&2)<<1); + K4buf[x]|=(V&0xF)<<((A&2)<<1); + VROM_BANK1(x<<10,K4buf[x]>>1); + } + } +} + + +void Mapper22_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper22_write); +} diff --git a/mappers/225.c b/mappers/225.c new file mode 100644 index 0000000..6edb486 --- /dev/null +++ b/mappers/225.c @@ -0,0 +1,87 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define reg1 mapbyte1[0] +#define reg2 mapbyte1[1] +#define reg3 mapbyte1[2] +#define reg4 mapbyte1[3] + +DECLFR(A110in1read) +{ +switch(A&0x3) + { + case 0:return reg1;break; + case 1:return reg2;break; + case 2:return reg3;break; + case 3:return reg4;break; + } +return 0xF; +} +DECLFW(A110in1regwr) +{ +switch(A&0x3) + { + case 0:reg1=V&0xF;break; + case 1:reg2=V&0xF;break; + case 2:reg3=V&0xF;break; + case 3:reg4=V&0xF;break; + } +} + +DECLFW(Mapper225_write) +{ + int banks=0; + + MIRROR_SET((A>>13)&1); + if(A&0x4000) + banks=1; + else + banks=0; + + VROM_BANK8(((A&0x003f)+(banks<<6))); + if(A&0x1000) + { + if(A&0x40) + { + ROM_BANK16(0x8000,((((((A>>7)&0x1F)+(banks<<5)))<<1)+1)); + ROM_BANK16(0xC000,((((((A>>7)&0x1F)+(banks<<5)))<<1)+1)); + } + else + { + ROM_BANK16(0x8000,(((((A>>7)&0x1F)+(banks<<5)))<<1)); + ROM_BANK16(0xC000,(((((A>>7)&0x1F)+(banks<<5)))<<1)); + } + } + else + { + ROM_BANK32(((((A>>7)&0x1F)+(banks<<5)))); + } +} + +void Mapper225_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper225_write); + SetReadHandler(0x5800,0x5fff,A110in1read); + SetWriteHandler(0x5800,0x5fff,A110in1regwr); +} + diff --git a/mappers/226.c b/mappers/226.c new file mode 100644 index 0000000..41fd9a4 --- /dev/null +++ b/mappers/226.c @@ -0,0 +1,105 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define rg mapbyte1 +static void DoPRG(void) +{ + int32 b=((rg[0]>>1)&0xF) | ((rg[0]>>3)&0x10) | ((rg[1]&1)<<5); + if(rg[0]&0x20) // 16 KB + { + ROM_BANK16(0x8000,(b<<1)|(rg[0]&1)); + ROM_BANK16(0xC000,(b<<1)|(rg[0]&1)); + } + else + ROM_BANK32(b); +} + +static DECLFW(Mapper226_write) +{ + rg[A&1]=V; + DoPRG(); + if(A&1) + { + if(rg[1]&2) + PPUCHRRAM=0; // Write protected. + else + PPUCHRRAM=0xFF; // Not write protected. + } + else + MIRROR_SET2((rg[0]>>6)&1); +} + +static void M26Reset(void) +{ + rg[0]=rg[1]=0; + DoPRG(); + PPUCHRRAM=0xFF; + MIRROR_SET2(0); +} + +static void M26Restore(int version) +{ + DoPRG(); + if(rg[1]&2) + PPUCHRRAM=0; // Write protected. + else + PPUCHRRAM=0xFF; // Not write protected. + MIRROR_SET2((rg[0]>>6)&1); +} + +void Mapper226_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper226_write); + MapperReset=M26Reset; + GameStateRestore=M26Restore; + M26Reset(); +} + +#ifdef OLD // What the heck is this?? +DECLFW(Mapper226_write) +{ + MIRROR_SET((A>>13)&1); + VROM_BANK8(A&0x7F); + if(A&0x1000) + { + if(A&0x40) + { + ROM_BANK16(0x8000,(((A>>7))<<1)+1); + ROM_BANK16(0xC000,(((A>>7))<<1)+1); + } + else + { + ROM_BANK16(0x8000,(((A>>7))<<1)); + ROM_BANK16(0xC000,(((A>>7))<<1)); + } + } + else + { + ROM_BANK32(A>>7); + } +} + +void Mapper226_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper226_write); +} +#endif diff --git a/mappers/227.c b/mappers/227.c new file mode 100644 index 0000000..a006ce3 --- /dev/null +++ b/mappers/227.c @@ -0,0 +1,79 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define rg mapbyte1 + +static void DoSync(uint32 A) +{ + int32 p=((A>>3)&0xF) | ((A>>4)&0x10); + + rg[0]=A; + rg[1]=A>>8; + + MIRROR_SET((A>>1)&1); + if(A&1) //32 KB + { + ROM_BANK32(p); + } + else //16 KB + { + ROM_BANK16(0x8000,(p<<1)|((A&4)>>2)); + ROM_BANK16(0xc000,(p<<1)|((A&4)>>2)); + } + if(A&0x80) + { + PPUCHRRAM=0; + } + else + { + PPUCHRRAM=0xFF; + if(A&0x200) + ROM_BANK16(0xC000,(p<<1)|7); + else + ROM_BANK16(0xC000,(p<<1)&(~7)); + } +} + +static DECLFW(Mapper227_write) +{ + rg[A&1]=V; + DoSync(A); +} + +static void M227Reset(void) +{ + rg[0]=rg[1]=0; + DoSync(0); +} + +static void M227Restore(int version) +{ + DoSync(rg[0]|(rg[1]<<8)); +} + +void Mapper227_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper227_write); + MapperReset=M227Reset; + GameStateRestore=M227Restore; + M227Reset(); +} diff --git a/mappers/228.c b/mappers/228.c new file mode 100644 index 0000000..741bab0 --- /dev/null +++ b/mappers/228.c @@ -0,0 +1,52 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static DECLFW(Mapper228_write) +{ + uint32 page,pagel,pageh; + + MIRROR_SET((A>>13)&1); + + page=(A>>7)&0x3F; + if((page&0x30)==0x30) + page-=0x10; + + pagel=pageh=(page<<1) + (((A>>6)&1)&((A>>5)&1)); + pageh+=((A>>5)&1)^1; + + ROM_BANK16(0x8000,pagel); + ROM_BANK16(0xC000,pageh); + VROM_BANK8( (V&0x3) | ((A&0xF)<<2) ); +} + +static void A52Reset(void) +{ + Mapper228_write(0,0); +} + +void Mapper228_init(void) +{ + MapperReset=A52Reset; + A52Reset(); + SetWriteHandler(0x8000,0xffff,Mapper228_write); +} + diff --git a/mappers/229.c b/mappers/229.c new file mode 100644 index 0000000..8ffe911 --- /dev/null +++ b/mappers/229.c @@ -0,0 +1,48 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper229_write) +{ +if(A>=0x8000) +{ +MIRROR_SET((A>>5)&1); +if(!(A&0x1e)) + { + ROM_BANK32(0); + } +else + { + ROM_BANK16(0x8000,A&0x1f); + ROM_BANK16(0xC000,A&0x1f); + } + VROM_BANK8(A); +} + +} + +void Mapper229_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper229_write); +} + diff --git a/mappers/23.c b/mappers/23.c new file mode 100644 index 0000000..12f30d8 --- /dev/null +++ b/mappers/23.c @@ -0,0 +1,102 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define K4buf mapbyte2 +#define K4IRQ mapbyte1[1] +#define K4sel mapbyte1[0] + +DECLFW(Mapper23_write) +{ + if((A&0xF000)==0x8000) + { + if(K4sel&2) + ROM_BANK8(0xC000,V); + else + ROM_BANK8(0x8000,V); + } + else if((A&0xF000)==0xA000) + ROM_BANK8(0xA000,V); + else + { + A|=((A>>2)&0x3)|((A>>4)&0x3)|((A>>6)&0x3); + A&=0xF003; + if(A>=0xb000 && A<=0xe003) + { + int x=((A>>1)&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&1)<<2); + K4buf[x]|=(V&0xF)<<((A&1)<<2); + VROM_BANK1(x<<10,K4buf[x]); + } + else + switch(A) + { + 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);IRQCount=IRQLatch;IRQa=V&2;K4IRQ=V&1;break; + case 0xf003:X6502_IRQEnd(FCEU_IQEXT);IRQa=K4IRQ;break; + case 0x9001: + case 0x9002: + case 0x9003: + if((K4sel&2)!=(V&2)) + { + uint8 swa; + swa=PRGBankList[0]; + ROM_BANK8(0x8000,PRGBankList[2]); + ROM_BANK8(0xc000,swa); + } + K4sel=V; + break; + case 0x9000: + switch(V&0x3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + } + } +} + +void FP_FASTAPASS(1) KonamiIRQHook2(int a) +{ + static int acount=0; + if(IRQa) + { + acount+=(a<<1)+a; + if(acount>=342) + { + doagainbub:acount-=342;IRQCount++; + if(IRQCount&0x100) {X6502_IRQBegin(FCEU_IQEXT);IRQCount=IRQLatch;} + if(acount>=342) goto doagainbub; + } + } +} + +void Mapper23_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper23_write); + MapIRQHook=KonamiIRQHook2; +} + diff --git a/mappers/232.c b/mappers/232.c new file mode 100644 index 0000000..9d71b24 --- /dev/null +++ b/mappers/232.c @@ -0,0 +1,50 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static void DoIt(void) +{ + ROM_BANK16(0x8000,(mapbyte1[1]&3) | ((mapbyte1[0]&0x18)>>1)); + ROM_BANK16(0xc000,3|(((mapbyte1[0])&0x18)>>1)); +} + +DECLFW(Mapper232_write) +{ + if(A<=0x9FFF) + mapbyte1[0]=V; + else + mapbyte1[1]=V; + DoIt(); +} + +static void QuattroReset(void) +{ + mapbyte1[0]=0x18; + DoIt(); +} + +void Mapper232_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper232_write); + MapperReset=QuattroReset; + QuattroReset(); +} + diff --git a/mappers/234.c b/mappers/234.c new file mode 100644 index 0000000..4a8ac45 --- /dev/null +++ b/mappers/234.c @@ -0,0 +1,107 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define r1 mapbyte1[0] +#define r2 mapbyte1[1] + +static void DoBS(void) +{ + if(r1&0x40) + { + ROM_BANK32((r1&0xE)|(r2&1)); + VROM_BANK8( ((r1&0xE)<<2) | ((r2>>4)&7) ); + } + else + { + ROM_BANK32(r1&0xF); + VROM_BANK8( ((r1&0xF)<<2) | ((r2>>4)&3) ); + } +} + +static void R1Set(uint8 V) +{ + if(r1) return; + r1=V; + MIRROR_SET(V>>7); + DoBS(); +} + +static void R2Set(uint8 V) +{ + r2=V; + DoBS(); +} + +DECLFW(R1W) +{ + R1Set(V); +} + +DECLFR(R1R) +{ + uint8 r=CartBR(A); + R1Set(r); + return r; +} + +DECLFW(R2W) +{ + R2Set(V); +} + +DECLFR(R2R) +{ + uint8 r=CartBR(A); + R2Set(r); + return r; +} + +static void M15Restore(int version) +{ + DoBS(); + MIRROR_SET(r1>>7); +} + +static void M15Reset(void) +{ + r1=r2=0; + DoBS(); + MIRROR_SET(0); +} + +void Mapper234_init(void) +{ + SetWriteHandler(0xff80,0xff9f,R1W); + SetReadHandler(0xff80,0xff9f,R1R); + + SetWriteHandler(0xffe8,0xfff7,R2W); + SetReadHandler(0xffe8,0xfff7,R2R); + + SetReadHandler(0x6000,0x7FFF,0); + SetWriteHandler(0x6000,0x7FFF,0); + + M15Reset(); + + GameStateRestore=M15Restore; + MapperReset=M15Reset; +} + diff --git a/mappers/240.c b/mappers/240.c new file mode 100644 index 0000000..4c1fd27 --- /dev/null +++ b/mappers/240.c @@ -0,0 +1,39 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper240_write) +{ + if(A<0x8000) + { + ROM_BANK32(V>>4); + VROM_BANK8(V&0xF); + } +} + +void Mapper240_init(void) +{ + SetWriteHandler(0x4020,0x5fff,Mapper240_write); + SetWriteHandler(0x8000,0xffff,Mapper240_write); +} + diff --git a/mappers/242.c b/mappers/242.c new file mode 100644 index 0000000..93f654c --- /dev/null +++ b/mappers/242.c @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +DECLFW(Mapper242_write) +{ + ROM_BANK32((A>>3)&0xF); + switch(V&3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } +} + +void Mapper242_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper242_write); +} + diff --git a/mappers/245.c b/mappers/245.c new file mode 100644 index 0000000..2a25182 --- /dev/null +++ b/mappers/245.c @@ -0,0 +1,51 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static void Synco(void) +{ + ROM_BANK8(0x8000,mapbyte2[0]); + ROM_BANK8(0xA000,mapbyte2[1]); + ROM_BANK8(0xc000,0x3e); + ROM_BANK8(0xe000,0x3f); +} +static DECLFW(Mapper245_write) +{ + switch(A&0xe001) + { + case 0xa000:mapbyte1[1]=V;Synco();break; + case 0x8000:mapbyte1[0]=V;break; + case 0x8001:switch(mapbyte1[0]&7) + { +// default:printf("ark\n");break; + case 6:mapbyte2[0]=V;Synco();break; + case 7:mapbyte2[1]=V;Synco();break; + }break; + //case 0xa001:MIRROR_SET2(V>>7);break; + } +// printf("$%04x:$%02x\n",A,V); +} + +void Mapper245_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper245_write); +} + diff --git a/mappers/246.c b/mappers/246.c new file mode 100644 index 0000000..42613fc --- /dev/null +++ b/mappers/246.c @@ -0,0 +1,44 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +DECLFW(Mapper246_write) +{ + switch(A&0xF007) + { + case 0x6000:ROM_BANK8(0x8000,V);break; + case 0x6001:ROM_BANK8(0xA000,V);break; + case 0x6002:ROM_BANK8(0xC000,V);break; + case 0x6003:ROM_BANK8(0xE000,V);break; + case 0x6004:VROM_BANK2(0x0000,V);break; + case 0x6005:VROM_BANK2(0x0800,V);break; + case 0x6006:VROM_BANK2(0x1000,V);break; + case 0x6007:VROM_BANK2(0x1800,V);break; + } +} + +void Mapper246_init(void) +{ + SetWriteHandler(0x4020,0x67ff,Mapper246_write); + SetWriteHandler(0x8000,0xffff,Mapper246_write); +} + diff --git a/mappers/248.c b/mappers/248.c new file mode 100644 index 0000000..af40264 --- /dev/null +++ b/mappers/248.c @@ -0,0 +1,90 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define cmd mapbyte1[0] +#define lpa mapbyte1[1] +#define prgl mapbyte2 + +static void PRGSynco(void) +{ + if(lpa&0x80) + { + ROM_BANK16(0x8000,lpa&0xF); + } + else + { + ROM_BANK8(0x8000,prgl[0]&0x1F); + ROM_BANK8(0xa000,prgl[1]&0x1F); + } +} + +static DECLFW(Mapper248_writelow) +{ + lpa=V; + PRGSynco(); +} + +static DECLFW(Mapper248_write) +{ + switch(A&0xF001) + { + case 0xa000:MIRROR_SET(V&1);break; // Not sure if this is right. Mirroring may be hard wired... + case 0xc000:IRQLatch=V;break; + case 0xc001:IRQCount=IRQLatch;break; + case 0xe000:IRQa=0;X6502_IRQEnd(FCEU_IQEXT);break; + case 0xe001:IRQa=1;break; + case 0x8000:cmd=V;break; + case 0x8001:switch(cmd&7) + { + case 0:VROM_BANK2(0x000,V>>1);break; + case 1:VROM_BANK2(0x800,V>>1);break; + case 2:VROM_BANK1(0x1000,V);break; + case 3:VROM_BANK1(0x1400,V);break; + case 4:VROM_BANK1(0x1800,V);break; + case 5:VROM_BANK1(0x1c00,V);break; + case 6:prgl[0]=V;PRGSynco();break; + case 7:prgl[1]=V;PRGSynco();break; + } + break; + } +} + +static void Mapper248_hb(void) +{ + if(IRQa) + { + IRQCount--; + if(IRQCount<0) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount=IRQLatch; + } + } +} + +void Mapper248_init(void) +{ + SetWriteHandler(0x6000,0x6fff,Mapper248_writelow); + SetWriteHandler(0x8000,0xffff,Mapper248_write); + GameHBIRQHook=Mapper248_hb; +} + diff --git a/mappers/249.c b/mappers/249.c new file mode 100644 index 0000000..6225d49 --- /dev/null +++ b/mappers/249.c @@ -0,0 +1,46 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +uint8 doh; + +static DECLFW(Mapper249_write) +{ + switch(A&0xe001) + { + case 0x8000:doh=V;break; + case 0x8001:switch(doh&7) + { + case 0:VROM_BANK2(0x0000,V>>1);break; + case 1:VROM_BANK2(0x0800,V>>1);break; + case 2:VROM_BANK1(0x1000,V);break; +// case 6:ROM_BANK8(0xa000,V);break; +// case 2:ROM_BANK8(0x8000,V);break; + } + } +// printf("$%04x:$%02x\n",A,V); +} + +void Mapper249_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper249_write); +} + diff --git a/mappers/24and26.c b/mappers/24and26.c new file mode 100644 index 0000000..575e5e6 --- /dev/null +++ b/mappers/24and26.c @@ -0,0 +1,351 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define vrctemp mapbyte1[0] +#define regb000 mapbyte3[0] +#define regb001 mapbyte3[1] +#define regb002 mapbyte3[2] +#define exchstat mapbyte4[0] +#define VPSG2 mapbyte3 +#define VPSG mapbyte2 + +static void DoSQV1(void); +static void DoSQV2(void); +static void DoSawV(void); + +static int swaparoo; +static int32 inc; +static DECLFW(VRC6PSGW90) +{ + DoSQV1();VPSG[0]=V; +} +static DECLFW(VRC6PSGW91) +{ + DoSQV1();VPSG[2]=V; +} +static DECLFW(VRC6PSGW92) +{ + DoSQV1();VPSG[3]=V; +} +static DECLFW(VRC6PSGWA0) +{ + DoSQV2();VPSG[4]=V; +} +static DECLFW(VRC6PSGWA1) +{ + DoSQV2();VPSG[6]=V; +} +static DECLFW(VRC6PSGWA2) +{ + DoSQV2();VPSG[7]=V; +} +static DECLFW(VRC6PSGWB0) +{ + DoSawV();VPSG2[0]=V; +} +static DECLFW(VRC6PSGWB1) +{ + DoSawV();VPSG2[1]=V; +} +static DECLFW(VRC6PSGWB2) +{ + DoSawV();VPSG2[2]=V; +} + +static int acount=0; + +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + #define LCYCS 114 + if(IRQa) + { + acount+=a; + if(acount>=LCYCS) + { + doagainbub:acount-=LCYCS;IRQCount++; + if(IRQCount==0x100) {TriggerIRQ();IRQCount=IRQLatch;} + if(acount>=LCYCS) goto doagainbub; + } + } +} + +DECLFW(Mapper24_write) +{ + if(swaparoo) + A=(A&0xFFFC)|((A>>1)&1)|((A<<1)&2); + + switch(A&0xF003) + { + case 0x8000:ROM_BANK16(0x8000,V);break; + case 0xB003: + switch(V&0xF) + { + case 0x0:MIRROR_SET2(1);break; + case 0x4:MIRROR_SET2(0);break; + case 0x8:onemir(0);break; + case 0xC:onemir(1);break; + } + break; + case 0xC000:ROM_BANK8(0xC000,V);break; + case 0xD000:VROM_BANK1(0x0000,V);break; + case 0xD001:VROM_BANK1(0x0400,V);break; + case 0xD002:VROM_BANK1(0x0800,V);break; + case 0xD003:VROM_BANK1(0x0c00,V);break; + case 0xE000:VROM_BANK1(0x1000,V);break; + case 0xE001:VROM_BANK1(0x1400,V);break; + case 0xE002:VROM_BANK1(0x1800,V);break; + case 0xE003:VROM_BANK1(0x1c00,V);break; + case 0xF000:IRQLatch=V;break; + case 0xF001:IRQa=V&2; + vrctemp=V&1; + if(V&2) {IRQCount=IRQLatch;} + //acount=0; + break; + case 0xf002:IRQa=vrctemp;break; + case 0xF003:break; + } +} + +static int CVBC[3]={0,0,0}; +static int32 vcount[2]; + +static void DoSQV1(void) +{ + uint8 amp; + int32 freq; + int V; + int32 start,end; + + start=CVBC[0]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CVBC[0]=end; + + if(VPSG[0x3]&0x80) + { + amp=(VPSG[0]&15)<<4; + if(VPSG[0]&0x80) + { + for(V=start;V>4]+=amp; + } + else + { + unsigned long dcycs; + freq=(((VPSG[0x2]|((VPSG[0x3]&15)<<8))+1)); + inc=(long double)((unsigned long)((FSettings.SndRate OVERSAMPLE)<<12))/((long double)PSG_base/freq); + switch(VPSG[0]&0x70) + { + default: + case 0x00:dcycs=inc>>4;break; + case 0x10:dcycs=inc>>3;break; + case 0x20:dcycs=(inc*3)>>4;break; + case 0x30:dcycs=inc>>2;break; + case 0x40:dcycs=(inc*5)>>4;break; + case 0x50:dcycs=(inc*6)>>4;break; + case 0x60:dcycs=(inc*7)>>4;break; + case 0x70:dcycs=inc>>1;break; + } + for(V=start;V>4]+=amp; + vcount[0]+=0x1000; + if(vcount[0]>=inc) vcount[0]-=inc; + } + } + } +} +static void DoSQV2(void) +{ + uint8 amp; + int32 freq; + int V; + int32 start,end; + + start=CVBC[1]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CVBC[1]=end; + + if(VPSG[0x7]&0x80) + { + amp=(VPSG[4]&15)<<4; + if(VPSG[4]&0x80) + { + for(V=start;V>4]+=amp; + } + else + { + unsigned long dcycs; + freq=(((VPSG[0x6]|((VPSG[0x7]&15)<<8))+1)); + inc=(long double)((unsigned long)((FSettings.SndRate OVERSAMPLE)<<12))/((long double)PSG_base/freq); + switch(VPSG[4]&0x70) + { + default: + case 0x00:dcycs=inc>>4;break; + case 0x10:dcycs=inc>>3;break; + case 0x20:dcycs=(inc*3)>>4;break; + case 0x30:dcycs=inc>>2;break; + case 0x40:dcycs=(inc*5)>>4;break; + case 0x50:dcycs=(inc*6)>>4;break; + case 0x60:dcycs=(inc*7)>>4;break; + case 0x70:dcycs=inc>>1;break; + } + for(V=start;V>4]+=amp; + vcount[1]+=0x1000; + if(vcount[1]>=inc) vcount[1]-=inc; + } + } + } +} + +static void DoSawV(void) +{ + int V; + int32 start,end; + + start=CVBC[2]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CVBC[2]=end; + + if(VPSG2[2]&0x80) + { + static int64 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>3)&0x1f)<<4); + } + Wave[V>>4]+=duff; + } + } +} + +void VRC6Sound(int Count) +{ + int x; + + DoSQV1(); + DoSQV2(); + DoSawV(); + for(x=0;x<3;x++) + CVBC[x]=Count; +} + +static int satype=0; + +void VRC6SoundC(void) +{ + int x; + + if(FSettings.SndRate) + VRC6_ESI(satype); + else + { + for(x=000;x<0x1000;x+=4) + { + SetWriteHandler(0x9000+x,0x9002+x,0); + SetWriteHandler(0xa000+x,0xa002+x,0); + SetWriteHandler(0xb000+x,0xb002+x,0); + } + } +} + +void VRC6_ESI(int t) +{ + int x; + + satype=t; + + GameExpSound.RChange=VRC6SoundC; + GameExpSound.Fill=VRC6Sound; + if(FSettings.SndRate) + for(x=000;x<0x1000;x+=4) + { + uint32 a; + + a=0x9000+x; + SetWriteHandler(a,a,VRC6PSGW90); + SetWriteHandler(a+(1^t),a+(1^t),VRC6PSGW91); + SetWriteHandler(a+(2^t),a+(2^t),VRC6PSGW92); + + a=0xa000+x; + SetWriteHandler(a,a,VRC6PSGWA0); + SetWriteHandler(a+(1^t),a+(1^t),VRC6PSGWA1); + SetWriteHandler(a+(2^t),a+(2^t),VRC6PSGWA2); + + a=0xb000+x; + SetWriteHandler(a,a,VRC6PSGWB0); + SetWriteHandler(a+(1^t),a+(1^t),VRC6PSGWB1); + SetWriteHandler(a+(2^t),a+(2^t),VRC6PSGWB2); + } +} + +void Mapper24_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper24_write); + if(FSettings.SndRate) + VRC6_ESI(0); + GameExpSound.RChange=VRC6SoundC; + MapIRQHook=KonamiIRQHook; + swaparoo=0; +} + +void Mapper26_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper24_write); + if(FSettings.SndRate) + VRC6_ESI(3); + GameExpSound.RChange=VRC6SoundC; + MapIRQHook=KonamiIRQHook; + swaparoo=1; +} + diff --git a/mappers/25.c b/mappers/25.c new file mode 100644 index 0000000..db5ba33 --- /dev/null +++ b/mappers/25.c @@ -0,0 +1,95 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define K4buf mapbyte2 +#define K4IRQ mapbyte1[1] +#define K4sel mapbyte1[0] + +DECLFW(Mapper25_write) +{ + A=(A&0xF003)|((A&0xC)>>2); + + if((A&0xF000)==0xA000) + ROM_BANK8(0xA000,V); + else if(A>=0xB000 && A<=0xEFFF) + { + int x=(A&1)|((A-0xB000)>>11); + + K4buf[x]&=(0xF0)>>((A&2)<<1); + K4buf[x]|=(V&0xF)<<((A&2)<<1); + VROM_BANK1(x<<10,K4buf[x]); + } + else if((A&0xF000)==0x8000) + { + if(K4sel&2) + ROM_BANK8(0xC000,V); + else + ROM_BANK8(0x8000,V); + } + else switch(A) + { + case 0x9000:switch(V&0x3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0x9001:if((K4sel&2)!=(V&2)) + { + uint8 swa; + swa=PRGBankList[0]; + ROM_BANK8(0x8000,PRGBankList[2]); + ROM_BANK8(0xc000,swa); + } + K4sel=V; + break; + case 0xf000:IRQLatch&=0xF0;IRQLatch|=V&0xF;break; + case 0xf002:IRQLatch&=0x0F;IRQLatch|=V<<4;break; + case 0xf001:IRQCount=IRQLatch;IRQa=V&2;K4IRQ=V&1;break; + case 0xf003:IRQa=K4IRQ;break; + } +} + +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + static int count=0; + #define LCYCS 114 + if(IRQa) + { + count+=a; + if(count>=LCYCS) + { + doagainbub:count-=LCYCS;IRQCount++; + if(IRQCount&0x100) {count=0;TriggerIRQ();IRQCount=IRQLatch;} + if(count>=LCYCS) goto doagainbub; + } + } +} + +void Mapper25_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper25_write); + MapIRQHook=KonamiIRQHook; +} + diff --git a/mappers/32.c b/mappers/32.c new file mode 100644 index 0000000..da856b9 --- /dev/null +++ b/mappers/32.c @@ -0,0 +1,54 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define IREMCon mapbyte1[0] + +static DECLFW(Mapper32_write) +{ + switch(A>>12) + { + case 0x8: + mapbyte1[1]=V; + if(IREMCon) {ROM_BANK8(0xc000,V);ROM_BANK8(0x8000,~1);} + else {ROM_BANK8(0x8000,V);ROM_BANK8(0xc000,~1);} + break; + case 0x9:MIRROR_SET2(V&1); + IREMCon=(V>>1)&1; + if(IREMCon) {ROM_BANK8(0xc000,mapbyte1[1]);ROM_BANK8(0x8000,~1);} + else {ROM_BANK8(0x8000,mapbyte1[1]); ROM_BANK8(0xc000,~1);} + MIRROR_SET(V&1); + break; + case 0xa:ROM_BANK8(0xA000,V); + break; + } + + if((A&0xF000)==0xb000) + VROM_BANK1((A&0x7)<<10,V); +} + +void Mapper32_init(void) +{ + ROM_BANK16(0x8000,0); + ROM_BANK16(0xc000,~0); + SetWriteHandler(0x8000,0xffff,Mapper32_write); +} diff --git a/mappers/33.c b/mappers/33.c new file mode 100644 index 0000000..baa2bf9 --- /dev/null +++ b/mappers/33.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static DECLFW(Mapper33_write) +{ + A&=0xF003; + + //printf("$%04x:$%02x, %d\n",A,V,scanline); + if(A>=0xA000 && A<=0xA003) + VROM_BANK1(0x1000+((A&3)<<10),V); + else switch(A){ + case 0x8000:if(!mapbyte1[0]) + MIRROR_SET((V>>6)&1); + ROM_BANK8(0x8000,V); + break; + case 0x8001:ROM_BANK8(0xA000,V); break; + case 0x8002:VROM_BANK2(0x0000,V);break; + case 0x8003:VROM_BANK2(0x0800,V);break; + case 0xc000:IRQLatch=V;break; + case 0xc001:IRQCount=IRQLatch;break; + case 0xc003:IRQa=0;break; + case 0xc002:IRQa=1;break; + case 0xe000:mapbyte1[0]=1;MIRROR_SET((V>>6)&1);break; + } +} + +static void heho(void) +{ + if(IRQa) + { + IRQCount++; + if(IRQCount==0x100) + { + TriggerIRQ(); + IRQa=0; + } + } +} + +void Mapper33_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper33_write); + GameHBIRQHook=heho; +} diff --git a/mappers/40.c b/mappers/40.c new file mode 100644 index 0000000..30f9459 --- /dev/null +++ b/mappers/40.c @@ -0,0 +1,58 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +static DECLFW(Mapper40_write) +{ + switch(A&0xe000) + { + case 0x8000:IRQa=0;IRQCount=0;break; + case 0xa000:IRQa=1;break; + case 0xe000:ROM_BANK8(0xc000,V&7);break; + } +} + +static void FP_FASTAPASS(1) Mapper40IRQ(int a) +{ + if(IRQa) + { + if(IRQCount<4096) + IRQCount+=a; + else + { + IRQa=0; + TriggerIRQ(); + } + } +} + +void Mapper40_init(void) +{ + ROM_BANK8(0x6000,(~0)-1); + ROM_BANK8(0x8000,(~0)-3); + ROM_BANK8(0xa000,(~0)-2); + SetWriteHandler(0x8000,0xffff,Mapper40_write); + SetReadHandler(0x6000,0x7fff,CartBR); + MapIRQHook=Mapper40IRQ; +} + + diff --git a/mappers/41.c b/mappers/41.c new file mode 100644 index 0000000..1139db9 --- /dev/null +++ b/mappers/41.c @@ -0,0 +1,51 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define calreg mapbyte1[0] +#define calchr mapbyte1[1] + +DECLFW(Mapper41_write) +{ + if(A<0x8000) + { + ROM_BANK32(A&7); + MIRROR_SET((A>>5)&1); + calreg=A; + calchr&=0x3; + calchr|=(A>>1)&0xC; + VROM_BANK8(calchr); + } + else if(calreg&0x4) + { + calchr&=0xC; + calchr|=A&3; + VROM_BANK8(calchr); + } +} + +void Mapper41_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper41_write); + SetWriteHandler(0x6000,0x67ff,Mapper41_write); +} diff --git a/mappers/42.c b/mappers/42.c new file mode 100644 index 0000000..2700227 --- /dev/null +++ b/mappers/42.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +static DECLFW(Mapper42_write) +{ + switch(A&0xe003) + { + case 0xe000:mapbyte1[0]=V;ROM_BANK8(0x6000,V&0xF);break; + case 0xe001:MIRROR_SET((V>>3)&1);break; + case 0xe002:IRQa=V&2;if(!IRQa) IRQCount=0;break; + } +} + +static void FP_FASTAPASS(1) Mapper42IRQ(int a) +{ + if(IRQa) + { + if(IRQCount<24576) + IRQCount+=a; + else + { + IRQa=0; + TriggerIRQ(); + } + } +} + +static void Mapper42_StateRestore(int version) +{ + ROM_BANK8(0x6000,mapbyte1[0]&0xF); +} + + +void Mapper42_init(void) +{ + ROM_BANK8(0x6000,0); + ROM_BANK32(~0); + SetWriteHandler(0xe000,0xffff,Mapper42_write); + SetReadHandler(0x6000,0x7fff,CartBR); + MapStateRestore=Mapper42_StateRestore; + MapIRQHook=Mapper42IRQ; +} + diff --git a/mappers/43.c b/mappers/43.c new file mode 100644 index 0000000..2e5f427 --- /dev/null +++ b/mappers/43.c @@ -0,0 +1,63 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper43_write) +{ + uint32 m; + int z; + + if(A&0x400) + onemir(0); + else + MIRROR_SET((A>>13)&1); + m=A&0x1f; + + z=(A>>8)&3; + + switch(CHRmask8[0]) + { + default: + case 0xFF: + if(z&2) + m|=0x20; + break; + case 0x1FF: + m|=z<<5; + break; + } + + if(A&0x800) + { + ROM_BANK16(0x8000,(m<<1)|((A&0x1000)>>12)); + ROM_BANK16(0xC000,(m<<1)|((A&0x1000)>>12)); + } + else + ROM_BANK32(m); +} + +void Mapper43_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper43_write); +} diff --git a/mappers/46.c b/mappers/46.c new file mode 100644 index 0000000..4157f2b --- /dev/null +++ b/mappers/46.c @@ -0,0 +1,48 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +#define A64reg mapbyte1[0] +#define A64wr mapbyte1[1] + +DECLFW(Mapper46_writel) +{ + A64reg=V; + ROM_BANK32((A64wr&1)+((A64reg&0xF)<<1)); + VROM_BANK8(((A64wr>>4)&7)+((A64reg&0xF0)>>1)); +} + +DECLFW(Mapper46_write) +{ + A64wr=V; + ROM_BANK32((V&1)+((A64reg&0xF)<<1)); + VROM_BANK8(((V>>4)&7)+((A64reg&0xF0)>>1)); +} + +void Mapper46_init(void) +{ + MIRROR_SET(0); + ROM_BANK32(0); + SetWriteHandler(0x8000,0xffff,Mapper46_write); + SetWriteHandler(0x6000,0x7fff,Mapper46_writel); +} diff --git a/mappers/51.c b/mappers/51.c new file mode 100644 index 0000000..c6a33f3 --- /dev/null +++ b/mappers/51.c @@ -0,0 +1,66 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define mode mapbyte1[0] +#define page mapbyte1[1] + +static uint32 Get8K(uint32 A) +{ + uint32 bank; + + bank=(page<<2)|((A>>13)&1); + + if(A&0x4000 && !(mode&1)) bank|=0xC; + if(!(A&0x8000)) bank|=0x20; + if(mode==2) bank|=2; + else bank|=(A>>13)&2; + return(bank); +} + +static void Synco(void) +{ + uint32 x; + if(mapbyte1[0]<=2) + MIRROR_SET2(1); + else + MIRROR_SET2(0); + for(x=0x6000;x<0x10000;x+=8192) + ROM_BANK8(x,Get8K(x)); +} + +static DECLFW(Write) +{ + if(A&0x8000) mapbyte1[1]=V&0xF; + else mapbyte1[0]=(mapbyte1[0]&2)|((V>>1)&1); + + if(A&0x4000) mapbyte1[0]=(mapbyte1[0]&1)|((V>>3)&2); + Synco(); +} + +void Mapper51_init(void) +{ + SetWriteHandler(0x6000,0xFFFF,Write); + SetReadHandler(0x6000,0xFFFF,CartBR); + mapbyte1[0]=1; + mapbyte1[1]=0; + Synco(); +} diff --git a/mappers/6.c b/mappers/6.c new file mode 100644 index 0000000..015aa9c --- /dev/null +++ b/mappers/6.c @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define FVRAM_BANK8(A,V) {VPage[0]=VPage[1]=VPage[2]=VPage[3]=VPage[4]=VPage[5]=VPage[6]=VPage[7]=V?&MapperExRAM[(V)<<13]-(A):&CHRRAM[(V)<<13]-(A);CHRBankList[0]=((V)<<3);CHRBankList[1]=((V)<<3)+1;CHRBankList[2]=((V)<<3)+2;CHRBankList[3]=((V)<<3)+3;CHRBankList[4]=((V)<<3)+4;CHRBankList[5]=((V)<<3)+5;CHRBankList[6]=((V)<<3)+6;CHRBankList[7]=((V)<<3)+7;PPUCHRRAM=0xFF;} + +static void FP_FASTAPASS(1) FFEIRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + if(IRQCount>=0x10000) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0; + } + } +} + +DECLFW(Mapper6_write) +{ + if(A<0x8000) + { + switch(A){ + case 0x42FF:MIRROR_SET((V>>4)&1);break; + case 0x42FE:onemir((V>>3)&2);break; + case 0x4501:IRQa=0;break; + case 0x4502:IRQCount&=0xFF00;IRQCount|=V;break; + case 0x4503:IRQCount&=0xFF;IRQCount|=V<<8;IRQa=1;break; + } + } else { + ROM_BANK16(0x8000,V>>2); + FVRAM_BANK8(0x0000,V&3); + } +} +void Mapper6_StateRestore(int version) +{ + int x; + for(x=0;x<8;x++) + if(PPUCHRRAM&(1<7) + VPage[x]=&MapperExRAM[(CHRBankList[x]&31)*0x400]-(x*0x400); + else VPage[x]=&CHRRAM[(CHRBankList[x]&7)*0x400]-(x*0x400); + } +} +void Mapper6_init(void) +{ +MapIRQHook=FFEIRQHook; +ROM_BANK16(0xc000,7); + +SetWriteHandler(0x4020,0x5fff,Mapper6_write); +SetWriteHandler(0x8000,0xffff,Mapper6_write); +MapStateRestore=Mapper6_StateRestore; +} + diff --git a/mappers/64.c b/mappers/64.c new file mode 100644 index 0000000..96dbac2 --- /dev/null +++ b/mappers/64.c @@ -0,0 +1,150 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define cmd mapbyte1[0] +#define mir mapbyte1[1] +#define rmode mapbyte1[2] +#define regsl mapbyte2 +#define regsh mapbyte3 + +static void RAMBO1_hb(void) +{ + rmode=0; + if(IRQCount>=0) + { + IRQCount--; + if(IRQCount<0) + { + if(IRQa) + { +// printf("IRQ: %d\n",scanline); + rmode = 1; + X6502_IRQBegin(FCEU_IQEXT); + } + } + } +} + +static void Synco(void) +{ + int x; + + if(cmd&0x20) + { + setchr1(0x0000,regsl[0]); + setchr1(0x0800,regsl[1]); + setchr1(0x0400,regsh[0]); + setchr1(0x0c00,regsh[1]); + } + else + { + setchr2(0x0000,regsl[0]>>1); + setchr2(0x0800,regsl[1]>>1); + } + + for(x=0;x<4;x++) + setchr1(0x1000+x*0x400,regsl[2+x]); + + setprg8(0x8000,regsl[6]); + setprg8(0xA000,regsl[7]); + + setprg8(0xC000,regsh[7]); +} + + +static DECLFW(RAMBO1_write) +{ + //if(A>=0xC000 && A<=0xFFFF) printf("$%04x:$%02x, %d, %d\n",A,V,scanline,timestamp); + switch(A&0xF001) + { + case 0xa000:mir=V&1; + setmirror(mir^1); + break; + case 0x8000:cmd = V; + break; + case 0x8001: + if((cmd&15)<8) + regsl[cmd&7]=V; + else + regsh[cmd&7]=V; + Synco(); + break; + case 0xc000:IRQLatch=V; + if(rmode==1) + { + IRQCount=IRQLatch; + } + break; + case 0xc001:rmode=1; + IRQCount=IRQLatch; + 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 RAMBO1_Restore(int version) +{ + if(version<74) + { + int x; + + x=mapbyte1[1]; // was MMC3_cmd + cmd=x; + + regsl[0]=CHRBankList[0]; + regsl[1]=CHRBankList[2]; + regsh[0]=CHRBankList[1]; + regsh[1]=CHRBankList[3]; + + for(x=0;x<4;x++) + regsl[2+x]=CHRBankList[4+x]; + + regsl[6]=PRGBankList[0]; + regsl[7]=PRGBankList[1]; + regsh[7]=PRGBankList[2]; + mir=Mirroring^1; + } + Synco(); + setmirror(mir^1); +} + +void Mapper64_init(void) +{ + int x; + + for(x=0;x<8;x++) + regsl[x]=regsh[x]=~0; + cmd=0; + mir=0; + setmirror(1); + Synco(); + GameHBIRQHook=RAMBO1_hb; + GameStateRestore=RAMBO1_Restore; + SetWriteHandler(0x8000,0xffff,RAMBO1_write); +} diff --git a/mappers/65.c b/mappers/65.c new file mode 100644 index 0000000..b4051e8 --- /dev/null +++ b/mappers/65.c @@ -0,0 +1,68 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +void FP_FASTAPASS(1) IREMIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0-4) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0xFFFF; + } + } +} + +DECLFW(Mapper65_write) +{ +switch(A) +{ +case 0x8000:ROM_BANK8(0x8000,V);break; +case 0x9000:MIRROR_SET2((V>>6)&1);break; +case 0x9003:IRQa=V&0x80;break; +case 0x9004:IRQCount=IRQLatch;break; +case 0x9005: IRQLatch&=0x00FF; + IRQLatch|=V<<8; + break; +case 0x9006: IRQLatch&=0xFF00;IRQLatch|=V; + break; +case 0xB000:VROM_BANK1(0x0000,V);break; +case 0xB001:VROM_BANK1(0x0400,V);break; +case 0xB002:VROM_BANK1(0x0800,V);break; +case 0xB003:VROM_BANK1(0x0C00,V);break; +case 0xB004:VROM_BANK1(0x1000,V);break; +case 0xB005:VROM_BANK1(0x1400,V);break; +case 0xB006:VROM_BANK1(0x1800,V);break; +case 0xB007:VROM_BANK1(0x1C00,V);break; +case 0xa000:ROM_BANK8(0xA000,V);break; +case 0xC000:ROM_BANK8(0xC000,V);break; + } +} + +void Mapper65_init(void) +{ + MapIRQHook=IREMIRQHook; + SetWriteHandler(0x8000,0xffff,Mapper65_write); +} diff --git a/mappers/67.c b/mappers/67.c new file mode 100644 index 0000000..96c695b --- /dev/null +++ b/mappers/67.c @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define suntoggle mapbyte1[0] + +DECLFW(Mapper67_write) +{ + A&=0xF800; + if((A&0x800) && A<=0xb800) + { + VROM_BANK2((A-0x8800)>>1,V); + } + else switch(A) + { + case 0xc800: + case 0xc000:if(!suntoggle) + {IRQCount&=0xFF;IRQCount|=V<<8;} + else{IRQCount&=0xFF00;IRQCount|=V;} + suntoggle^=1; + break; + case 0xd800:suntoggle=0;IRQa=V&0x10;break; + + case 0xe800:switch(V&3) + { + case 0:MIRROR_SET2(1);break; + case 1:MIRROR_SET2(0);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0xf800:ROM_BANK16(0x8000,V);break; + } +} +static void FP_FASTAPASS(1) SunIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0) + {TriggerIRQ();IRQa=0;IRQCount=0xFFFF;} + } +} +void Mapper67_init(void) +{ +SetWriteHandler(0x8000,0xffff,Mapper67_write); +MapIRQHook=SunIRQHook; +} diff --git a/mappers/68.c b/mappers/68.c new file mode 100644 index 0000000..98cb0d8 --- /dev/null +++ b/mappers/68.c @@ -0,0 +1,104 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static void Fixerit(void) +{ + switch(mapbyte2[0]&3) + { + case 0:vnapage[0]=vnapage[2]=VROM+(((mapbyte1[0]|128)&CHRmask1[0])<<10); + vnapage[1]=vnapage[3]=VROM+(((mapbyte1[1]|128)&CHRmask1[0])<<10); + break; + case 1:vnapage[0]=vnapage[1]=VROM+(((mapbyte1[0]|128)&CHRmask1[0])<<10); + vnapage[2]=vnapage[3]=VROM+(((mapbyte1[1]|128)&CHRmask1[0])<<10); + break; + case 2:vnapage[0]=vnapage[1]=vnapage[2]=vnapage[3]=VROM+(((mapbyte1[0]|128)&CHRmask1[0])<<10); + break; + case 3:vnapage[0]=vnapage[1]=vnapage[2]=vnapage[3]=VROM+(((mapbyte1[1]|128)&CHRmask1[0])<<10); + break; + } +} + +DECLFW(Mapper68_write) +{ + A&=0xF000; + + if(A>=0x8000 && A<=0xB000) + { + VROM_BANK2((A-0x8000)>>1,V); + } + else switch(A) + { + case 0xc000:mapbyte1[0]=V; + if(VROM_size && mapbyte2[0]&0x10) + Fixerit(); + break; + + case 0xd000:mapbyte1[1]=V; + if(VROM_size && mapbyte2[0]&0x10) + Fixerit(); + break; + + case 0xe000: mapbyte2[0]=V; + if(!(V&0x10)) + { + switch(V&3) + { + case 0:MIRROR_SET2(1);break; + case 1:MIRROR_SET2(0);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + } + else if(VROM_size) + { + Fixerit(); + PPUNTARAM=0; + } + break; + case 0xf000: ROM_BANK16(0x8000,V);break; + } +} + +static void Mapper68_StateRestore(int version) +{ + if(!(mapbyte2[0]&0x10)) + { + switch(mapbyte2[0]&3) + { + case 0:MIRROR_SET(0);break; + case 1:MIRROR_SET(1);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + } + else if(VROM_size) + { + Fixerit(); + PPUNTARAM=0; + } +} + +void Mapper68_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper68_write); + MapStateRestore=Mapper68_StateRestore; +} diff --git a/mappers/69.c b/mappers/69.c new file mode 100644 index 0000000..ec6336f --- /dev/null +++ b/mappers/69.c @@ -0,0 +1,268 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" +static void AYSound(int Count); +static void DoAYSQ(int x); +static void DoAYNoise(void); + +#define sunselect mapbyte1[0] +#define sungah mapbyte1[1] +#define sunindex mapbyte1[2] + +static uint16 znreg; +static int32 inc; + +DECLFW(SUN5BWRAM) +{ + if((sungah&0xC0)==0xC0) + (WRAM-0x6000)[A]=V; +} + +DECLFR(SUN5AWRAM) +{ + if((sungah&0xC0)==0x40) + return X.DB; + return CartBR(A); +} + +DECLFW(Mapper69_SWL) +{ + sunindex=V%14; +} +DECLFW(Mapper69_SWH) +{ + GameExpSound.Fill=AYSound; + switch(sunindex) + { + case 0: + case 1: + case 8:DoAYSQ(0);break; + case 2: + case 3: + case 9:DoAYSQ(1);break; + case 4: + case 5: + case 10:DoAYSQ(2);break; + case 6:DoAYNoise();znreg=0xFFFF;break; + case 7:DoAYNoise(); + DoAYSQ(0); + DoAYSQ(1); + DoAYSQ(2);break; + } + MapperExRAM[sunindex]=V; +} + +DECLFW(Mapper69_write) +{ + switch(A&0xE000) + { + case 0x8000:sunselect=V;break; + case 0xa000: + sunselect&=0xF; + if(sunselect<=7) + VROM_BANK1(sunselect<<10,V); + else + switch(sunselect&0x0f) + { + case 8: + sungah=V; + if(V&0x40) + { + if(V&0x80) // Select WRAM + setprg8r(0x10,0x6000,0); + } + else + setprg8(0x6000,V); + break; + case 9:ROM_BANK8(0x8000,V);break; + case 0xa:ROM_BANK8(0xa000,V);break; + case 0xb:ROM_BANK8(0xc000,V);break; + case 0xc: + switch(V&3) + { + case 0:MIRROR_SET2(1);break; + case 1:MIRROR_SET2(0);break; + case 2:onemir(0);break; + case 3:onemir(1);break; + } + break; + case 0xd:IRQa=V;break; + case 0xe:IRQCount&=0xFF00;IRQCount|=V;break; + case 0xf:IRQCount&=0x00FF;IRQCount|=V<<8;break; + } + break; + } +} + +static int32 vcount[4]; +static int CAYBC[4]={0,0,0,0}; +static void DoAYSQ(int x) +{ + int V; + uint32 freq; + unsigned char amp; + int32 start,end; + + start=CAYBC[x]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CAYBC[x]=end; + + if(!(MapperExRAM[0x7]&(1<>1)) + Wave[V>>4]+=amp; + vcoo+=0x1000; + if(vcoo>=inc) vcoo-=inc; + } + vcount[x]=vcoo; + } +} +static void DoAYNoise(void) +{ + int V; + uint32 freq; + unsigned char amp; + int32 start,end; + + start=CAYBC[3]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + CAYBC[3]=end; + + amp=0; + for(V=0;V<3;V++) + { + if(!(MapperExRAM[0x7]&(8<44100) + inc=((freq<<11)/(FSettings.SndRate OVERSAMPLE))<<4; + else + inc=(freq<<15)/(FSettings.SndRate OVERSAMPLE); + + for(V=start;V=32768) + { + unsigned char feedback; + mixer=0; + if(znreg&1) mixer+=amp; + feedback=((znreg>>13)&1)^((znreg>>14)&1); + znreg=(znreg<<1)+(feedback); + vcount[3]-=32768; + } + Wave[V>>4]+=mixer; + vcount[3]+=inc; + } + } + + +} +static void AYSound(int Count) +{ + int x; + DoAYSQ(0); + DoAYSQ(1); + DoAYSQ(2); + DoAYNoise(); + for(x=0;x<4;x++) + CAYBC[x]=Count; +} + +static void FP_FASTAPASS(1) SunIRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<=0) + {TriggerIRQ();IRQa=0;IRQCount=0xFFFF;} + } +} + +void Mapper69_StateRestore(int version) +{ + if(version>=19) + { + if(mapbyte1[1]&0x40) + { + if(mapbyte1[1]&0x80) // Select WRAM + setprg8r(0x10,0x6000,0); + } + else + setprg8(0x6000,mapbyte1[1]); + } + else + mapbyte1[1]=0xC0; +} + +static void M69SC(void) +{ + if(FSettings.SndRate) + Mapper69_ESI(); + else + SetWriteHandler(0xc000,0xffff,(writefunc)0); +} + +void Mapper69_ESI(void) +{ + GameExpSound.RChange=M69SC; + if(FSettings.SndRate) + { + SetWriteHandler(0xc000,0xdfff,Mapper69_SWL); + SetWriteHandler(0xe000,0xffff,Mapper69_SWH); + } +} + +void Mapper69_init(void) +{ + SetupCartPRGMapping(0x10,WRAM,8192,1); + + SetWriteHandler(0x8000,0xbfff,Mapper69_write); + SetWriteHandler(0x6000,0x7fff,SUN5BWRAM); + SetReadHandler(0x6000,0x7fff,SUN5AWRAM); + Mapper69_ESI(); + MapIRQHook=SunIRQHook; + MapStateRestore=Mapper69_StateRestore; + znreg=0; +} + diff --git a/mappers/71.c b/mappers/71.c new file mode 100644 index 0000000..7643978 --- /dev/null +++ b/mappers/71.c @@ -0,0 +1,41 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper71_write) +{ +switch(A&0xF000) + { + case 0xF000: + case 0xE000: + case 0xD000: + case 0xC000:ROM_BANK16(0x8000,V);break; + case 0x9000:onemir((V>>3)&2);break; + } +} + +void Mapper71_init(void) +{ +SetWriteHandler(0x4020,0xffff,Mapper71_write); +} + diff --git a/mappers/72.c b/mappers/72.c new file mode 100644 index 0000000..dcc5828 --- /dev/null +++ b/mappers/72.c @@ -0,0 +1,37 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +DECLFW(Mapper72_write) +{ + mapbyte1[0]=V; + if(V&0x80) + ROM_BANK16(0x8000,V&0xF); + if(V&0x40) + VROM_BANK8(V&0xF); +} + +void Mapper72_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper72_write); +} + diff --git a/mappers/73.c b/mappers/73.c new file mode 100644 index 0000000..ca230f8 --- /dev/null +++ b/mappers/73.c @@ -0,0 +1,57 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper73_write) +{ +switch(A&0xF000) + { + case 0x8000:IRQCount&=0xFFF0;IRQCount|=(V&0xF);break; + case 0x9000:IRQCount&=0xFF0F;IRQCount|=(V&0xF)<<4;break; + case 0xa000:IRQCount&=0xF0FF;IRQCount|=(V&0xF)<<8;break; + case 0xb000:IRQCount&=0x0FFF;IRQCount|=(V&0xF)<<12;break; + case 0xc000:IRQa=V&2;break; + case 0xf000:ROM_BANK16(0x8000,V);break; + } +} + +static void FP_FASTAPASS(1) Mapper73IRQHook(int a) +{ + if(IRQa) + { + IRQCount+=a; + if(IRQCount>=0xFFFF) + { + IRQCount&=0xFFFF; + IRQa=0; + TriggerIRQ(); + } + } +} + +void Mapper73_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper73_write); + MapIRQHook=Mapper73IRQHook; +} + diff --git a/mappers/75.c b/mappers/75.c new file mode 100644 index 0000000..0bd2711 --- /dev/null +++ b/mappers/75.c @@ -0,0 +1,47 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define map75sel mapbyte1[0] +#define map75ar mapbyte2 + +DECLFW(Mapper75_write) +{ +switch(A&0xF000) + { + case 0x8000:ROM_BANK8(0x8000,V);break; + case 0x9000: + VROM_BANK4(0x0000,map75ar[0]|((V&2)<<3)); + VROM_BANK4(0x1000,map75ar[1]|((V&4)<<2)); + map75sel=V;MIRROR_SET(V&1);break; + case 0xa000:ROM_BANK8(0xa000,V);break; + case 0xc000:ROM_BANK8(0xc000,V);break; + case 0xe000:V&=0xF;map75ar[0]=V;V|=(map75sel&2)<<3;VROM_BANK4(0x0000,V);break; + case 0xf000:V&=0xF;map75ar[1]=V;V|=(map75sel&4)<<2;VROM_BANK4(0x1000,V);break; + } +} + +void Mapper75_init(void) +{ +SetWriteHandler(0x8000,0xffff,Mapper75_write); +} + diff --git a/mappers/76.c b/mappers/76.c new file mode 100644 index 0000000..d901bb7 --- /dev/null +++ b/mappers/76.c @@ -0,0 +1,55 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper76_write) +{ + switch(A&0xE001){ + case 0x8000: + MMC3_cmd = V; + break; + case 0x8001: + switch(MMC3_cmd&0x07){ + case 2: VROM_BANK2(0x000,V);break; + case 3: VROM_BANK2(0x800,V);break; + case 4: VROM_BANK2(0x1000,V);break; + case 5: VROM_BANK2(0x1800,V);break; + case 6: + if (MMC3_cmd&0x40) ROM_BANK8(0xC000,V); + else ROM_BANK8(0x8000,V); + break; + case 7: ROM_BANK8(0xA000,V); + break; + } + break; + case 0xA000: + MIRROR_SET(V&1); + break; + } +} + +void Mapper76_init(void) +{ +SetWriteHandler(0x8000,0xffff,Mapper76_write); +} + diff --git a/mappers/77.c b/mappers/77.c new file mode 100644 index 0000000..e9d0015 --- /dev/null +++ b/mappers/77.c @@ -0,0 +1,54 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +/* Original code provided by LULU */ + +static DECLFW(Mapper77_write) +{ + mapbyte1[0]=V; + ROM_BANK32(V&0x7); + VROM_BANK2(0x0000, (V&0xf0)>>4); +} + +static void Mapper77_StateRestore(int version) +{ + int x; + + if(version>=72) + { + ROM_BANK32(mapbyte1[0]&0x7); + VROM_BANK2(0x0000, (mapbyte1[0]&0xf0)>>4); + } + for(x=2;x<8;x++) + VRAM_BANK1(x*0x400,x); +} + +void Mapper77_init(void) +{ + int x; + + ROM_BANK32(0); + for(x=2;x<8;x++) + VRAM_BANK1(x*0x400,x); + SetWriteHandler(0x6000,0xffff,Mapper77_write); + MapStateRestore=Mapper77_StateRestore; +} diff --git a/mappers/79.c b/mappers/79.c new file mode 100644 index 0000000..5e79efe --- /dev/null +++ b/mappers/79.c @@ -0,0 +1,40 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper79_write) +{ + if(A<0x8000 && ((A^0x4100)==0)) + { + ROM_BANK32((V>>3)&1); + } + VROM_BANK8(V); +} + +void Mapper79_init(void) +{ + ROM_BANK32(~0); + SetWriteHandler(0x8000,0xffff,Mapper79_write); + SetWriteHandler(0x4020,0x5fff,Mapper79_write); +} + diff --git a/mappers/8.c b/mappers/8.c new file mode 100644 index 0000000..1a68bf8 --- /dev/null +++ b/mappers/8.c @@ -0,0 +1,38 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper8_write) +{ + ROM_BANK16(0x8000,V>>3); + VROM_BANK8(V&7); +} + +void Mapper8_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper8_write); +} + diff --git a/mappers/80.c b/mappers/80.c new file mode 100644 index 0000000..c824a12 --- /dev/null +++ b/mappers/80.c @@ -0,0 +1,49 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper80_write) +{ +switch(A) + { + case 0x7ef0: VROM_BANK1(0x000,V);VROM_BANK1(0x400,(V+1));break; + case 0x7ef1: VROM_BANK1(0x800,V);VROM_BANK1(0xC00,(V+1));break; + + case 0x7ef2: VROM_BANK1(0x1000,V);break; + case 0x7ef3: VROM_BANK1(0x1400,V);break; + case 0x7ef4: VROM_BANK1(0x1800,V);break; + case 0x7ef5: VROM_BANK1(0x1c00,V);break; + case 0x7efa: + case 0x7efb: ROM_BANK8(0x8000,V);break; + case 0x7efd: + case 0x7efc: ROM_BANK8(0xA000,V);break; + case 0x7efe: + case 0x7eff: ROM_BANK8(0xC000,V);break; + } +} + +void Mapper80_init(void) +{ +SetWriteHandler(0x4020,0x7fff,Mapper80_write); +} + diff --git a/mappers/82.c b/mappers/82.c new file mode 100644 index 0000000..b6a1cb2 --- /dev/null +++ b/mappers/82.c @@ -0,0 +1,62 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define ctrl mapbyte1[6] + +static void DoCHR(void) +{ + int x; + + for(x=0;x<2;x++) + VROM_BANK2((x<<11)|((ctrl&2)<<11),mapbyte1[x]>>1); + for(x=0;x<4;x++) + VROM_BANK1((x<<10) | (((ctrl&2)^2)<<11),mapbyte1[2+x]); +} + +static DECLFW(Mapper82_write) +{ + if(A<=0x7EF5) + { + mapbyte1[A&7]=V; + DoCHR(); + } + else + switch(A) + { + case 0x7ef6:ctrl=V&3; + MIRROR_SET2(V&1); + DoCHR(); + break; + case 0x7efa:V>>=2;mapbyte2[0]=V;ROM_BANK8(0x8000,V);break; + case 0x7efb:V>>=2;mapbyte2[1]=V;ROM_BANK8(0xa000,V);break; + case 0x7efc:V>>=2;mapbyte2[2]=V;ROM_BANK8(0xc000,V);break; + } +} + +void Mapper82_init(void) +{ + ROM_BANK8(0xE000,~0); + + /* external WRAM might end at $73FF */ + SetWriteHandler(0x7ef0,0x7efc,Mapper82_write); +} + diff --git a/mappers/83.c b/mappers/83.c new file mode 100644 index 0000000..6bcca19 --- /dev/null +++ b/mappers/83.c @@ -0,0 +1,113 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +void FP_FASTAPASS(1) m83IRQHook(int a) +{ + if(IRQa) + { + IRQCount-=a; + if(IRQCount<0) + { + TriggerIRQ(); + IRQa=0; + IRQCount=0xFFFF; + } + } +} + +static DECLFW(wrlow) +{ + mapbyte4[A&3]=V; +} + +static DECLFR(rdlow) +{ + return mapbyte4[A&3]; +} + +static void m83prg(void) +{ + ROM_BANK16(0x8000,mapbyte1[0]&0x3F); + ROM_BANK16(0xC000,(mapbyte1[0]&0x30)|0xF); +} + +static void m83chr(void) +{ + int x; + for(x=0;x<8;x++) + VROM_BANK1(x*0x400,mapbyte2[x]|((mapbyte1[0]&0x30)<<4)); +} + +static DECLFW(Mapper83_write) +{ + //printf("$%04x:$%02x\n",A,V); + switch(A) + { + case 0x8000: + case 0xB000: + case 0xB0FF: + case 0xB1FF: + { + mapbyte1[0]=V; + m83prg(); + m83chr(); + } + break; + case 0x8100: + switch(V&0x3) + { + case 0x00:MIRROR_SET2(1);break; + case 0x01:MIRROR_SET2(0);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + case 0x8200:IRQCount&=0xFF00;IRQCount|=V;break; + case 0x8201:IRQa=1;IRQCount&=0xFF;IRQCount|=V<<8;break; + //case 0x8300:ROM_BANK8(0x8000,V);break; + //case 0x8301:ROM_BANK8(0xA000,V);break; + //case 0x8302:ROM_BANK8(0xC000,V);break; + case 0x8310:mapbyte2[0]=V;m83chr();break; + case 0x8311:mapbyte2[1]=V;m83chr();break; + case 0x8312:mapbyte2[2]=V;m83chr();break; + case 0x8313:mapbyte2[3]=V;m83chr();break; + case 0x8314:mapbyte2[4]=V;m83chr();break; + case 0x8315:mapbyte2[5]=V;m83chr();break; + case 0x8316:mapbyte2[6]=V;m83chr();break; + case 0x8317:mapbyte2[7]=V;m83chr();break; + } +// printf("$%04x:$%02x, $%04x\n",A,V,X.PC.W); + +} + +void Mapper83_init(void) +{ + + ROM_BANK8(0xc000,0x1e); + ROM_BANK8(0xe000,0x1f); + + MapIRQHook=m83IRQHook; + + SetReadHandler(0x5100,0x5103,rdlow); + SetWriteHandler(0x5100,0x5103,wrlow); + SetWriteHandler(0x8000,0xffff,Mapper83_write); +} diff --git a/mappers/85.c b/mappers/85.c new file mode 100644 index 0000000..0d45898 --- /dev/null +++ b/mappers/85.c @@ -0,0 +1,140 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define vrctemp mapbyte1[0] +#define indox mapbyte1[1] + +static int acount=0; + +void KillOPL(void); +void UpdateOPL(int Count); +void vrc7translate(uint8 A, uint8 V); +void LoadOPL(void); +extern uint8 VRC7Instrument[16][8]; +extern uint8 VRC7Chan[3][6]; + +static INLINE void DaMirror(int V) +{ + int salpo[4]={MI_V,MI_H,MI_0,MI_1}; + setmirror(salpo[V&3]); +} + +DECLFW(Mapper85_write) +{ + A|=(A&8)<<1; + + if(A>=0xa000 && A<=0xDFFF) + { + A&=0xF010; + { + int x=((A>>4)&1)|((A-0xA000)>>11); + mapbyte3[x]=V; + setchr1(x<<10,V); + } + } + else if(A==0x9030) + { + if(FSettings.SndRate) + vrc7translate(indox,V); + GameExpSound.Fill=UpdateOPL; + } + else switch(A&0xF010) + { + case 0x8000:mapbyte2[0]=V;setprg8(0x8000,V);break; + case 0x8010:mapbyte2[1]=V;setprg8(0xa000,V);break; + case 0x9000:mapbyte2[2]=V;setprg8(0xc000,V);break; + case 0x9010:indox=V;break; + case 0xe000:mapbyte2[3]=V;DaMirror(V);break; + case 0xE010:IRQLatch=V; + break; + case 0xF000:IRQa=V&2; + vrctemp=V&1; + if(V&2) {IRQCount=IRQLatch;acount=0;} + break; + case 0xf010:if(vrctemp) IRQa=1; + else IRQa=0; + break; + } +} + +static void FP_FASTAPASS(1) KonamiIRQHook(int a) +{ + if(IRQa) + { + acount+=(a<<1)+a; + if(acount>=339) + { + doagainbub:acount-=339;IRQCount++; + if(IRQCount&0x100) {TriggerIRQ();IRQCount=IRQLatch;} + if(acount>=339) goto doagainbub; + } + } +} + +void Mapper85_StateRestore(int version) +{ + int x; + + if(version<72) + { + for(x=0;x<8;x++) + mapbyte3[x]=CHRBankList[x]; + for(x=0;x<3;x++) + mapbyte2[x]=PRGBankList[x]; + mapbyte2[3]=(Mirroring<0x10)?Mirroring:Mirroring-0xE; + } + + for(x=0;x<8;x++) + setchr1(x*0x400,mapbyte3[x]); + for(x=0;x<3;x++) + setprg8(0x8000+x*8192,mapbyte2[x]); + DaMirror(mapbyte2[3]); + LoadOPL(); +} + +static void M85SC(void) +{ + KillOPL(); +} + +void VRC7_ESI(void) +{ + if(FSettings.SndRate) + { + SetWriteHandler(0x9010,0x901F,Mapper85_write); + SetWriteHandler(0x9030,0x903F,Mapper85_write); + } + GameExpSound.RChange=M85SC; +} + +void Mapper85_init(void) +{ + MapIRQHook=KonamiIRQHook; + SetWriteHandler(0x8000,0xffff,Mapper85_write); + GameStateRestore=Mapper85_StateRestore; + VRC7_ESI(); + if(!VROM_size) + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(VRC7Instrument, 16, 0, "VC7I"); + AddExState(VRC7Chan, sizeof(VRC7Chan), 0, "V7CH"); + +} diff --git a/mappers/86.c b/mappers/86.c new file mode 100644 index 0000000..5bcefcd --- /dev/null +++ b/mappers/86.c @@ -0,0 +1,32 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +DECLFW(Mapper86_write) +{ + VROM_BANK8((V&3)|((V>>4)&4)); + ROM_BANK32((V>>4)&3); +} + +void Mapper86_init(void) +{ + SetWriteHandler(0x6000,0x6000,Mapper86_write); +} diff --git a/mappers/88.c b/mappers/88.c new file mode 100644 index 0000000..d6a7f86 --- /dev/null +++ b/mappers/88.c @@ -0,0 +1,56 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static DECLFW(Mapper88_write) +{ + //if(A>=0x8002 || A<0x8000) + //if(A==0xc000) + // printf("$%04x:$%02x\n",A,V); + switch(A) //&0xc001) + { + //case 0xc000: + //MIRROR_SET((V&0x40)>>6); + //onemir((V&0x40)>>6); + //break; + case 0x8000:mapbyte1[0]=V;break; + case 0x8001: + switch(mapbyte1[0]&7) + { + case 0:VROM_BANK2(0,V>>1);break; + case 1:VROM_BANK2(0x800,V>>1);break; + case 2:VROM_BANK1(0x1000,V|0x40);break; + case 3:VROM_BANK1(0x1400,V|0x40);break; + case 4:VROM_BANK1(0x1800,V|0x40);break; + case 5:VROM_BANK1(0x1c00,V|0x40);break; + case 6:ROM_BANK8(0x8000,V);break; + case 7:ROM_BANK8(0xA000,V);break; + } + break; + + } +} + +void Mapper88_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper88_write); +} + diff --git a/mappers/89.c b/mappers/89.c new file mode 100644 index 0000000..696e698 --- /dev/null +++ b/mappers/89.c @@ -0,0 +1,34 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +DECLFW(Mapper89_write) +{ + VROM_BANK8((V&7)|((V>>4)&8)); + ROM_BANK16(0x8000,(V>>4)&7); + onemir((V>>3)&1); +} + +void Mapper89_init(void) +{ + Mirroring=0; + SetWriteHandler(0x8000,0xffff,Mapper89_write); +} diff --git a/mappers/90.c b/mappers/90.c new file mode 100644 index 0000000..97405dc --- /dev/null +++ b/mappers/90.c @@ -0,0 +1,154 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define tkcom1 mapbyte1[1] +#define tkcom2 mapbyte1[2] + +#define prgb mapbyte2 +#define unkl (mapbyte2+4) +#define chrlow mapbyte3 +#define chrhigh mapbyte4 + +static uint8 tekker=0x80; + +static DECLFR(tekread) +{ + return tekker; +} + +static void tekprom(void) +{ + switch(tkcom1&3) + { + case 1: // 16 KB + ROM_BANK16(0x8000,prgb[0]); + ROM_BANK16(0xC000,prgb[2]); + break; + + case 2: //2 = 8 KB ?? + case 3: + ROM_BANK8(0x8000,prgb[0]); + ROM_BANK8(0xa000,prgb[1]); + ROM_BANK8(0xc000,prgb[2]); + ROM_BANK8(0xe000,prgb[3]); + break; + } +} + +static void tekvrom(void) +{ + int x; + switch(tkcom1&0x18) + { + case 0x00: // 8KB + VROM_BANK8(chrlow[0]|(chrhigh[0]<<8)); + break; + case 0x08: // 4KB + for(x=0;x<8;x+=4) + VROM_BANK4(x<<10,chrlow[x]|(chrhigh[x]<<8)); + break; + case 0x10: // 2KB + for(x=0;x<8;x+=2) + VROM_BANK2(x<<10,chrlow[x]|(chrhigh[x]<<8)); + break; + case 0x18: // 1KB + for(x=0;x<8;x++) + VROM_BANK1(x<<10,chrlow[x]|(chrhigh[x]<<8)); + break; + } +} + +static DECLFW(Mapper90_write) +{ + A&=0xF007; + + if(A>=0x8000 && A<=0x8003) + { + prgb[A&3]=V; + tekprom(); + } + else if(A>=0x9000 && A<=0x9007) + { + chrlow[A&7]=V; + tekvrom(); + } + else if(A>=0xa000 && A<=0xa007) + { + chrhigh[A&7]=V; + tekvrom(); + } + else if(A>=0xb000 && A<=0xb003) + { + unkl[A&3]=V; + } + else switch(A) + { + case 0xc004: + case 0xc000:IRQLatch=V;break; + + case 0xc005: + case 0xc001:X6502_IRQEnd(FCEU_IQEXT); + IRQCount=V;break; + case 0xc006: + case 0xc002:X6502_IRQEnd(FCEU_IQEXT); + IRQa=0; + IRQCount=IRQLatch; + break; + case 0xc007: + case 0xc003:IRQa=1;break; + + case 0xd000:tkcom1=V;break; + case 0xd001:switch(V&3){ + case 0x00:MIRROR_SET(0);break; + case 0x01:MIRROR_SET(1);break; + case 0x02:onemir(0);break; + case 0x03:onemir(1);break; + } + break; + break; + } +} + +static void Mapper90_hb(void) +{ + if(IRQa) + { + if(IRQCount) + { + IRQCount--; + if(!IRQCount) + { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount=IRQLatch; + } + } + } +} + +void Mapper90_init(void) +{ + tekker^=0x80; + SetWriteHandler(0x8000,0xffff,Mapper90_write); + SetReadHandler(0x5000,0x5000,tekread); + GameHBIRQHook=Mapper90_hb; +} + diff --git a/mappers/92.c b/mappers/92.c new file mode 100644 index 0000000..1494e7f --- /dev/null +++ b/mappers/92.c @@ -0,0 +1,45 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +/* Original code provided by LULU */ + +static DECLFW(Mapper92_write) +{ + uint8 reg=(A&0xF0)>>4; + uint8 bank=A&0xF; + + if(A>=0x9000) + { + if(reg==0xD) ROM_BANK16(0xc000,bank); + else if(reg==0xE) VROM_BANK8(bank); + } + else + { + if(reg==0xB) ROM_BANK16(0xc000,bank); + else if(reg==0x7) VROM_BANK8(bank); + } +} + +void Mapper92_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper92_write); +} diff --git a/mappers/95.c b/mappers/95.c new file mode 100644 index 0000000..2d6792b --- /dev/null +++ b/mappers/95.c @@ -0,0 +1,76 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + +#define dbarray mapbyte1 +static void FP_FASTAPASS(1) dragonbust_ppu(uint32 A) +{ + static int last=-1; + static uint8 z; + + if(A>=0x2000) return; + + A>>=13; + + z=dbarray[A]; + + if(z!=last) + { + onemir(z); + last=z; + } +} + + +DECLFW(Mapper95_write) +{ + switch(A&0xF001){ + + case 0x8000: + MMC3_cmd = V; + break; + + case 0x8001: + switch(MMC3_cmd&7){ + case 0: dbarray[0]=dbarray[1]=(V&0x20)>>4;onemir((V&0x20)>>4);V>>=1;VROM_BANK2(0x0000,V);break; + case 1: dbarray[2]=dbarray[3]=(V&0x20)>>4;onemir((V&0x20)>>4);V>>=1;VROM_BANK2(0x0800,V);break; + case 2: dbarray[4]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1000,V); break; + case 3: dbarray[5]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1400,V); break; + case 4: dbarray[6]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1800,V); break; + case 5: dbarray[7]=(V&0x20)>>4;onemir((V&0x20)>>4);VROM_BANK1(0x1C00,V); break; + case 6: + ROM_BANK8(0x8000,V); + break; + case 7: + ROM_BANK8(0xA000,V); + break; + } + break; +} +} + +void Mapper95_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper95_write); + PPU_hook=dragonbust_ppu; +} + diff --git a/mappers/97.c b/mappers/97.c new file mode 100644 index 0000000..f019fdc --- /dev/null +++ b/mappers/97.c @@ -0,0 +1,42 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + + + +DECLFW(Mapper97_write) +{ +ROM_BANK16(0xC000,V&15); +switch(V>>6) + { + case 0:break; + case 1:MIRROR_SET2(0);break; + case 2:MIRROR_SET2(1);break; + case 3:break; + } +} + +void Mapper97_init(void) +{ + ROM_BANK16(0x8000,~0); + SetWriteHandler(0x8000,0xffff,Mapper97_write); +} + diff --git a/mappers/99.c b/mappers/99.c new file mode 100644 index 0000000..c71611e --- /dev/null +++ b/mappers/99.c @@ -0,0 +1,35 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static writefunc oldmorko; + +static DECLFW(morko) +{ + VROM_BANK8((V>>2)&1); + oldmorko(A,V); +} + +void Mapper99_init(void) +{ + oldmorko=GetWriteHandler(0x4016); + SetWriteHandler(0x4016,0x4016,morko); +} diff --git a/mappers/Makefile b/mappers/Makefile new file mode 100644 index 0000000..c95ca8f --- /dev/null +++ b/mappers/Makefile @@ -0,0 +1,74 @@ +MOBJS = mappers/mmc2and4.o mappers/simple.o mappers/112.o mappers/117.o mappers/15.o mappers/151.o mappers/16.o mappers/17.o mappers/18.o mappers/180.o mappers/184.o mappers/19.o mappers/21.o mappers/22.o mappers/225.o mappers/226.o mappers/228.o mappers/229.o mappers/23.o mappers/24and26.o mappers/240.o mappers/246.o mappers/25.o mappers/32.o mappers/33.o mappers/40.o mappers/41.o mappers/6.o mappers/64.o mappers/65.o mappers/67.o mappers/68.o mappers/69.o mappers/71.o mappers/73.o mappers/75.o mappers/76.o mappers/79.o mappers/8.o mappers/80.o mappers/85.o mappers/90.o mappers/95.o mappers/97.o mappers/99.o mappers/182.o mappers/vrc7snd.o mappers/46.o mappers/43.o mappers/42.o mappers/113.o mappers/86.o mappers/89.o mappers/83.o mappers/77.o mappers/92.o mappers/105.o mappers/88.o mappers/248.o mappers/fmopl.o mappers/242.o mappers/232.o mappers/72.o mappers/234.o mappers/227.o mappers/82.o mappers/189.o mappers/245.o mappers/249.o mappers/51.o + +fmopl.o: mappers/fmopl.c mappers/fmopl.h +vrc7snd.o: mappers/vrc7snd.c + +mappers/simple.o: mappers/simple.c +mappers/mmc2and4.o: mappers/mmc2and4.c + +mappers/112.o: mappers/112.c +mappers/117.o: mappers/117.c +mappers/15.o: mappers/15.c +mappers/151.o: mappers/151.c +mappers/16.o: mappers/16.c +mappers/17.o: mappers/17.c +mappers/18.o: mappers/18.c +mappers/19.o: mappers/19.c +mappers/180.o: mappers/180.c +mappers/184.o: mappers/184.c +mappers/21.o: mappers/21.c +mappers/22.o: mappers/22.c +mappers/23.o: mappers/23.c +mappers/24and26.o: mappers/24and26.c +mappers/25.o: mappers/25.c +mappers/225.o: mappers/225.c +mappers/226.o: mappers/226.c +mappers/228.o: mappers/228.c +mappers/229.o: mappers/229.c +mappers/240.o: mappers/240.c +mappers/246.o: mappers/246.c +mappers/32.o: mappers/32.c +mappers/33.o: mappers/33.c +mappers/40.o: mappers/40.c +mappers/41.o: mappers/41.c +mappers/6.o: mappers/6.c +mappers/64.o: mappers/64.c +mappers/65.o: mappers/65.c +mappers/67.o: mappers/67.c +mappers/68.o: mappers/68.c +mappers/69.o: mappers/69.c +mappers/71.o: mappers/71.c +mappers/73.o: mappers/73.c +mappers/75.o: mappers/75.c +mappers/76.o: mappers/76.c +mappers/79.o: mappers/79.c +mappers/8.o: mappers/8.c +mappers/80.o: mappers/80.c +mappers/85.o: mappers/85.c +mappers/90.o: mappers/90.c +mappers/95.o: mappers/95.c +mappers/97.o: mappers/97.c +mappers/99.o: mappers/99.c +mappers/182.o: mappers/182.c +mappers/46.o: mappers/46.c +mappers/43.o: mappers/43.c +mappers/42.o: mappers/42.c +mappers/113.o: mappers/113.c +mappers/86.o: mappers/86.c +mappers/89.o: mappers/89.c +mappers/83.o: mappers/83.c +mappers/77.o: mappers/77.c +mappers/92.o: mappers/92.c +mappers/105.o: mappers/105.c +mappers/88.o: mappers/88.c +mappers/248.o: mappers/248.c +mappers/242.o: mappers/242.c +mappers/232.o: mappers/232.c +mappers/72.o: mappers/72.c +mappers/234.o: mappers/234.c +mappers/227.o: mappers/227.c +mappers/82.o: mappers/82.c +mappers/189.o: mappers/189.c +mappers/245.o: mappers/245.c +mappers/249.o: mappers/249.c +mappers/51.o: mappers/51.c diff --git a/mappers/fmopl.c b/mappers/fmopl.c new file mode 100644 index 0000000..b2fc12c --- /dev/null +++ b/mappers/fmopl.c @@ -0,0 +1,871 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1999,2000 Tatsuyuki Satoh + * Copyright (C) 2001,2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 file has been heavily modified from the original(mostly unused + code was removed). If you want to use it for anything other than + VRC7 sound emulation, you should get the original from the AdPlug + source distribution or the MAME(version 0.37b16) source distribution + (but be careful about the different licenses). + - Xodnizel +*/ + +#include +#include +#include +#include +#include +#include "mapinc.h" +#include "fmopl.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* -------------------- preliminary define section --------------------- */ +/* attack/decay rate time rate */ +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ + +#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */ + +#define FREQ_BITS 24 /* frequency turn */ + +/* counter bits = 20 , octerve 7 */ +#define FREQ_RATE (1<<(FREQ_BITS-20)) +#define TL_BITS (FREQ_BITS+2) + +/* final output shift , limit minimum and maximum */ +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ +#define OPL_MAXOUT (0x7fff< max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + +/* ----- key on ----- */ +INLINE void OPL_KEYON(OPL_SLOT *SLOT) +{ + /* sin wave restart */ + SLOT->Cnt = 0; + /* set attack */ + SLOT->evm = ENV_MOD_AR; + SLOT->evs = SLOT->evsa; + SLOT->evc = EG_AST; + SLOT->eve = EG_AED; +} +/* ----- key off ----- */ +INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) +{ + if( SLOT->evm > ENV_MOD_RR) + { + /* set envelope counter from envleope output */ + SLOT->evm = ENV_MOD_RR; + if( !(SLOT->evc&EG_DST) ) + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; + SLOT->eve = EG_DED; + SLOT->evs = SLOT->evsr; + } +} + +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ +/* return : envelope output */ +INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) +{ + /* calcrate envelope generator */ + if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) + { + switch( SLOT->evm ){ + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ + /* next DR */ + SLOT->evm = ENV_MOD_DR; + SLOT->evc = EG_DST; + SLOT->eve = SLOT->SL; + SLOT->evs = SLOT->evsd; + break; + case ENV_MOD_DR: /* DECAY -> SL or RR */ + SLOT->evc = SLOT->SL; + SLOT->eve = EG_DED; + if(SLOT->eg_typ) + { + SLOT->evs = 0; + } + else + { + SLOT->evm = ENV_MOD_RR; + SLOT->evs = SLOT->evsr; + } + break; + case ENV_MOD_RR: /* RR -> OFF */ + SLOT->evc = EG_OFF; + SLOT->eve = EG_OFF+1; + SLOT->evs = 0; + break; + } + } + /* calcrate envelope */ + return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); +} + +/* set algorythm connection */ +static void set_algorythm( OPL_CH *CH) +{ + INT32 *carrier = &outd[0]; + CH->connect1 = CH->CON ? carrier : &feedback2; + CH->connect2 = carrier; +} + +/* ---------- frequency counter for operater update ---------- */ +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* frequency step counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + /* attack , decay rate recalcration */ + SLOT->evsa = SLOT->AR[ksr]; + SLOT->evsd = SLOT->DR[ksr]; + SLOT->evsr = SLOT->RR[ksr]; + } + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = MUL_TABLE[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_typ = (v&0x20)>>5; + SLOT->vib = (v&0x40); + SLOT->ams = (v&0x80); + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */ + +// if(slot&1) +// if(ksl) {sprintf(errmsg,"doh");howlong=255;ksl=0;} + + SLOT->ksl = ksl ? ksl : 31; +// SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */ + + if( !(OPL->mode&0x80) ) + { /* not CSM latch total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ar = v>>4; + int dr = v&0x0f; + + SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0; + SLOT->evsa = SLOT->AR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa; + + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; + SLOT->evsd = SLOT->DR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int sl = v>>4; + int rr = v & 0x0f; + + SLOT->SL = SL_TABLE[sl]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL; + SLOT->RR = &OPL->DR_TABLE[rr<<2]; + SLOT->evsr = SLOT->RR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr; +} + +/* operator output calcrator */ +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] +/* ---------- calcrate one of channel ---------- */ +INLINE void OPL_CALC_CH( OPL_CH *CH ) +{ + UINT32 env_out; + OPL_SLOT *SLOT; + + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH->FB) + { + int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; + CH->op1_out[1] = CH->op1_out[0]; + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1); + } + else + { + *CH->connect1 += OP_OUT(SLOT,env_out,0); + } + }else + { + CH->op1_out[1] = CH->op1_out[0]; + CH->op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH->SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT,env_out, feedback2); + } +} + +/* ----------- initialize time tabls ----------- */ +static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE ) +{ + int i; + double rate; + + /* make attack rate & decay rate tables */ + for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; + for (i = 4;i <= 60;i++){ + rate = OPL->freqbase; /* frequency rate */ + if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ + rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ + rate *= (double)(EG_ENT<AR_TABLE[i] = rate / ARRATE; + OPL->DR_TABLE[i] = rate / DRRATE; + } + for (i = 60;i < 76;i++) + { + OPL->AR_TABLE[i] = EG_AED-1; + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; + } +} + +/* ---------- generic table initialize ---------- */ +static int OPLOpenTable( void ) +{ + int s,t; + double rate; + int i,j; + double pom; + + /* allocate dynamic tables */ + if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL) + return 0; + if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL) + { + free(TL_TABLE); + return 0; + } + if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + return 0; + } + if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + return 0; + } + /* make total level table */ + for (t = 0;t < EG_ENT-1 ;t++){ + rate = ((1< voltage */ + TL_TABLE[ t] = (int)rate; + TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; + + } + /* fill volume off area */ + for ( t = EG_ENT-1; t < TL_MAX ;t++){ + TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; + } + + /* make sinwave table (total level offet) */ + /* degree 0 = degree 180 = off */ + SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; + for (s = 1;s <= SIN_ENT/4;s++){ + pom = sin(2*PI*s/SIN_ENT); /* sin */ + pom = 20*log10(1/pom); /* decibel */ + j = pom / EG_STEP; /* TL_TABLE steps */ + + /* degree 0 - 90 , degree 180 - 90 : plus section */ + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; + /* degree 180 - 270 , degree 360 - 270 : minus section */ + SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; + + } + for (s = 0;s < SIN_ENT;s++) + { + SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; + SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)]; + SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s]; + } + + /* envelope counter -> envelope output table */ + for (i=0; i= EG_ENT ) pom = EG_ENT-1; */ + ENV_CURVE[i] = (int)pom; + /* DECAY ,RELEASE curve */ + ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; + } + /* off */ + ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; + /* make LFO ams table */ + for (i=0; iSLOT[SLOT1]; + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; + /* all key off */ + OPL_KEYOFF(slot1); + OPL_KEYOFF(slot2); + /* total level latch */ + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + /* key on */ + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(slot1); + OPL_KEYON(slot2); +} + +/* ---------- opl initialize ---------- */ +static void OPL_initalize(FM_OPL *OPL) +{ + int fn; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; + /* make time tables */ + init_timetables( OPL , OPL_ARRATE , OPL_DRRATE ); + /* make fnumber -> increment counter table */ + for( fn=0 ; fn < 1024 ; fn++ ) + { + OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2; + } + /* LFO freq.table */ + OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<rate * 3.7 * ((double)OPL->clock/3600000) : 0; + OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<rate * 6.4 * ((double)OPL->clock/3600000) : 0; +} + +/* ---------- write a OPL registers ---------- */ +static void OPLWriteReg(FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + switch(r&0xe0) + { + case 0x00: /* 00-1f:controll */ + switch(r&0x1f) + { + case 0x01: + /* wave selector enable */ + if(OPL->type&OPL_TYPE_WAVESEL) + { + OPL->wavesel = v&0x20; + if(!OPL->wavesel) + { + /* preset compatible mode */ + int c; + for(c=0;cmax_ch;c++) + { + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; + } + } + } + return; + } + break; + case 0x20: /* am,vib,ksr,eg type,mul */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_mul(OPL,slot,v); + return; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ksl_tl(OPL,slot,v); + return; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ar_dr(OPL,slot,v); + return; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_sl_rr(OPL,slot,v); + return; + case 0xa0: + switch(r) + { + case 0xbd: + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ + { + OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; + OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + int keyon = (v>>5)&1; + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + if(CH->keyon != keyon) + { + if( (CH->keyon=keyon) ) + { + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(&CH->SLOT[SLOT1]); + OPL_KEYON(&CH->SLOT[SLOT2]); + } + else + { + OPL_KEYOFF(&CH->SLOT[SLOT1]); + OPL_KEYOFF(&CH->SLOT[SLOT2]); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + int blockRv = 7-(block_fnum>>10); + int fnum = block_fnum&0x3ff; + CH->block_fnum = block_fnum; + + CH->ksl_base = KSL_TABLE[block_fnum>>6]; + CH->fc = OPL->FN_TABLE[fnum]>>blockRv; + CH->kcode = CH->block_fnum>>9; + if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1; + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + return; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + { + int feedback = (v>>1)&7; + CH->FB = feedback ? (8+1) - feedback : 0; + CH->CON = v&1; + set_algorythm(CH); + } + return; + case 0xe0: /* wave type */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + CH = &OPL->P_CH[slot/2]; + if(OPL->wavesel) + { + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT]; + } + return; + } +} + +/* lock/unlock for common table */ +static int OPL_LockTable(void) +{ + num_lock++; + if(num_lock>1) return 0; + /* first time */ + cur_chip = NULL; + /* allocate total level table (128kb space) */ + if( !OPLOpenTable() ) + { + num_lock--; + return -1; + } + return 0; +} + +static void OPL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + /* last time */ + cur_chip = NULL; + OPLCloseTable(); +} + +/*******************************************************************************/ +/* YM3812 local section */ +/*******************************************************************************/ + +/* ---------- update one of chip ----------- */ +void YM3812UpdateOne(FM_OPL *OPL, UINT32 *buffer, int length) +{ + int i; + UINT32 *buf = buffer; + UINT32 amsCnt = OPL->amsCnt; + UINT32 vibCnt = OPL->vibCnt; + OPL_CH *CH,*R_CH; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[6]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = E_CH; + for( i=0; i < length ; i++ ) + { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; + outd[0] = 0; + /* FM part */ + for(CH=S_CH ; CH < R_CH ; CH++) + OPL_CALC_CH(CH); + /* limit check */ + //data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); + /* store to sound buffer */ + { + int32 d=outd[0]>>OPL_OUTSB; + if(d<-32768) d=-32768; + d+=32768; + buf[i] += d; + } + } + + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; +} + +/* ---------- reset one of chip ---------- */ +void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; + + /* reset chip */ + OPL->mode = 0; /* normal mode */ + + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wabesel disable */ + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + /* reset OPerator paramater */ + for( c = 0 ; c < OPL->max_ch ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + /* OPL->P_CH[c].PAN = OPN_CENTER; */ + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = &SIN_TABLE[0]; + /* CH->SLOT[s].evm = ENV_MOD_RR; */ + CH->SLOT[s].evc = EG_OFF; + CH->SLOT[s].eve = EG_OFF+1; + CH->SLOT[s].evs = 0; + } + } +} + +/* ---------- Create one of vietual YM3812 ---------- */ +/* 'rate' is sampling rate and 'bufsiz' is the size of the */ +FM_OPL *OPLCreate(int type, int clock, int rate) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + int max_ch = 9; /* normaly 9 channels */ + + if( OPL_LockTable() ==-1) return NULL; + /* allocate OPL state space */ + state_size = sizeof(FM_OPL); + state_size += sizeof(OPL_CH)*max_ch; + + /* allocate memory block */ + ptr = malloc(state_size); + if(ptr==NULL) return NULL; + /* clear */ + memset(ptr,0,state_size); + OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL); + OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch; + + /* set channel state pointer */ + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + OPL->max_ch = max_ch; + /* init grobal tables */ + OPL_initalize(OPL); + /* reset chip */ + OPLResetChip(OPL); + + return OPL; +} + +/* ---------- Destroy one of vietual YM3812 ---------- */ +void OPLDestroy(FM_OPL *OPL) +{ + OPL_UnLockTable(); + free(OPL); +} + +/* ---------- YM3812 I/O interface ---------- */ +void OPLWrite(FM_OPL *OPL,UINT8 a,UINT8 v) +{ + OPLWriteReg(OPL,a,v); +} diff --git a/mappers/fmopl.h b/mappers/fmopl.h new file mode 100644 index 0000000..f11a087 --- /dev/null +++ b/mappers/fmopl.h @@ -0,0 +1,149 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1999,2000 Tatsuyuki Satoh + * Copyright (C) 2001,2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 file has been heavily modified from the original(mostly unused + code was removed). If you want to use it for anything other than + VRC7 sound emulation, you should get the original from the AdPlug + source distribution or the MAME(version 0.37b16) source distribution + (but be careful about the different licenses). + - Xodnizel +*/ + +#ifndef __FMOPL_H_ +#define __FMOPL_H_ + +/* --- system optimize --- */ +/* select bit size of output : 8 or 16 */ +#define OPL_OUTPUT_BIT 16 + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H + +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned long UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed long INT32; /* signed 32bit */ +#endif + +#if (OPL_OUTPUT_BIT==16) +typedef INT16 OPLSAMPLE; +#endif +#if (OPL_OUTPUT_BIT==8) +typedef unsigned char OPLSAMPLE; +#endif + + +/* !!!!! here is private section , do not access there member direct !!!!! */ + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ + +/* Saving is necessary for member of the 'R' mark for suspend/resume */ +/* ---------- OPL one of slot ---------- */ +typedef struct fm_opl_slot { + INT32 TL; /* total level :TL << 8 */ + INT32 TLL; /* adjusted now TL */ + UINT8 KSR; /* key scale rate :(shift down bit) */ + INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */ + INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */ + INT32 SL; /* sustin level :SL_TALBE[SL] */ + INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */ + UINT8 ksl; /* keyscale level :(shift down bits) */ + UINT8 ksr; /* key scale rate :kcode>>KSR */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + UINT32 Cnt; /* frequency count : */ + UINT32 Incr; /* frequency step : */ + /* envelope generator state */ + UINT8 eg_typ; /* envelope type flag */ + UINT8 evm; /* envelope phase */ + INT32 evc; /* envelope counter */ + INT32 eve; /* envelope counter end point */ + INT32 evs; /* envelope counter step */ + INT32 evsa; /* envelope step for AR :AR[ksr] */ + INT32 evsd; /* envelope step for DR :DR[ksr] */ + INT32 evsr; /* envelope step for RR :RR[ksr] */ + /* LFO */ + UINT8 ams; /* ams flag */ + UINT8 vib; /* vibrate flag */ + /* wave selector */ + INT32 **wavetable; +}OPL_SLOT; + +/* ---------- OPL one of channel ---------- */ +typedef struct fm_opl_channel { + OPL_SLOT SLOT[2]; + UINT8 CON; /* connection type */ + UINT8 FB; /* feed back :(shift down bit) */ + INT32 *connect1; /* slot1 output pointer */ + INT32 *connect2; /* slot2 output pointer */ + INT32 op1_out[2]; /* slot1 output for selfeedback */ + /* phase generator state */ + UINT32 block_fnum; /* block+fnum : */ + UINT8 kcode; /* key code : KeyScaleCode */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 keyon; /* key on/off flag */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + UINT8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time) */ + UINT8 address; /* address register */ + UINT32 mode; /* Reg.08 : CSM , notesel,etc. */ + + /* FM channel slots */ + OPL_CH *P_CH; /* pointer of CH */ + int max_ch; /* maximum channel */ + + /* time tables */ + INT32 AR_TABLE[75]; /* atttack rate tables */ + INT32 DR_TABLE[75]; /* decay rate tables */ + UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */ + /* LFO */ + INT32 *ams_table; + INT32 *vib_table; + INT32 amsCnt; + INT32 amsIncr; + INT32 vibCnt; + INT32 vibIncr; + /* wave selector enable flag */ + UINT8 wavesel; + +} FM_OPL; + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) + +FM_OPL *OPLCreate(int type, int clock, int rate); +void OPLDestroy(FM_OPL *OPL); + +void OPLResetChip(FM_OPL *OPL); +void OPLWrite(FM_OPL *OPL,UINT8 a,UINT8 v); + +/* YM3626/YM3812 local section */ +void YM3812UpdateOne(FM_OPL *OPL, UINT32 *buffer, int length); + +#endif diff --git a/mappers/mapinc.h b/mappers/mapinc.h new file mode 100644 index 0000000..ebe446c --- /dev/null +++ b/mappers/mapinc.h @@ -0,0 +1,12 @@ +#include "../types.h" +#include "../x6502.h" +#include "../fce.h" +#define INESPRIV +#include "../ines.h" +#include "../version.h" +#include "../memory.h" +#include "../sound.h" +#include "../svga.h" +#include "../state.h" +#include "../cart.h" +#include "mapshare.h" diff --git a/mappers/mapshare.h b/mappers/mapshare.h new file mode 100644 index 0000000..4853050 --- /dev/null +++ b/mappers/mapshare.h @@ -0,0 +1,5 @@ +void MMC3_hb(void); + +#define resetmode mapbyte1[0] +#define MMC3_cmd mapbyte1[1] + diff --git a/mappers/mmc2and4.c b/mappers/mmc2and4.c new file mode 100644 index 0000000..1688d57 --- /dev/null +++ b/mappers/mmc2and4.c @@ -0,0 +1,121 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define MMC4reg mapbyte1 +#define latcha1 mapbyte2[0] +#define latcha2 mapbyte2[1] + + +static void FP_FASTAPASS(1) latchcheck(uint32 VAddr) +{ + uint8 l,h; + + h=VAddr>>8; + + if(h>=0x20 || ((h&0xF)!=0xF)) + return; + + l=VAddr&0xF0; + + if(h<0x10) + { + if(l==0xD0) + { + VROM_BANK4(0x0000,MMC4reg[0]); + latcha1=0xFD; + } + else if(l==0xE0) + { + VROM_BANK4(0x0000,MMC4reg[1]); + latcha1=0xFE; + } + } + else + { + if(l==0xD0) + { + VROM_BANK4(0x1000,MMC4reg[2]); + latcha2=0xFD; + } + else if(l==0xE0) + { + VROM_BANK4(0x1000,MMC4reg[3]); + latcha2=0xFE; + } + } +} + +DECLFW(Mapper9_write) // $Axxx +{ + ROM_BANK8(0x8000,V); +} + +DECLFW(Mapper10_write) +{ + ROM_BANK16(0x8000,V); +} + +DECLFW(Mapper9and10_write) +{ + switch(A&0xF000) + { + case 0xB000: + if (latcha1==0xFD) { VROM_BANK4(0x0000,V);} + MMC4reg[0]=V; + break; + case 0xC000: + if (latcha1==0xFE) {VROM_BANK4(0x0000,V);} + MMC4reg[1]=V; + break; + case 0xD000: + if (latcha2==0xFD) {VROM_BANK4(0x1000,V);} + MMC4reg[2]=V; + break; + case 0xE000: + if (latcha2==0xFE) {VROM_BANK4(0x1000,V);} + MMC4reg[3]=V; + break; + case 0xF000: + MIRROR_SET(V&1); + break; + } +} + +void Mapper9_init(void) +{ + latcha1=0xFE; + latcha2=0xFE; + ROM_BANK8(0xA000,~2); + ROM_BANK8(0x8000,0); + SetWriteHandler(0xA000,0xAFFF,Mapper9_write); + SetWriteHandler(0xB000,0xFFFF,Mapper9and10_write); + PPU_hook=latchcheck; +} + +void Mapper10_init(void) +{ + latcha1=latcha2=0xFE; + SetWriteHandler(0xA000,0xAFFF,Mapper10_write); + SetWriteHandler(0xB000,0xFFFF,Mapper9and10_write); + PPU_hook=latchcheck; +} + diff --git a/mappers/simple.c b/mappers/simple.c new file mode 100644 index 0000000..8f1ad74 --- /dev/null +++ b/mappers/simple.c @@ -0,0 +1,299 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +static uint8 latche; + +static DECLFW(Mapper2_write) +{ + latche=V; + ROM_BANK16(0x8000,V); +} + +void Mapper2_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper2_write); + AddExState(&latche, 1, 0, "LATC"); +} + +static DECLFW(Mapper3_write) +{ + VROM_BANK8(V); + latche=V; +} + +void Mapper3_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper3_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper7_write) +{ + ROM_BANK32(V&0xF); + onemir((V>>4)&1); + latche=V; +} + +void Mapper7_init(void) +{ + onemir(0); + SetWriteHandler(0x8000,0xFFFF,Mapper7_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper11_write) +{ + ROM_BANK32(V); + VROM_BANK8(V>>4); + latche=V; +} + +void Mapper11_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x8000,0xFFFF,Mapper11_write); + AddExState(&latche, 1, 0, "LATC"); +} + +static DECLFW(Mapper13_write) +{ + setchr4r(0x10,0x1000,V&3); + setprg32(0x8000,(V>>4)&3); + latche=V; +} + +static void Mapper13_StateRestore(int version) +{ + setchr4r(0x10,0x0000,0); + setchr4r(0x10,0x1000,latche&3); + setprg32(0x8000,(latche>>4)&3); +} + +void Mapper13_init(void) +{ + SetWriteHandler(0x8000,0xFFFF,Mapper13_write); + GameStateRestore=Mapper13_StateRestore; + AddExState(&latche, 1, 0, "LATC"); + AddExState(MapperExRAM, 16384, 0, "CHRR"); + SetupCartCHRMapping(0x10, MapperExRAM, 16384, 1); + + latche=0; + Mapper13_StateRestore(VERSION_NUMERIC); +} + +DECLFW(Mapper34_write) +{ +switch(A) + { + case 0x7FFD:ROM_BANK32(V);break; + case 0x7FFE:VROM_BANK4(0x0000,V);break; + case 0x7fff:VROM_BANK4(0x1000,V);break; + } +if(A>=0x8000) + ROM_BANK32(V); +} + +void Mapper34_init(void) +{ + SetWriteHandler(0x7ffd,0xffff,Mapper34_write); +} + +DECLFW(Mapper66_write) +{ + VROM_BANK8(V&0xF); + ROM_BANK32((V>>4)); + latche=V; +} + +void Mapper66_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x6000,0xffff,Mapper66_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper152_write) +{ + ROM_BANK16(0x8000,(V>>4)&0x7); + VROM_BANK8(V&0xF); + onemir((V>>7)&1); /* Saint Seiya...hmm. */ + latche=V; +} + +void Mapper152_init(void) +{ + onemir(0); + SetWriteHandler(0x6000,0xffff,Mapper152_write); + AddExState(&latche, 1, 0, "LATC"); +} + +static DECLFW(Mapper70_write) +{ + ROM_BANK16(0x8000,V>>4); + VROM_BANK8(V&0xF); + latche=V; +} + +void Mapper70_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper70_write); + AddExState(&latche, 1, 0, "LATC"); +} +/* Should be two separate emulation functions for this "mapper". Sigh. URGE TO KILL RISING. */ +static DECLFW(Mapper78_write) +{ + //printf("$%04x:$%02x\n",A,V); + ROM_BANK16(0x8000,V&0x7); + VROM_BANK8(V>>4); + onemir((V>>3)&1); + latche=V; +} + +void Mapper78_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper78_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper87_write) +{ + VROM_BANK8(V>>1); + latche=V; +} + +void Mapper87_init(void) +{ + SetWriteHandler(0x6000,0xffff,Mapper87_write); + AddExState(&latche, 1, 0, "LATC"); +} + +DECLFW(Mapper93_write) +{ + ROM_BANK16(0x8000,V>>4); + MIRROR_SET(V&1); + latche=V; +} + +void Mapper93_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper93_write); + AddExState(&latche, 1, 0, "LATC"); +} + + +DECLFW(Mapper94_write) +{ + ROM_BANK16(0x8000,V>>2); + latche=V; +} + +void Mapper94_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper94_write); + AddExState(&latche, 1, 0, "LATC"); +} + +/* I might want to add some code to the mapper 96 PPU hook function + to not change CHR banks if the attribute table is being accessed, + if I make emulation a little more accurate in the future. +*/ + +static uint8 M96LA; +static DECLFW(Mapper96_write) +{ + latche=V; + setprg32(0x8000,V&3); + setchr4r(0x10,0x0000,(latche&4)|M96LA); + setchr4r(0x10,0x1000,(latche&4)|3); +} + +static void FP_FASTAPASS(1) M96Hook(uint32 A) +{ + if(A<0x2000) + return; + M96LA=(A>>8)&3; + setchr4r(0x10,0x0000,(latche&4)|M96LA); +} + +static void M96Sync() +{ + setprg32(0x8000,latche&3); + setchr4r(0x10,0x0000,(latche&4)|M96LA); + setchr4r(0x10,0x1000,(latche&4)|3); +} + +void Mapper96_init(void) +{ + SetWriteHandler(0x8000,0xffff,Mapper96_write); + PPU_hook=M96Hook; + AddExState(&latche, 1, 0, "LATC"); + AddExState(&M96LA, 1, 0, "LAVA"); + SetupCartCHRMapping(0x10, MapperExRAM, 32768, 1); + latche=M96LA=0; + M96Sync(); + GameStateRestore=M96Sync; +} + +static DECLFW(Mapper140_write) +{ + VROM_BANK8(V&0xF); + ROM_BANK32((V>>4)&0xF); +} + +void Mapper140_init(void) +{ + ROM_BANK32(0); + SetWriteHandler(0x6000,0x7FFF,Mapper140_write); +} + +static void M185Sync() +{ + int x; + + if(!(mapbyte1[0]&3)) +// if(!(mapbyte1[0]==0x21)) + { + for(x=0;x<8;x++) + setchr1r(0x10,x<<10,0); + } + else + setchr8(0); +} + +static DECLFW(Mapper185_write) +{ + mapbyte1[0]=V; + M185Sync(); + // printf("Wr: $%04x:$%02x\n",A,V); +} + +void Mapper185_init(void) +{ + memset(MapperExRAM,0xFF,1024); + MapStateRestore=M185Sync; + mapbyte1[0]=0; + M185Sync(); + + SetupCartCHRMapping(0x10,MapperExRAM,1024,0); + SetWriteHandler(0x8000,0xFFFF,Mapper185_write); +} diff --git a/mappers/vrc7snd.c b/mappers/vrc7snd.c new file mode 100644 index 0000000..355143f --- /dev/null +++ b/mappers/vrc7snd.c @@ -0,0 +1,190 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" +#include "fmopl.h" + +static void VRC7_LoadInstrument(uint8 Chan); +void vrc7translate(uint8 Reg,uint8 V); + +FM_OPL *fmob=0; +uint8 VRC7Chan[3][6]; +static void InitOPL(void); + +void OPL2_setreg(uint8 A, uint8 V) +{ + if(fmob) + OPLWrite(fmob,A,V); +} + + +void LoadOPL(void) +{ + int x; + int y; + + for(x=y=0;x<0x40;x++) + y|=MapperExRAM[x]; + if(y) + { + InitOPL(); + for(x=0;x<6;x++) + { + VRC7_LoadInstrument(x); + vrc7translate(0x10+x,VRC7Chan[0][x]); + } + } +} + +static int dwave=0; + +void VRC7Update(void) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + + if(a && fmob) + YM3812UpdateOne(fmob, &Wave[dwave], a); + dwave+=a; +} + +void UpdateOPL(int Count) +{ + int32 z,a; + + z=((timestamp<<16)/soundtsinc)>>4; + a=z-dwave; + + if(fmob && a) + YM3812UpdateOne(fmob, &Wave[dwave], a); + + dwave=0; +} + +void KillOPL(void) +{ + if(fmob) OPLDestroy(fmob); + fmob=0; +} + +static void InitOPL(void) +{ + int x; + + if(!fmob) + { + if(!( fmob=OPLCreate(OPL_TYPE_WAVESEL,1789772*2,FSettings.SndRate))) + return; + } + GameExpSound.Kill=KillOPL; + OPLResetChip(fmob); + + for(x=0x1;x<0xF6;x++) + OPL2_setreg(x,0); + OPL2_setreg(0xBD,0xC0); + OPL2_setreg(1,0x20); /* Enable waveform type manipulation */ +} + +/* This following code is in the public domain, but the author, Quietust(see */ +/* the "AUTHORS" file, would appreciate credit to go to him if this code */ +/* is used. */ + +uint8 VRC7Instrument[16][8] = { + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* Custom instrument. */ + {0x03,0x01,0x14,0x80,0xC2,0x90,0x43,0x14}, /* Currently working on this one */ + {0x13,0x41,0x10,0x0B,0xFF,0xF2,0x32,0xD6}, + {0x01,0x01,0x10,0x08,0xF0,0xF4,0x00,0x04}, /* 90% perfect */ + {0x21,0x41,0x1B,0x08,0x66,0x80,0x30,0x85}, + {0x22,0x21,0x20,0x03,0x75,0x70,0x24,0x14}, + {0x02,0x01,0x06,0x00,0xF0,0xF2,0x03,0x95}, /* Do not touch! 98% perfect! */ + {0x21,0x41,0x18,0x10,0x93,0xE0,0x21,0x15}, + {0x01,0x22,0x13,0x00,0xF0,0x82,0x00,0x15}, + {0x05,0x01,0x22,0x00,0x60,0xE3,0xA0,0xF5}, /* 90% perfect */ + {0x85,0x01,0x20,0x00,0xD7,0xA2,0x22,0xF5}, /* 90% perfect */ + {0x07,0x81,0x2B,0x05,0xF4,0xF2,0x14,0xF4}, /* 95% perfect */ + {0x21,0x41,0x20,0x18,0xF3,0x80,0x13,0x95}, + {0x01,0x02,0x20,0x00,0xF9,0x92,0x41,0x75}, /* Do not touch! 98% perfect! */ + {0x21,0x62,0x0E,0x00,0x84,0x85,0x45,0x15}, /* 90% perfect */ + {0x21,0x62,0x0E,0x00,0xA1,0xA0,0x34,0x16} /* Do not touch! 98% perfect! */ +}; + +static uint8 InstTrans[6] = {0x00,0x01,0x02,0x08,0x09,0x0A}; + +static void VRC7_LoadInstrument(uint8 Chan) +{ + uint8 *i; + uint8 x = InstTrans[Chan]; + uint8 y = (VRC7Chan[2][Chan] >> 4) & 0xF; + + i=VRC7Instrument[y]; + + OPL2_setreg((0x20+x),i[0]); + OPL2_setreg((0x23+x),i[1]); + OPL2_setreg((0x40+x),i[2]); + OPL2_setreg((0x43+x),((i[3] & 0xC0) + | ((VRC7Chan[2][Chan] << 2) & 0x3C))); // quiet + OPL2_setreg(0xe0+x,(i[3] >> 3) & 0x01); + OPL2_setreg(0xe3+x,(i[3] >> 4) & 0x01); + OPL2_setreg(0xC0+Chan,(i[3] << 1) & 0x0E); + OPL2_setreg(0x60+x,i[4]); + OPL2_setreg(0x63+x,i[5]); + OPL2_setreg(0x80+x,i[6]); + OPL2_setreg(0x83+x,i[7]); +} + +void vrc7translate(uint8 Reg,uint8 V) +{ + uint8 x = Reg & 0x0F, y; + if(!fmob) InitOPL(); + + MapperExRAM[Reg]=V; + + VRC7Update(); + switch ((Reg & 0xF0) >> 4) + { + case 0: + if (x & 0x08) break; + VRC7Instrument[0][x] = V; + for (y = 0; y < 6; y++) + if (!(VRC7Chan[2][y]&0xF0)) + VRC7_LoadInstrument(y); + break; + case 1: + if(x>5) break; + VRC7Chan[0][x] = V; + OPL2_setreg(0xA0 + x,(VRC7Chan[0][x] << 1) & 0xFE); + OPL2_setreg(0xB0 + x,((VRC7Chan[0][x] >> 7) & 0x01) | ((VRC7Chan[1][x] << 1) & 0x3E)); + break; + case 2: + if(x>5) break; + VRC7Chan[1][x] = V; + OPL2_setreg(0xB0 + x,(((VRC7Chan[0][x] >> 7) & 0x01) | ((VRC7Chan[1][x] << 1) & 0x3E))); + break; + case 3: + if(x>5) break; + VRC7Chan[2][x] = V; + VRC7_LoadInstrument(x); + break; + } +} diff --git a/mbshare/Makefile b/mbshare/Makefile new file mode 100644 index 0000000..3508da1 --- /dev/null +++ b/mbshare/Makefile @@ -0,0 +1,5 @@ +MUSOBJS = mbshare/mmc5.o mbshare/mmc3.o mbshare/mmc1.o + +mbshare/mmc1.o: mbshare/mmc1.c +mbshare/mmc3.o: mbshare/mmc3.c +mbshare/mmc5.o: mbshare/mmc5.c diff --git a/mbshare/mapinc.h b/mbshare/mapinc.h new file mode 100644 index 0000000..9702ec2 --- /dev/null +++ b/mbshare/mapinc.h @@ -0,0 +1,14 @@ +#include "../types.h" +#include "../x6502.h" +#include "../fce.h" +#define INESPRIV +#include "../ines.h" +#include "../version.h" +#include "../memory.h" +#include "../sound.h" +#include "../svga.h" +#include "../state.h" +#define UNIFPRIV +#include "../unif.h" +#include "../cart.h" +#include "../cheat.h" diff --git a/mbshare/mmc1.c b/mbshare/mmc1.c new file mode 100644 index 0000000..8b238d7 --- /dev/null +++ b/mbshare/mmc1.c @@ -0,0 +1,353 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "mapinc.h" + +#define MMC1_reg mapbyte1 +#define MMC1_buf mapbyte2[0] +#define MMC1_sft mapbyte3[0] + +static int mmc1opts; + + +uint8 MMC1WRAMsize; /* For use in iNES.c */ + +static DECLFW(MBWRAM) +{ + if(!(MMC1_reg[3]&0x10)) + Page[A>>11][A]=V; // WRAM is enabled. +} + +static DECLFR(MAWRAM) +{ + if(MMC1_reg[3]&0x10) + return X.DB; // WRAM is disabled + return(Page[A>>11][A]); +} + +static void MMC1CHR(void) +{ + if(mmc1opts&4) + { + if(MMC1_reg[0]&0x10) + setprg8r(0x10,0x6000,(MMC1_reg[1]>>4)&1); + else + setprg8r(0x10,0x6000,(MMC1_reg[1]>>3)&1); + } + + if(MMC1_reg[0]&0x10) + { + setchr4(0x0000,MMC1_reg[1]); + setchr4(0x1000,MMC1_reg[2]); + } + else + setchr8(MMC1_reg[1]>>1); +} + +static void MMC1PRG(void) +{ + uint8 offs; + + offs=MMC1_reg[1]&0x10; + switch(MMC1_reg[0]&0xC) + { + case 0xC: setprg16(0x8000,(MMC1_reg[3]+offs)); + setprg16(0xC000,0xF+offs); + break; + case 0x8: setprg16(0xC000,(MMC1_reg[3]+offs)); + setprg16(0x8000,offs); + break; + case 0x0: + case 0x4: + setprg16(0x8000,((MMC1_reg[3]&~1)+offs)); + setprg16(0xc000,((MMC1_reg[3]&~1)+offs+1)); + break; + } +} +static void MMC1MIRROR(void) +{ + switch(MMC1_reg[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; + //FCEU_DispMessage("%016x",timestampbase+timestamp); + //printf("$%04x:$%02x, $%04x\n",A,V,X.PC); + //DumpMem("out",0xe000,0xffff); + + /* 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; + if (V&0x80) + { + MMC1_reg[0]|=0xC; + MMC1_sft=MMC1_buf=0; + MMC1PRG(); + lreset=timestampbase+timestamp; + return; + } + + MMC1_buf|=(V&1)<<(MMC1_sft++); + + if (MMC1_sft==5) { + MMC1_reg[n]=MMC1_buf; + MMC1_sft = MMC1_buf=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(); +} + +static void MMC1CMReset(void) +{ + int i; + + for(i=0;i<4;i++) + MMC1_reg[i]=0; + MMC1_sft = MMC1_buf =0; + MMC1_reg[0]=0x1F; + + MMC1_reg[1]=0; + MMC1_reg[2]=0; // Should this be something other than 0? + MMC1_reg[3]=0; + + MMC1MIRROR(); + MMC1CHR(); + MMC1PRG(); +} + +void DetectMMC1WRAMSize(void) +{ + switch(iNESGameCRC32) + { + default:MMC1WRAMsize=1;break; + case 0xc6182024: /* Romance of the 3 Kingdoms */ + case 0x2225c20f: /* Genghis Khan */ + case 0x4642dda6: /* Nobunaga's Ambition */ + case 0x29449ba9: /* "" "" (J) */ + case 0x2b11e0b0: /* "" "" (J) */ + MMC1WRAMsize=2; + break; + } +} + +void Mapper1_init(void) +{ + lreset=0; + mmc1opts=0; + MMC1CMReset(); + SetWriteHandler(0x8000,0xFFFF,MMC1_write); + MapStateRestore=MMC1_Restore; + AddExState(&lreset, 8, 1, "LRST"); + + if(!VROM_size) + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + + if(MMC1WRAMsize==2) + mmc1opts|=4; + + SetupCartPRGMapping(0x10,WRAM,MMC1WRAMsize*8192,1); + SetReadHandler(0x6000,0x7FFF,MAWRAM); + SetWriteHandler(0x6000,0x7FFF,MBWRAM); + setprg8r(0x10,0x6000,0); +} + +static void GenMMC1Close(void) +{ + UNIFOpenWRAM(UOW_WR,0,0); + UNIFWriteWRAM(WRAM+((mmc1opts&4)?8192:0),8192); + UNIFCloseWRAM(); +} + + +static void GenMMC1Power(void) +{ + lreset=0; + if(mmc1opts&1) + { + FCEU_CheatAddRAM(8,0x6000,WRAM); + if(mmc1opts&4) + FCEU_dwmemset(WRAM,0,8192) + else if(!(mmc1opts&2)) + FCEU_dwmemset(WRAM,0,8192); + } + SetWriteHandler(0x8000,0xFFFF,MMC1_write); + SetReadHandler(0x8000,0xFFFF,CartBR); + + if(mmc1opts&1) + { + SetReadHandler(0x6000,0x7FFF,MAWRAM); + SetWriteHandler(0x6000,0x7FFF,MBWRAM); + setprg8r(0x10,0x6000,0); + } + + MMC1CMReset(); +} + +static void GenMMC1Init(int prg, int chr, int wram, int battery) +{ + mmc1opts=0; + PRGmask16[0]&=(prg>>14)-1; + CHRmask4[0]&=(chr>>12)-1; + CHRmask8[0]&=(chr>>13)-1; + + if(wram) + { + mmc1opts|=1; + if(wram>8) mmc1opts|=4; + SetupCartPRGMapping(0x10,WRAM,wram*1024,1); + AddExState(WRAM, wram*1024, 0, "WRAM"); + } + + if(battery && UNIFbattery) + { + mmc1opts|=2; + BoardClose=GenMMC1Close; + + UNIFOpenWRAM(UOW_RD,0,0); + UNIFReadWRAM(WRAM+((mmc1opts&4)?8192:0),8192); + UNIFCloseWRAM(); + } + + if(!chr) + { + CHRmask4[0]=1; + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(CHRRAM, 8192, 0, "CHRR"); + } + AddExState(mapbyte1, 32, 0, "MPBY"); + BoardPower=GenMMC1Power; + + GameStateRestore=MMC1_Restore; + AddExState(&lreset, 8, 1, "LRST"); +} + +//static void GenMMC1Init(int prg, int chr, int wram, int battery) +void SAROM_Init(void) +{ + GenMMC1Init(128, 64, 8, 1); +} + +void SBROM_Init(void) +{ + GenMMC1Init(128, 64, 0, 0); +} + +void SCROM_Init(void) +{ + GenMMC1Init(128, 128, 0, 0); +} + +void SEROM_Init(void) +{ + GenMMC1Init(32, 64, 0, 0); +} + +void SGROM_Init(void) +{ + GenMMC1Init(256, 0, 0, 0); +} + +void SKROM_Init(void) +{ + GenMMC1Init(256, 64, 8, 1); +} + +void SLROM_Init(void) +{ + GenMMC1Init(256, 128, 0, 0); +} + +void SL1ROM_Init(void) +{ + GenMMC1Init(128, 128, 0, 0); +} + +/* Begin unknown - may be wrong - perhaps they use different MMC1s from the + similarly functioning boards? +*/ + +void SL2ROM_Init(void) +{ + GenMMC1Init(256, 256, 0, 0); +} + +void SFROM_Init(void) +{ + GenMMC1Init(256, 256, 0, 0); +} + +void SHROM_Init(void) +{ + GenMMC1Init(256, 256, 0, 0); +} + +/* End unknown */ +/* */ +/* */ + +void SNROM_Init(void) +{ + GenMMC1Init(256, 0, 8, 1); +} + +void SOROM_Init(void) +{ + GenMMC1Init(256, 0, 16, 1); +} + + diff --git a/mbshare/mmc3.c b/mbshare/mmc3.c new file mode 100644 index 0000000..e1e55c8 --- /dev/null +++ b/mbshare/mmc3.c @@ -0,0 +1,649 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 BERO + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Code for emulating iNES mappers 4, 118,119 */ + +#include "mapinc.h" + +#define resetmode mapbyte1[0] +#define MMC3_cmd mapbyte1[1] +#define A000B mapbyte1[2] +#define A001B mapbyte1[3] +#define DRegBuf mapbyte4 + +#define PPUCHRBus mapbyte2[0] +#define TKSMIR mapbyte3 +#define PIRREGS mapbyte2 + +static void (*pwrap)(uint32 A, uint8 V); +static void (*cwrap)(uint32 A, uint8 V); +static void (*mwrap)(uint8 V); + +static int mmc3opts=0; + +static INLINE void FixMMC3PRG(int V); +static INLINE void FixMMC3CHR(int V); + +static int latched; + +static DECLFW(MMC3_IRQWrite) +{ + //printf("$%04x:$%02x, %d\n",A,V,scanline); + switch(A&0xE001) + { + case 0xc000:IRQLatch=V; + latched=1; + if(resetmode) + { + IRQCount=V; + latched=0; + //resetmode=0; + } + break; + case 0xc001:IRQCount=IRQLatch; + break; + case 0xE000:IRQa=0; + X6502_IRQEnd(FCEU_IQEXT); + resetmode=1; + break; + case 0xE001:IRQa=1; + if(latched) + IRQCount=IRQLatch; + break; + } +} + +static INLINE 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); +} + +static INLINE 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]); +} + +static 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; + + FixMMC3PRG(0); + FixMMC3CHR(0); +} + +static DECLFW(Mapper4_write) +{ + 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&1); + break; + case 0xA001: + A001B=V; + break; + } +} + +static void MMC3_hb(void) +{ + resetmode=0; + if(IRQCount>=0) + { + IRQCount--; + if(IRQCount<0) + { + //printf("IRQ: %d\n",scanline); + if(IRQa) + X6502_IRQBegin(FCEU_IQEXT); + } + } +} +static int isines; + +static void genmmc3restore(int version) +{ + if(version>=56) + { + mwrap(A000B&1); + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } + else if(isines) + iNESStateRestore(version); +} + +static void GENCWRAP(uint32 A, uint8 V) +{ + setchr1(A,V); +} + +static void GENPWRAP(uint32 A, uint8 V) +{ + setprg8(A,V&0x3F); +} + +static void GENMWRAP(uint8 V) +{ + A000B=V; + setmirror(V^1); +} + +static void GENNOMWRAP(uint8 V) +{ + A000B=V; +} + +static void genmmc3ii(void (*PW)(uint32 A, uint8 V), + void (*CW)(uint32 A, uint8 V), + void (*MW)(uint8 V)) +{ + pwrap=GENPWRAP; + cwrap=GENCWRAP; + mwrap=GENMWRAP; + if(PW) pwrap=PW; + if(CW) cwrap=CW; + if(MW) mwrap=MW; + A000B=(Mirroring&1)^1; // For hard-wired mirroring on some MMC3 games. + // iNES format needs to die or be extended... + mmc3opts=0; + SetWriteHandler(0x8000,0xBFFF,Mapper4_write); + SetWriteHandler(0xC000,0xFFFF,MMC3_IRQWrite); + + GameHBIRQHook=MMC3_hb; + GameStateRestore=genmmc3restore; + if(!VROM_size) + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + isines=1; + MMC3RegReset(); + MapperReset=MMC3RegReset; +} + +void Mapper4_init(void) +{ + genmmc3ii(0,0,0); +} + +static void M47PW(uint32 A, uint8 V) +{ + V&=0xF; + V|=PIRREGS[0]<<4; + setprg8(A,V); +} + +static void M47CW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0x7F; + NV|=PIRREGS[0]<<7; + setchr1(A,NV); +} + +static DECLFW(M47Write) +{ + PIRREGS[0]=V&1; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +void Mapper47_init(void) +{ + genmmc3ii(M47PW,M47CW,0); + SetWriteHandler(0x6000,0x7FFF,M47Write); + SetReadHandler(0x6000,0x7FFF,0); +} + +static void M44PW(uint32 A, uint8 V) +{ + uint32 NV=V; + if(PIRREGS[0]>=6) NV&=0x1F; + else NV&=0x0F; + NV|=PIRREGS[0]<<4; + setprg8(A,NV); +} +static void M44CW(uint32 A, uint8 V) +{ + uint32 NV=V; + if(PIRREGS[0]<6) NV&=0x7F; + NV|=PIRREGS[0]<<7; + setchr1(A,NV); +} + +static DECLFW(Mapper44_write) +{ + if(A&1) + { + PIRREGS[0]=V&7; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } + else + Mapper4_write(A,V); +} + +void Mapper44_init(void) +{ + genmmc3ii(M44PW,M44CW,0); + SetWriteHandler(0xA000,0xBFFF,Mapper44_write); +} + +static void M52PW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0x1F^((PIRREGS[0]&8)<<1); + NV|=((PIRREGS[0]&6)|((PIRREGS[0]>>3)&PIRREGS[0]&1))<<4; + setprg8(A,NV); +} + +static void M52CW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0xFF^((PIRREGS[0]&0x40)<<1); + NV|=(((PIRREGS[0]>>3)&4)|((PIRREGS[0]>>1)&2)|((PIRREGS[0]>>6)&(PIRREGS[0]>>4)&1))<<7; + setchr1(A,NV); +} + +static DECLFW(Mapper52_write) +{ + if(PIRREGS[1]) + { + (WRAM-0x6000)[A]=V; + return; + } + PIRREGS[1]=1; + PIRREGS[0]=V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M52Reset(void) +{ + PIRREGS[0]=PIRREGS[1]=0; + MMC3RegReset(); +} + +void Mapper52_init(void) +{ + genmmc3ii(M52PW,M52CW,0); + SetWriteHandler(0x6000,0x7FFF,Mapper52_write); + MapperReset=M52Reset; +} + +static void M45CW(uint32 A, uint8 V) +{ + uint32 NV=V; + if(PIRREGS[2]&8) + NV&=(1<<( (PIRREGS[2]&7)+1 ))-1; + else + NV&=0; + NV|=PIRREGS[0]|((PIRREGS[2]&0x10)<<4); + setchr1(A,NV); +} + +static void M45PW(uint32 A, uint8 V) +{ + V&=(PIRREGS[3]&0x3F)^0x3F; + V|=PIRREGS[1]; + setprg8(A,V); +} + +static DECLFW(Mapper45_write) +{ + if(PIRREGS[3]&0x40) return; + PIRREGS[PIRREGS[4]]=V; + PIRREGS[4]=(PIRREGS[4]+1)&3; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M45Reset(void) +{ + FCEU_dwmemset(PIRREGS,0,5); + MMC3RegReset(); +} + +void Mapper45_init(void) +{ + genmmc3ii(M45PW,M45CW,0); + SetWriteHandler(0x6000,0x7FFF,Mapper45_write); + SetReadHandler(0x6000,0x7FFF,0); + MapperReset=M45Reset; +} +static void M49PW(uint32 A, uint8 V) +{ + if(PIRREGS[0]&1) + { + V&=0xF; + V|=(PIRREGS[0]&0xC0)>>2; + setprg8(A,V); + } + else + setprg32(0x8000,(PIRREGS[0]>>4)&3); +} + +static void M49CW(uint32 A, uint8 V) +{ + uint32 NV=V; + NV&=0x7F; + NV|=(PIRREGS[0]&0xC0)<<1; + setchr1(A,NV); +} + +static DECLFW(M49Write) +{ + //printf("$%04x:$%02x\n",A,V); + if(A001B&0x80) + { + PIRREGS[0]=V; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } +} + +static void M49Reset(void) +{ + PIRREGS[0]=0; + MMC3RegReset(); +} + +void Mapper49_init(void) +{ + genmmc3ii(M49PW,M49CW,0); + SetWriteHandler(0x6000,0x7FFF,M49Write); + SetReadHandler(0x6000,0x7FFF,0); + MapperReset=M49Reset; +} + +static DECLFW(Mapper250_write) +{ + Mapper4_write((A&0xE000)|((A&0x400)>>10),A&0xFF); +} + +static DECLFW(M250_IRQWrite) +{ + MMC3_IRQWrite((A&0xE000)|((A&0x400)>>10),A&0xFF); +} + +void Mapper250_init(void) +{ + genmmc3ii(0,0,0); + SetWriteHandler(0x8000,0xBFFF,Mapper250_write); + SetWriteHandler(0xC000,0xFFFF,M250_IRQWrite); +} + +static void FP_FASTAPASS(1) TKSPPU(uint32 A) +{ + //static uint8 z; + //if(A>=0x2000 || type<0) return; + //if(type<0) return; + A&=0x1FFF; + //if(scanline>=140 && scanline<=200) {setmirror(MI_1);return;} + //if(scanline>=140 && scanline<=200) + // if(scanline>=190 && scanline<=200) {setmirror(MI_1);return;} + // setmirror(MI_1); + //printf("$%04x\n",A); + + 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)); +} + +void Mapper118_init(void) +{ + genmmc3ii(0,TKSWRAP,GENNOMWRAP); + PPU_hook=TKSPPU; +} + +static void TQWRAP(uint32 A, uint8 V) +{ + setchr1r((V&0x40)>>2,A,V&0x3F); +} + +void Mapper119_init(void) +{ + genmmc3ii(0,TQWRAP,0); + SetupCartCHRMapping(0x10, CHRRAM, 8192, 1); +} + +static int wrams; + +static void GenMMC3Close(void) +{ + UNIFOpenWRAM(UOW_WR,0,1); + UNIFWriteWRAM(WRAM,wrams); + UNIFCloseWRAM(); +} + +static DECLFW(MBWRAM) +{ + (WRAM-0x6000)[A]=V; +} + +static DECLFR(MAWRAM) +{ + return((WRAM-0x6000)[A]); +} + +static DECLFW(MBWRAMMMC6) +{ + WRAM[A&0x3ff]=V; +} + +static DECLFR(MAWRAMMMC6) +{ + return(WRAM[A&0x3ff]); +} + +static void GenMMC3Power(void) +{ + SetWriteHandler(0x8000,0xBFFF,Mapper4_write); + SetReadHandler(0x8000,0xFFFF,CartBR); + SetWriteHandler(0xC000,0xFFFF,MMC3_IRQWrite); + + if(mmc3opts&1) + { + if(wrams==1024) + { + FCEU_CheatAddRAM(1,0x7000,WRAM); + SetReadHandler(0x7000,0x7FFF,MAWRAMMMC6); + SetWriteHandler(0x7000,0x7FFF,MBWRAMMMC6); + } + else + { + FCEU_CheatAddRAM(wrams/1024,0x6000,WRAM); + SetReadHandler(0x6000,0x6000+wrams-1,MAWRAM); + SetWriteHandler(0x6000,0x6000+wrams-1,MBWRAM); + } + if(!(mmc3opts&2)) + FCEU_dwmemset(WRAM,0,wrams); + } + MMC3RegReset(); +} + +void GenMMC3_Init(int prg, int chr, int wram, int battery) +{ + pwrap=GENPWRAP; + cwrap=GENCWRAP; + mwrap=GENMWRAP; + + wrams=wram*1024; + + PRGmask8[0]&=(prg>>13)-1; + CHRmask1[0]&=(chr>>10)-1; + CHRmask2[0]&=(chr>>11)-1; + + if(wram) + { + mmc3opts|=1; + AddExState(WRAM, wram*1024, 0, "WRAM"); + } + + if(battery) + { + mmc3opts|=2; + BoardClose=GenMMC3Close; + + UNIFOpenWRAM(UOW_RD,0,1); + UNIFReadWRAM(WRAM,wram*1024); + UNIFCloseWRAM(); + } + + if(!chr) + { + CHRmask1[0]=7; + CHRmask2[0]=3; + SetupCartCHRMapping(0, CHRRAM, 8192, 1); + AddExState(CHRRAM, 8192, 0, "CHRR"); + } + AddExState(mapbyte1, 32, 0, "MPBY"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQLatch, 4, 1, "IQL1"); + + BoardPower=GenMMC3Power; + BoardReset=MMC3RegReset; + + GameHBIRQHook=MMC3_hb; + GameStateRestore=genmmc3restore; + isines=0; +} + +// void GenMMC3_Init(int prg, int chr, int wram, int battery) + +void TFROM_Init(void) +{ + GenMMC3_Init(512, 64, 0, 0); +} + +void TGROM_Init(void) +{ + GenMMC3_Init(512, 0, 0, 0); +} + +void TKROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 1); +} + +void TLROM_Init(void) +{ + GenMMC3_Init(512, 256, 0, 0); +} + +void TSROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 0); +} + +void TLSROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 0); + cwrap=TKSWRAP; + mwrap=GENNOMWRAP; +} + +void TKSROM_Init(void) +{ + GenMMC3_Init(512, 256, 8, 1); + cwrap=TKSWRAP; + mwrap=GENNOMWRAP; +} + +void TQROM_Init(void) +{ + GenMMC3_Init(512, 64, 0, 0); + cwrap=TQWRAP; +} + +/* MMC6 board */ +void HKROM_Init(void) +{ + GenMMC3_Init(512, 512, 1, 1); +} diff --git a/mbshare/mmc5.c b/mbshare/mmc5.c new file mode 100644 index 0000000..bb3e8f1 --- /dev/null +++ b/mbshare/mmc5.c @@ -0,0 +1,758 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* None of this code should use any of the iNES bank switching wrappers. */ + +#include "mapinc.h" + +void MMC5Sound(int Count); +void Do5SQ(int P); + +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 int32 inc; +uint8 MMC5fill[0x400]; + +#define MMC5IRQR mapbyte3[4] +#define MMC5LineCounter mapbyte3[5] +#define mmc5psize mapbyte1[0] +#define mmc5vsize mapbyte1[1] + +uint8 MMC5WRAMsize; +uint8 MMC5WRAMIndex[8]; + +uint8 MMC5ROMWrProtect[4]; +uint8 MMC5MemIn[5]; + +static void MMC5CHRA(void); +static void MMC5CHRB(void); + +typedef struct __cartdata { + unsigned long crc32; + unsigned char size; +} cartdata; + + +// ETROM seems to have 16KB of WRAM, ELROM seems to have 8KB +// EWROM seems to have 32KB of WRAM + +#define MMC5_NOCARTS 14 +cartdata MMC5CartList[MMC5_NOCARTS]= +{ + {0x9c18762b,2}, /* L'Empereur */ + {0x26533405,2}, + {0x6396b988,2}, + + {0xaca15643,2}, /* Uncharted Waters */ + {0xfe3488d1,2}, /* Dai Koukai Jidai */ + + {0x15fe6d0f,2}, /* BKAC */ + {0x39f2ce4b,2}, /* Suikoden */ + + {0x8ce478db,2}, /* Nobunaga's Ambition 2 */ + {0xeee9a682,2}, + + {0x1ced086f,2}, /* Ishin no Arashi */ + + {0xf540677b,4}, /* Nobunaga...Bushou Fuuun Roku */ + + {0x6f4e4312,4}, /* Aoki Ookami..Genchou */ + + {0xf011e490,4}, /* Romance of the 3 Kingdoms 2 */ + {0x184c2124,4}, /* Sangokushi 2 */ +}; + + +// Called by iNESLoad() +void DetectMMC5WRAMSize(void) +{ + int x; + + MMC5WRAMsize=1; + + for(x=0;x3)?255:0;break; + case 2:MMC5WRAMIndex[x]=(x&4)>>2;break; + case 4:MMC5WRAMIndex[x]=(x>3)?255:(x&3);break; + } + } +} + +static void MMC5CHRA(void) +{ + int x; + switch(mapbyte1[1]&3) + { + case 0:MMC5SPRVROM_BANK8(mapbyte2[7]); + setchr8(mapbyte2[7]); + break; + case 1:MMC5SPRVROM_BANK4(0x0000,mapbyte2[3]); + MMC5SPRVROM_BANK4(0x1000,mapbyte2[7]); + setchr4(0x0000,mapbyte2[3]); + setchr4(0x1000,mapbyte2[7]); + break; + case 2:MMC5SPRVROM_BANK2(0x0000,mapbyte2[1]); + MMC5SPRVROM_BANK2(0x0800,mapbyte2[3]); + MMC5SPRVROM_BANK2(0x1000,mapbyte2[5]); + MMC5SPRVROM_BANK2(0x1800,mapbyte2[7]); + setchr2(0x0000,mapbyte2[1]); + setchr2(0x0800,mapbyte2[3]); + setchr2(0x1000,mapbyte2[5]); + setchr2(0x1800,mapbyte2[7]); + break; + case 3: + for(x=0;x<8;x++) + { + setchr1(x<<10,mapbyte2[x]); + MMC5SPRVROM_BANK1(x<<10,mapbyte2[x]); + } + break; + } +} + +static void MMC5CHRB(void) +{ +int x; +switch(mapbyte1[1]&3) + { + case 0:MMC5BGVROM_BANK8(mapbyte3[3]); + setchr8(mapbyte3[3]); + break; + case 1: + MMC5BGVROM_BANK4(0x0000,mapbyte3[3]); + MMC5BGVROM_BANK4(0x1000,mapbyte3[3]); + setchr4(0x0000,mapbyte3[3]); + setchr4(0x1000,mapbyte3[3]); + break; + case 2:MMC5BGVROM_BANK2(0x0000,mapbyte3[1]); + MMC5BGVROM_BANK2(0x0800,mapbyte3[3]); + MMC5BGVROM_BANK2(0x1000,mapbyte3[1]); + MMC5BGVROM_BANK2(0x1800,mapbyte3[3]); + setchr2(0x0000,mapbyte3[1]); + setchr2(0x0800,mapbyte3[3]); + setchr2(0x1000,mapbyte3[1]); + setchr2(0x1800,mapbyte3[3]); + break; + case 3: + for(x=0;x<8;x++) + { + setchr1(x<<10,mapbyte3[x&3]); + MMC5BGVROM_BANK1(x<<10,mapbyte3[x&3]); + } + break; + } +} + +static void FASTAPASS(2) MMC5WRAM(uint32 A, uint32 V) +{ + V=MMC5WRAMIndex[V&7]; + if(V!=255) + { + setprg8r(0x10,A,V); + MMC5MemIn[(A-0x6000)>>13]=1; + } + else + MMC5MemIn[(A-0x6000)>>13]=0; +} + +static void MMC5PRG(void) +{ + int x; + + switch(mapbyte1[0]&3) + { + case 0: + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]= + MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1; + setprg32(0x8000,((mapbyte1[5]&0x7F)>>2)); + for(x=0;x<4;x++) + MMC5MemIn[1+x]=1; + break; + case 1: + if(mapbyte1[5]&0x80) + { + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1; + setprg16(0x8000,(mapbyte1[5]>>1)); + MMC5MemIn[1]=MMC5MemIn[2]=1; + } + else + { + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0; + MMC5WRAM(0x8000,mapbyte1[5]&7&0xFE); + MMC5WRAM(0xA000,(mapbyte1[5]&7&0xFE)+1); + } + MMC5MemIn[3]=MMC5MemIn[4]=1; + MMC5ROMWrProtect[2]=MMC5ROMWrProtect[3]=1; + setprg16(0xC000,(mapbyte1[7]&0x7F)>>1); + break; + case 2: + if(mapbyte1[5]&0x80) + { + MMC5MemIn[1]=MMC5MemIn[2]=1; + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=1; + setprg16(0x8000,(mapbyte1[5]&0x7F)>>1); + } + else + { + MMC5ROMWrProtect[0]=MMC5ROMWrProtect[1]=0; + MMC5WRAM(0x8000,mapbyte1[5]&7&0xFE); + MMC5WRAM(0xA000,(mapbyte1[5]&7&0xFE)+1); + } + if(mapbyte1[6]&0x80) + {MMC5ROMWrProtect[2]=1;MMC5MemIn[3]=1;setprg8(0xC000,mapbyte1[6]&0x7F);} + else + {MMC5ROMWrProtect[2]=0;MMC5WRAM(0xC000,mapbyte1[6]&7);} + MMC5MemIn[4]=1; + MMC5ROMWrProtect[3]=1; + setprg8(0xE000,mapbyte1[7]&0x7F); + break; + case 3: + for(x=0;x<3;x++) + if(mapbyte1[4+x]&0x80) + { + MMC5ROMWrProtect[0]=1; + setprg8(0x8000+(x<<13),mapbyte1[4+x]&0x7F); + MMC5MemIn[1+x]=1; + } + else + { + MMC5ROMWrProtect[0]=0; + MMC5WRAM(0x8000+(x<<13),mapbyte1[4+x]&7); + } + MMC5MemIn[4]=1; + MMC5ROMWrProtect[3]=1; + setprg8(0xE000,mapbyte1[7]&0x7F); + break; + } +} + +#define mul1 mapbyte3[6] +#define mul2 mapbyte3[7] + +static DECLFW(Mapper5_write) +{ + switch(A) + { + default:break; + case 0x5105: + { + int x; + for(x=0;x<4;x++) + { + switch((V>>(x<<1))&3) + { + case 0:PPUNTARAM|=1<>3)&0x1F;break; + case 0x5202:MMC5HackSPPage=V&0x3F;break; + case 0x5203:X6502_IRQEnd(FCEU_IQEXT);IRQCount=V;break; + case 0x5204:X6502_IRQEnd(FCEU_IQEXT);IRQa=V&0x80;break; + case 0x5205:mul1=V;break; + case 0x5206:mul2=V;break; + } +} + +DECLFR(MMC5_ReadROMRAM) +{ + if(MMC5MemIn[(A-0x6000)>>13]) + return Page[A>>11][A]; + else + return X.DB; +} + +DECLFW(MMC5_WriteROMRAM) +{ + if(A>=0x8000) + if(MMC5ROMWrProtect[(A-0x8000)>>13]) + return; + if(MMC5MemIn[(A-0x6000)>>13]) + if(((mapbyte4[0]&3)|((mapbyte4[1]&3)<<2)) == 6) + Page[A>>11][A]=V; +} + +static DECLFW(MMC5_ExRAMWr) +{ + if(MMC5HackCHRMode!=3) + (MapperExRAM+0x6000)[A&0x3ff]=V; +} + +static DECLFR(MMC5_ExRAMRd) +{ + /* Not sure if this is correct, so I'll comment it out for now. */ + //if(MMC5HackCHRMode>=2) + return (MapperExRAM+0x6000)[A&0x3ff]; + //else + // return(X.DB); +} + +static DECLFR(MMC5_read) +{ + switch(A) + { + //default:printf("$%04x\n",A);break; + case 0x5204:X6502_IRQEnd(FCEU_IQEXT); + { + uint8 x; + x=MMC5IRQR; + MMC5IRQR&=0x40; + return x; + } + case 0x5205:return (mul1*mul2); + case 0x5206:return ((mul1*mul2)>>8); + } + return(X.DB); +} + +uint8 dorko[0x400]; + +void MMC5Synco(void) +{ + int x; + + MMC5PRG(); + for(x=0;x<4;x++) + { + switch((mapbyte4[3]>>(x<<1))&3) + { + case 0:PPUNTARAM|=1<=239) + MMC5IRQR|=0x40; +} + +void MMC5_hb(void) +{ + if(scanline==240) + { + MMC5LineCounter=0; + MMC5IRQR=0x40; + return; + } + + if(MMC5LineCounter<240) + { + MMC5LineCounter++; + if(MMC5LineCounter==IRQCount) + { + MMC5IRQR|=0x80; + if(IRQa&0x80) + X6502_IRQBegin(FCEU_IQEXT); + } + } +// printf("%d:%d\n",MMC5LineCounter,scanline); + if(MMC5LineCounter==240) + MMC5IRQR=0; +} + +void Mapper5_StateRestore(int version) +{ + if(version<=70) + { + uint8 tmp[8192]; + + FCEU_memmove(tmp,MapperExRAM,8192); + FCEU_memmove(MapperExRAM,MapperExRAM+8192,16384+8192); + FCEU_memmove(MapperExRAM+16384+8192,tmp,8192); + } + MMC5Synco(); +} + +#define MMC5PSG (MapperExRAM+0x640B+8) + +static int C5BC[3]={0,0,0}; + +static void Do5PCM(void) +{ + int32 V; + int32 start,end; + + start=C5BC[2]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + C5BC[2]=end; + + if(!(MMC5PSG[0x10]&0x40) && MMC5PSG[0x11]) + for(V=start;V>4]+=MMC5PSG[0x11]<<2; +} + +DECLFW(Mapper5_SW) +{ + GameExpSound.Fill=MMC5Sound; + A&=0x1F; + + switch(A) + { + case 0x10: + case 0x11:Do5PCM();break; + + case 0x0:Do5SQ(0);break; + case 0x2:Do5SQ(0);break; + case 0x3:Do5SQ(0);break; + case 0x4:Do5SQ(1);break; + case 0x6:Do5SQ(1);break; + case 0x7:Do5SQ(1);break; + case 0x15: + { + int t=V^MMC5PSG[0x15]; + if(t&1) + Do5SQ(0); + if(t&2) + Do5SQ(1); + } + break; + } + MMC5PSG[A]=V; +} + +static int32 vcount[2]; +void Do5SQ(int P) +{ + int32 start,end; + int V; + int32 freq; + + start=C5BC[P]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + C5BC[P]=end; + + + + if(MMC5PSG[0x15]&(P+1)) + { + unsigned long dcycs; + unsigned char amplitude; + long vcoo; + + freq=(((MMC5PSG[(P<<2)+0x2]|((MMC5PSG[(P<<2)+0x3]&7)<<8)))); + + if(freq<8) goto mmc5enda; + freq+=1; + inc=(long double)((unsigned long)((FSettings.SndRate OVERSAMPLE)<<12))/((long double)PSG_base/freq); + + switch(MMC5PSG[P<<2]&0xC0) + { + default: + case 0x00:dcycs=inc>>3;break; + case 0x40:dcycs=inc>>2;break; + case 0x80:dcycs=inc>>1;break; + case 0xC0:dcycs=(inc+inc+inc)>>2;break; + } + + amplitude=(MMC5PSG[P<<2]&15)<<4; + + vcoo=vcount[P]; + for(V=start;V>4]+=amplitude; + vcoo+=0x1000; + if(vcoo>=inc) vcoo-=inc; + } + vcount[P]=vcoo; + } + mmc5enda:; // semi-colon must be here for MSVC +} + +void MMC5Sound(int Count) +{ + int x; + Do5SQ(0); + Do5SQ(1); + Do5PCM(); + for(x=0;x<3;x++) + C5BC[x]=Count; +} + +static void MMC5SoundC(void) +{ + if(FSettings.SndRate) + Mapper5_ESI(); + else + SetWriteHandler(0x5000,0x5015,0); +} + +void Mapper5_ESI(void) +{ + GameExpSound.RChange=MMC5SoundC; + + if(FSettings.SndRate) + { + SetWriteHandler(0x5000,0x5015,Mapper5_SW); + SetWriteHandler(0x5205,0x5206,Mapper5_write); + SetReadHandler(0x5205,0x5206,MMC5_read); + } + if(FCEUGameInfo.type==GIT_NSF) + { + SetWriteHandler(0x5c00,0x5fef,MMC5_ExRAMWr); + SetReadHandler(0x5c00,0x5fef,MMC5_ExRAMRd); + MMC5HackCHRMode=2; + } + else + GameHBIRQHook=MMC5_hb; +} + +static void GenMMC5Reset(void) +{ + mapbyte1[4]=mapbyte1[5]=mapbyte1[6]=mapbyte1[7]=~0; + mapbyte1[0]=mapbyte1[1]=3; + mapbyte4[2]=0; + + mapbyte4[3]=mapbyte4[4]=mapbyte4[5]=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); + + Mapper5_ESI(); + + GameHBIRQHook=MMC5_hb; + FCEU_CheatAddRAM(8,0x6000,WRAM); + FCEU_CheatAddRAM(1,0x5c00,MapperExRAM+0x6000); +} + +void Mapper5_init(void) +{ + AddExState(&MMC5HackSPMode, 1, 0, "SPLM"); + AddExState(&MMC5HackSPScroll, 1, 0, "SPLS"); + AddExState(&MMC5HackSPPage, 1, 0, "SPLP"); + SetupCartPRGMapping(0x10,WRAM,32768,1); + GenMMC5Reset(); + BuildWRAMSizeTable(); + GameStateRestore=Mapper5_StateRestore; +} + +static int m5boo; +static void GenMMC5_Close(void) +{ + UNIFOpenWRAM(UOW_WR,0,1); + if(m5boo<=16) + UNIFWriteWRAM(WRAM,8192); + else + UNIFWriteWRAM(WRAM,32768); + UNIFCloseWRAM(); +} + +static void GenMMC5_Init(int wsize, int battery) +{ + SetupCartPRGMapping(0x10,WRAM,32768,1); + AddExState(WRAM, 8192, 0, "WRAM"); + AddExState(MapperExRAM, 32768, 0, "MEXR"); + AddExState(&IRQCount, 4, 1, "IRQC"); + AddExState(&IRQa, 1, 0, "IRQA"); + AddExState(mapbyte1, 32, 0, "MPBY"); + AddExState(&MMC5HackSPMode, 1, 0, "SPLM"); + AddExState(&MMC5HackSPScroll, 1, 0, "SPLS"); + AddExState(&MMC5HackSPPage, 1, 0, "SPLP"); + + MMC5WRAMsize=wsize/8; + BuildWRAMSizeTable(); + GameStateRestore=Mapper5_StateRestore; + BoardPower=GenMMC5Reset; + + if(battery) + { + UNIFOpenWRAM(UOW_RD,0,1); + if(wsize<=16) + UNIFReadWRAM(WRAM,8192); + else + UNIFReadWRAM(WRAM,32768); + UNIFCloseWRAM(); + BoardClose=GenMMC5_Close; + m5boo=wsize; + } + + MMC5HackVROMMask=CHRmask4[0]; + MMC5HackExNTARAMPtr=MapperExRAM+0x6000; + MMC5Hack=1; + MMC5HackVROMPTR=CHRptr[0]; + MMC5HackCHRMode=0; + MMC5HackSPMode=MMC5HackSPScroll=MMC5HackSPPage=0; + +} + +// ETROM seems to have 16KB of WRAM, ELROM seems to have 8KB +// EWROM seems to have 32KB of WRAM + +// ETROM and EWROM are battery-backed, ELROM isn't. + +void ETROM_Init(void) +{ + GenMMC5_Init(16,1); +} + +void ELROM_Init(void) +{ + GenMMC5_Init(8,0); +} + +void EWROM_Init(void) +{ + GenMMC5_Init(32,1); +} + +void EKROM_Init(void) +{ + GenMMC5_Init(8,1); +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..bec7270 --- /dev/null +++ b/memory.c @@ -0,0 +1,67 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "version.h" +#include "memory.h" +#include "general.h" +#include "svga.h" + +void *FCEU_malloc(uint32 size) +{ + void *ret; + ret=malloc(size); + if(!ret) + FCEU_PrintError(MSG_ERRAM); + return ret; +} + +void FCEU_free(void *ptr) // Might do something with this and FCEU_malloc later... +{ + free(ptr); +} + +void FASTAPASS(3) FCEU_memmove(void *d, void *s, uint32 l) +{ + uint32 x; + int t; + + /* Type really doesn't matter. */ + t=(int)d; + t|=(int)s; + t|=(int)l; + + if(t&3) // Not 4-byte aligned and/or length is not a multiple of 4. + for(x=l;x;x--) // This could be optimized further, though(more tests could be performed). + { + *(uint8*)d=*(uint8 *)s; + ((uint8 *)d)++; + ((uint8 *)s)++; + } + else + for(x=l>>2;x;x--) + { + *(uint32*)d=*(uint32*)s; + ((uint32 *)d)++; + ((uint32 *)s)++; + } +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..122a858 --- /dev/null +++ b/memory.h @@ -0,0 +1,29 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Various macros for faster memory stuff + (at least that's the idea) +*/ + +#define FCEU_dwmemset(d,c,n) {int _x; for(_x=n-4;_x>=0;_x-=4) *(uint32 *)&(d)[_x]=c;} + +void *FCEU_malloc(uint32 size); +void FCEU_free(void *ptr); +void FASTAPASS(3) FCEU_memmove(void *d, void *s, uint32 l); diff --git a/netplay.c b/netplay.c new file mode 100644 index 0000000..fc2a42e --- /dev/null +++ b/netplay.c @@ -0,0 +1,98 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef NETWORK + +#include +#include +#include + +#include "types.h" +#include "svga.h" +#include "netplay.h" + +int netplay=0; /* 1 if we are a server */ + /* 2 if we need a host */ +static uint8 netjoy[4]; // controller stuff. + +static void NetError(void) +{ + FCEU_DispMessage("Network error/connection lost!"); +// FCEUD_PrintError("Network error/connection lost!"); + netplay=0; + FCEUD_NetworkClose(); +} + +void KillNetplay(void) +{ + if(netplay) + { + FCEUD_NetworkClose(); + netplay=0; + } +} + +int InitNetplay(void) +{ + if(!FCEUD_NetworkConnect()) + {NetError();return 0;} + netplay=FSettings.NetworkPlay; + memset(netjoy,0,sizeof(netjoy)); + return 1; +} + +void NetplayUpdate(uint16 *joyp1, uint16 *joyp2) +{ + uint8 buf[5]; + if(netplay==1) // We're the server + { + int t; + + loopo: + + t=FCEUD_NetworkRecvData(&netjoy[2],1,0); + if(!t) {NetError();return;} + if(t!=-1) goto loopo; + + netjoy[0]=*joyp1; + memcpy(buf,netjoy,4); + buf[4]=CommandQueue; + + if(!FCEUD_NetworkSendData(buf,5)) {NetError();return;} + if(CommandQueue) + { + DoCommand(CommandQueue); + CommandQueue=0; + } + } + else if(netplay==2) // We're connected to a host (we're second player) + { + uint8 ja=(*joyp1)|(*joyp1>>8)|(*joyp2)|(*joyp2>>8); + if(!FCEUD_NetworkSendData(&ja,1)) {NetError();return;} + if(!FCEUD_NetworkRecvData(buf,5,1)) {NetError();return;} + + memcpy(netjoy,buf,4); + if(buf[4]) DoCommand(buf[4]); + } + *joyp1=netjoy[0]|(netjoy[1]<<8); + *joyp2=netjoy[2]|(netjoy[3]<<8); + +} +#endif diff --git a/netplay.h b/netplay.h new file mode 100644 index 0000000..d6ddd37 --- /dev/null +++ b/netplay.h @@ -0,0 +1,7 @@ +#ifdef NETWORK +int InitNetplay(void); +void KillNetplay(void); +void NetplayUpdate(uint16 *JS1, uint16 *JS2); + +extern int netplay; +#endif diff --git a/nsf.c b/nsf.c new file mode 100644 index 0000000..cadb726 --- /dev/null +++ b/nsf.c @@ -0,0 +1,410 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "types.h" +#include "x6502.h" +#include "fce.h" +#include "svga.h" +#include "video.h" +#include "sound.h" +#include "ines.h" +#include "nsf.h" +#include "nsfbgnew.h" +#include "general.h" +#include "memory.h" +#include "file.h" +#include "fds.h" +#include "cart.h" +#include "input.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +uint8 SongReload; +uint8 CurrentSong; + +static int sinetable[32]; + + +static uint8 NSFROM[0x30+6]= +{ +/* 0x00 */ + +0x08,0x48,0x8A,0x48,0x98,0x48, /* Store regs */ +0xA9,0xFF, +0x8D,0xF2,0x5F, /* NMI has occured */ +0x68,0xA8,0x68,0xAA,0x68,0x28, +0x40, /* Restore regs */ + +/* 0x12 */ + +0xAD,0xF2,0x5F, /* See if an NMI occured */ +0xF0,0xFB, /* If it hasn't, loop */ + +0xA9,0x00, +0x8D,0xF2,0x5F, /* Clear play pending reg*/ + + +0xAD,0xF0,0x5F, /* See if we need to init. */ +0xF0,0x09, /* If 0, go to JMP */ + +0xAD,0xF1,0x5F, /* Confirm and load A */ +0xAE,0xF3,0x5F, /* Load X with PAL/NTSC byte */ + +0x20,0x00,0x00, /* JSR to init routine */ + +0x20,0x00,0x00, /* JSR to play routine */ + +0x4C,0x12,0x38, /* Loop */ + +0xA2,0xFF,0x9A, /* Initialize the stack pointer. */ +0x4C,0x12,0x38 +}; + +static DECLFR(NSFROMRead) +{ + return (NSFROM-0x3800)[A]; +} + + + +static uint8 *NSFDATA=0; +static int NSFMaxBank; + +static int NSFSize; +static uint8 BSon; +static uint16 PlayAddr; +static uint16 InitAddr; +static uint16 LoadAddr; + +NSF_HEADER NSFHeader; + +void NSFGI(int h) +{ + switch(h) + { + case GI_CLOSE: + if(NSFDATA) {free(NSFDATA);NSFDATA=0;} + break; + case GI_POWER: NSF_init();break; + } +} + +// First 32KB is reserved for sound chip emulation in the iNES mapper code. + +#define WRAM (GameMemBlock+32768) +#define FDSMEM (GameMemBlock+32768) + +static INLINE void BANKSET(uint32 A, uint32 bank) +{ + bank&=NSFMaxBank; + if(NSFHeader.SoundChip&4) + memcpy(FDSMEM+(A-0x6000),NSFDATA+(bank<<12),4096); + else + setprg4(A,bank); +} + +int NSFLoad(int fp) +{ + int x; + + FCEU_fseek(fp,0,SEEK_SET); + FCEU_fread(&NSFHeader,1,0x80,fp); + if (memcmp(NSFHeader.ID,"NESM\x1a",5)) + return 0; + NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0; + + LoadAddr=NSFHeader.LoadAddressLow; + LoadAddr|=NSFHeader.LoadAddressHigh<<8; + + InitAddr=NSFHeader.InitAddressLow; + InitAddr|=NSFHeader.InitAddressHigh<<8; + + PlayAddr=NSFHeader.PlayAddressLow; + PlayAddr|=NSFHeader.PlayAddressHigh<<8; + + NSFSize=FCEU_fgetsize(fp)-0x80; + + NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096); + NSFMaxBank=uppow2(NSFMaxBank); + + if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096))) + return 0; + + FCEU_fseek(fp,0x80,SEEK_SET); + memset(NSFDATA,0x00,NSFMaxBank*4096); + FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp); + + NSFMaxBank--; + + BSon=0; + for(x=0;x<8;x++) + BSon|=NSFHeader.BankSwitch[x]; + + FCEUGameInfo.type=GIT_NSF; + FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_NONE; + + for(x=0;;x++) + { + if(NSFROM[x]==0x20) + { + NSFROM[x+1]=InitAddr&0xFF; + NSFROM[x+2]=InitAddr>>8; + NSFROM[x+4]=PlayAddr&0xFF; + NSFROM[x+5]=PlayAddr>>8; + break; + } + } + + if(NSFHeader.VideoSystem==0) + FCEUGameInfo.vidsys=GIV_NTSC; + else if(NSFHeader.VideoSystem==1) + FCEUGameInfo.vidsys=GIV_PAL; + + { + double fruit=0; + for(x=0;x<32;x++) + { + double ta,no; + + ta=sin(fruit)*7; + ta+=modf(ta,&no); + sinetable[x]=ta; + fruit+=(double)M_PI*2/32; + } + } + GameInterface=NSFGI; + + puts("NSF Loaded. File information:\n"); + printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright); + if(NSFHeader.SoundChip) + { + static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"}; + for(x=0;x<6;x++) + if(NSFHeader.SoundChip&(1<=6) + BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]); + BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); + } + } + else + { + int32 x; + for(x=(LoadAddr&0x7000);x<0x8000;x+=0x1000) + BANKSET(0x8000+x,((x-(LoadAddr&0x7000))>>12)); + } + + SetWriteHandler(0x2000,0x3fff,0); + SetReadHandler(0x2000,0x37ff,0); + SetReadHandler(0x3836,0x3FFF,0); + SetReadHandler(0x3800,0x3835,NSFROMRead); + + SetWriteHandler(0x4020,0x5fff,NSF_write); + SetReadHandler(0x4020,0x5fff,NSF_read); + + + if(NSFHeader.SoundChip&1) { + VRC6_ESI(0); + } else if (NSFHeader.SoundChip&2) { + VRC7_ESI(); + } else if (NSFHeader.SoundChip&4) { + FDSSoundReset(); + } else if (NSFHeader.SoundChip&8) { + Mapper5_ESI(); + } else if (NSFHeader.SoundChip&0x10) { + Mapper19_ESI(); + } else if (NSFHeader.SoundChip&0x20) { + Mapper69_ESI(); + } + CurrentSong=NSFHeader.StartingSong; + SongReload=1; +} + +static uint8 DoUpdateStuff=0; +DECLFW(NSF_write) +{ +switch(A) +{ + case 0x5FF2:if((X.PC&0xF000)==0x3000) DoUpdateStuff=V;break; + + case 0x5FF6: + case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return; + case 0x5FF8: + case 0x5FF9: + case 0x5FFA: + case 0x5FFB: + case 0x5FFC: + case 0x5FFD: + case 0x5FFE: + case 0x5FFF:if(!BSon) return; + A&=0xF; + BANKSET((A*4096),V); + break; +} +} + +DECLFR(NSF_read) +{ + int x; + + if((X.PC&0xF000)==0x3000) + switch(A) + { + case 0x5ff0:x=SongReload;SongReload=0;return x; + case 0x5ff1: + { + memset(RAM,0x00,0x800); + memset(WRAM,0x00,8192); + BWrite[0x4015](0x4015,0xF); + for(x=0;x<0x14;x++) + {if(x!=0x11) BWrite[0x4015](0x4015,0);} + BWrite[0x4015](0x4015,0x0); + for(x=0;x<0x14;x++) + {if(x!=0x11) BWrite[0x4015](0x4015,0);} + BWrite[0x4011](0x4011,0x40); + BWrite[0x4015](0x4015,0xF); + BWrite[0x4017](0x4017,0x40); + if(NSFHeader.SoundChip&4) + BWrite[0x4089](0x4089,0x80); + if(BSon) + { + for(x=0;x<8;x++) + BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); + } + return (CurrentSong-1); + } + case 0x5FF2:return DoUpdateStuff; + case 0x5FF3:return PAL; + } + return 0; +} +static int32 *Bufpl; +void DrawNSF(uint8 *XBuf) +{ + char snbuf[16]; + static int z=0; + int x,y; + uint8 *XBuf2,*tmpb; + + XBuf+=8; + XBuf2=XBuf; + + tmpb=NSFBG+8; + for(y=120;y;y--) + { + uint8 *offs; + + offs=tmpb+sinetable[((z+y)>>2)&31]; + memcpy(XBuf2,offs,256); + memcpy(XBuf2+(120*272),offs,256); + + XBuf2+=272; + tmpb+=272; + } + tmpb=NSFBG+8; + z=(z+1)&127; + + DrawTextTrans(XBuf+10*272+4+(((31-strlen(NSFHeader.SongName))<<2)), 272, NSFHeader.SongName, 38); + DrawTextTrans(XBuf+30*272+4+(((31-strlen(NSFHeader.Artist))<<2)), 272, NSFHeader.Artist, 38); + DrawTextTrans(XBuf+50*272+4+(((31-strlen(NSFHeader.Copyright))<<2)), 272, NSFHeader.Copyright, 38); + + DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, "Song:", 38); + sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs); + DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, snbuf, 38); + + GetSoundBuffer(&Bufpl); + for(x=0;x<256;x++) + XBuf[x+(224-((((Bufpl[x]>>(7)^128)&255)*3)>>3))*272]=38; +} + +void NSFControl(int z) +{ + if(z==1) + { + if(CurrentSong1) CurrentSong--; + } + SongReload=0xFF; +} diff --git a/nsf.h b/nsf.h new file mode 100644 index 0000000..3333201 --- /dev/null +++ b/nsf.h @@ -0,0 +1,53 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct { + char ID[5]; /*NESM^Z*/ + uint8 Version; + uint8 TotalSongs; + uint8 StartingSong; + uint8 LoadAddressLow; + uint8 LoadAddressHigh; + uint8 InitAddressLow; + uint8 InitAddressHigh; + uint8 PlayAddressLow; + uint8 PlayAddressHigh; + uint8 SongName[32]; + uint8 Artist[32]; + uint8 Copyright[32]; + uint8 NTSCspeed[2]; // Unused + uint8 BankSwitch[8]; + uint8 PALspeed[2]; // Unused + uint8 VideoSystem; + uint8 SoundChip; + uint8 Expansion[4]; + uint8 reserve[8]; + } NSF_HEADER; +int NSFLoad(int fp); +DECLFW(NSF_write); +DECLFR(NSF_read); +void NSF_init(void); +extern uint8 CurrentSong; +extern uint8 SongReload; +void DrawNSF(uint8 *XBuf); +void NSFControl(int z); +extern NSF_HEADER NSFHeader; +void NSFDealloc(void); +void NSFDodo(void); diff --git a/nsfbgnew.h b/nsfbgnew.h new file mode 100644 index 0000000..2c55fe1 --- /dev/null +++ b/nsfbgnew.h @@ -0,0 +1,2062 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +uint8 NSFBG[32640] = { +0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,5, +5,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,5,4,4,4,4,4,4, +4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, +4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4, +4,4,4,4,4,4,4,4,4,4,4,4,5,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,5,4,4,4,4,4,4,4,4,4,4,4,4, +5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +4,4,4,4,4,4,4,4,4,4,5,5,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5, +4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4, +4,4,4,4,4,4,4,4,5,5,6,6,6,6,6,6, +6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6, +7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,6, +6,6,6,6,6,6,6,6,6,6,6,6,6,5,4,4, +4,4,4,4,4,4,4,4,4,4,4,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4, +4,4,4,4,4,5,6,6,6,6,6,6,6,6,6,6, +6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7, +7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7, +8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,7, +7,7,7,7,7,7,7,7,7,7,7,7,6,6,6,6, +6,6,6,6,6,6,6,6,5,5,4,4,4,4,4,4, +4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2, +2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,24,24,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,4,4,4,4,4,4,4,4,4,4,4,5,6, +6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7, +7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9, +9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,7,7,7,7,7, +7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6, +6,5,4,4,4,4,4,4,4,4,4,0,0,0,0,0, +0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1, +1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3, +3,3,3,3,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,3,3,3,3,3,2,2,2,2,2,2,2,2, +2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1, +1,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +4,4,4,4,4,5,6,6,6,6,6,6,6,6,6,6, +6,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8, +8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +10,10,10,10,10,10,10,11,11,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,8, +8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,6, +6,6,6,6,6,6,6,6,5,4,4,4,4,4,4,4, +4,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1, +1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3, +3,3,24,24,24,24,24,24,24,24,24,24,24,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,24,24,24,24,24,24,24,24,24, +24,24,3,3,3,2,2,2,2,2,2,2,2,2,2,2, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,0,4,4,4,4,4,4,4,4,5,6,6,6,6,6, +6,6,6,6,7,7,7,7,7,7,7,7,7,7,8,8, +8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,11,11,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +12,12,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,11,9,9,9,9,9,9,9, +9,9,9,9,8,8,8,8,8,8,8,8,8,8,7,7, +7,7,7,7,7,7,6,6,6,6,6,6,6,6,5,4, +4,4,4,4,4,4,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3, +3,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,24,24,24,24,24,24,24,24,3,3,3,2, +2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1, +0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,6, +6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7, +8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,9,9,9,11,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,10,10,10,10,10,10,10,10, +10,10,10,11,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,7,7,7,7,7,7,7,7,6,6,6,6, +6,6,5,4,4,4,4,4,4,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3, +3,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25, +25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,25,25,25,25,25,25,25,25,25,25,25,24,24, +24,24,24,24,3,3,3,2,2,2,2,2,2,2,1,1, +1,1,1,1,1,0,0,0,0,0,0,0,4,4,4,4, +4,4,5,6,6,6,6,6,6,6,7,7,7,7,7,7, +7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,9,11,10,10,10,10,10,10,10,10,10,10,10,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,13,13,13,13,12,12,12,12,12,12,12,12,12,12, +12,12,12,10,10,10,10,10,10,10,10,9,9,9,9,9, +9,9,9,8,8,8,8,8,8,8,7,7,7,7,7,7, +6,6,6,6,6,6,5,4,4,4,4,4,0,0,0,0, +0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2, +3,3,24,24,24,24,24,25,25,25,25,25,25,25,25,26, +26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,27,26,26,26,26,26,26,26,26,26,26,26,25,25,25, +25,25,25,25,25,24,24,24,24,24,24,3,3,2,2,2, +2,2,2,1,1,1,1,1,1,1,0,0,0,0,0,0, +4,4,4,4,4,5,6,6,6,6,6,6,7,7,7,7, +7,7,7,8,8,8,8,8,8,9,9,9,9,9,9,9, +9,10,10,10,10,10,10,10,10,10,12,12,12,12,12,12, +12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,14,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,12,12,12,12,12,12,12,12,12,10,10,10,10,10,10, +10,11,9,9,9,9,9,9,8,8,8,8,8,8,7,7, +7,7,7,7,6,6,6,6,6,6,4,4,4,4,4,0, +0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2, +2,3,24,24,24,24,24,25,25,25,25,25,25,25,26,26, +26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27, +27,27,27,29,29,29,29,29,29,29,29,29,29,29,29,29, +29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29, +27,27,27,27,27,27,27,27,27,27,27,27,26,26,26,26, +26,26,26,26,25,25,25,25,25,25,25,24,24,24,24,24, +3,3,2,2,2,2,2,2,1,1,1,1,1,1,0,0, +0,0,0,4,4,4,4,4,5,6,6,6,6,6,7,7, +7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9, +9,10,10,10,10,10,10,10,12,12,12,12,12,12,12,12, +12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +13,13,13,13,13,13,13,13,13,12,12,12,12,12,12,12, +12,10,10,10,10,10,10,9,9,9,9,9,9,8,8,8, +8,8,7,7,7,7,7,7,6,6,6,6,6,4,4,4, +4,4,0,0,0,0,0,1,1,1,1,1,2,2,2,2, +2,2,3,24,24,24,24,25,25,25,25,25,25,26,26,26, +26,26,26,26,26,27,27,27,27,27,27,27,27,29,29,29, +29,29,28,28,28,28,28,28,28,28,28,28,30,30,30,30, +30,30,30,30,30,30,30,30,30,30,30,30,28,28,28,28, +28,28,28,28,28,29,29,29,29,29,29,27,27,27,27,27, +27,27,27,26,26,26,26,26,26,26,25,25,25,25,25,25, +25,24,24,24,24,3,2,2,2,2,2,2,1,1,1,1, +1,0,0,0,0,0,4,4,4,4,4,6,6,6,6,6, +7,7,7,7,7,8,8,8,8,8,8,9,9,9,9,9, +9,10,10,10,10,10,10,12,12,12,12,12,12,12,12,13, +13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, +15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,15,15,15,15,14,14, +14,14,14,14,14,14,14,14,13,13,13,13,13,13,13,13, +12,12,12,12,12,12,10,10,10,10,10,11,9,9,9,9, +9,8,8,8,8,8,7,7,7,7,7,6,6,6,6,5, +4,4,4,4,0,0,0,0,0,1,1,1,1,1,2,2, +2,2,2,3,24,24,24,24,25,25,25,25,25,26,26,26, +26,26,26,26,27,27,27,27,27,27,27,29,29,29,29,28, +28,28,28,28,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,30,30,30,30,30,28,28,28,28,28,29,29, +29,29,27,27,27,27,27,27,27,26,26,26,26,26,26,25, +25,25,25,25,25,24,24,24,24,3,2,2,2,2,2,1, +1,1,1,1,0,0,0,0,0,4,4,4,4,6,6,6, +6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9, +9,11,10,10,10,10,10,12,12,12,12,12,12,13,13,13, +13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,15, +16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,16,16,16,16,16,16, +16,16,16,16,15,15,14,14,14,14,14,14,14,14,13,13, +13,13,13,13,13,12,12,12,12,12,10,10,10,10,10,11, +9,9,9,9,8,8,8,8,8,7,7,7,7,7,6,6, +6,6,4,4,4,4,0,0,0,0,0,1,1,1,1,2, +2,2,2,2,3,24,24,24,24,25,25,25,25,25,26,26, +26,26,26,27,27,27,27,27,27,29,29,29,29,28,28,28, +28,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31, +31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,31,30,30,30,30,30,30,30,30,30,30, +30,28,28,28,28,29,29,29,27,27,27,27,27,27,26,26, +26,26,26,26,25,25,25,25,25,24,24,24,3,3,2,2, +2,2,1,1,1,1,1,0,0,0,0,4,4,4,4,5, +6,6,6,6,7,7,7,7,8,8,8,8,8,9,9,9, +9,9,10,10,10,10,10,12,12,12,12,12,12,13,13,13, +13,13,13,14,14,14,14,14,14,14,14,15,15,16,16,16, +16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,16,16,16,16,16,16,16,15,15,14,14,14, +14,14,14,14,13,13,13,13,13,12,12,12,12,12,10,10, +10,10,10,9,9,9,9,9,8,8,8,8,7,7,7,7, +6,6,6,6,5,4,4,4,4,0,0,0,0,1,1,1, +1,2,2,2,2,3,24,24,24,24,25,25,25,25,26,26, +26,26,26,27,27,27,27,27,27,29,29,29,28,28,28,30, +30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32, +32,31,31,31,31,31,31,31,31,31,31,31,31,31,31,30, +30,30,30,30,30,30,30,28,28,28,29,29,29,27,27,27, +27,27,27,26,26,26,26,26,25,25,25,25,24,24,24,24, +3,2,2,2,2,1,1,1,1,0,0,0,0,4,4,4, +4,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9, +9,9,9,10,10,10,10,10,12,12,12,12,12,13,13,13, +13,13,14,14,14,14,14,14,14,15,15,16,16,16,16,16, +17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,17,17,17,17,17,17,17,17,17,17,16,16,16,16,16, +16,15,14,14,14,14,14,14,13,13,13,13,13,12,12,12, +12,12,10,10,10,10,9,9,9,9,9,8,8,8,8,7, +7,7,7,6,6,6,6,4,4,4,4,0,0,0,0,1, +1,1,2,2,2,2,2,3,24,24,24,25,25,25,25,26, +26,26,26,26,27,27,27,27,27,29,29,28,28,28,30,30, +30,30,30,30,30,31,31,31,31,31,31,31,31,31,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31, +31,31,31,31,31,30,30,30,30,30,30,30,28,28,28,29, +29,27,27,27,27,27,26,26,26,26,26,25,25,25,25,24, +24,24,24,3,2,2,2,2,1,1,1,1,0,0,0,0, +4,4,4,5,6,6,6,6,7,7,7,8,8,8,8,8, +9,9,9,9,10,10,10,10,12,12,12,12,12,13,13,13, +13,13,14,14,14,14,14,14,15,16,16,16,16,16,16,17, +17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,17,17,17,17,17,17,17, +17,16,16,16,16,16,15,14,14,14,14,14,13,13,13,13, +13,12,12,12,12,10,10,10,10,9,9,9,9,8,8,8, +8,7,7,7,7,6,6,6,6,4,4,4,4,0,0,0, +1,1,1,1,2,2,2,2,3,24,24,24,25,25,25,25, +26,26,26,26,27,27,27,27,27,29,29,28,28,28,30,30, +30,30,30,30,31,31,31,31,31,31,31,32,32,32,32,32, +32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,32,32,32,32,32,32,32,32, +32,32,32,31,31,31,31,31,31,31,30,30,30,30,30,30, +28,28,28,29,29,27,27,27,27,27,26,26,26,26,25,25, +25,25,24,24,24,3,2,2,2,2,1,1,1,1,0,0, +0,0,4,4,4,5,6,6,6,7,7,7,7,8,8,8, +8,9,9,9,9,10,10,10,10,12,12,12,12,13,13,13, +13,13,14,14,14,14,14,15,15,16,16,16,16,16,17,17, +17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,18,18,18,18,18,18,18,18,18,17, +17,17,17,17,17,16,16,16,16,16,15,14,14,14,14,14, +13,13,13,13,12,12,12,12,10,10,10,10,9,9,9,9, +8,8,8,8,7,7,7,6,6,6,6,4,4,4,4,0, +0,0,1,1,1,1,2,2,2,3,24,24,24,25,25,25, +25,26,26,26,26,27,27,27,27,27,29,29,28,28,30,30, +30,30,30,30,31,31,31,31,31,31,32,32,32,32,32,32, +32,32,33,33,33,33,33,33,33,33,33,35,35,35,35,35, +35,35,35,35,35,35,35,33,33,33,33,33,33,33,33,33, +32,32,32,32,32,32,32,32,32,31,31,31,31,31,31,30, +30,30,30,30,28,28,28,29,29,27,27,27,27,26,26,26, +26,25,25,25,25,24,24,24,3,2,2,2,2,1,1,1, +0,0,0,0,4,4,4,6,6,6,6,7,7,7,7,8, +8,8,9,9,9,9,10,10,10,10,12,12,12,12,13,13, +13,13,13,14,14,14,14,15,15,16,16,16,16,17,17,17, +17,17,17,18,18,18,18,18,18,18,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,18,18,18,18, +18,18,18,18,17,17,17,17,17,17,16,16,16,15,15,14, +14,14,14,13,13,13,13,12,12,12,12,10,10,10,10,9, +9,9,9,8,8,8,7,7,7,7,6,6,6,4,4,4, +0,0,0,0,1,1,1,2,2,2,2,3,24,24,24,25, +25,25,26,26,26,26,27,27,27,27,29,29,28,28,28,30, +30,30,30,30,31,31,31,31,31,32,32,32,32,32,32,32, +33,33,33,33,33,33,35,35,35,35,35,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35, +35,33,33,33,33,33,32,32,32,32,32,32,32,31,31,31, +31,31,31,30,30,30,30,30,28,28,29,29,27,27,27,27, +26,26,26,26,25,25,25,25,24,24,3,2,2,2,2,1, +1,1,0,0,0,0,4,4,4,6,6,6,6,7,7,7, +8,8,8,8,9,9,9,11,10,10,10,12,12,12,12,13, +13,13,13,14,14,14,14,14,15,16,16,16,16,17,17,17, +17,17,18,18,18,18,18,18,18,19,19,19,19,19,19,19, +19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,19,19,19,19,19,19,19,19, +19,18,18,18,18,18,18,18,17,17,17,17,17,16,16,16, +16,15,14,14,14,14,13,13,13,13,12,12,12,12,10,10, +10,9,9,9,9,8,8,8,7,7,7,7,6,6,6,4, +4,4,0,0,0,0,1,1,1,2,2,2,2,24,24,24, +25,25,25,25,26,26,26,27,27,27,27,29,29,28,28,30, +30,30,30,30,31,31,31,31,31,32,32,32,32,32,32,33, +33,33,33,33,35,35,35,34,34,34,34,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,35,35,35,35,33,33,33,33,32,32,32,32,32, +32,31,31,31,31,31,30,30,30,30,30,28,28,29,29,27, +27,27,27,26,26,26,26,25,25,25,24,24,24,3,2,2, +2,1,1,1,1,0,0,0,4,4,4,6,6,6,6,7, +7,7,8,8,8,9,9,9,9,10,10,10,10,12,12,12, +13,13,13,13,14,14,14,14,14,15,16,16,16,17,17,17, +17,17,17,18,18,18,18,18,19,19,19,19,19,19,19,19, +20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,21,21,21,20,20,20,20,19,19, +19,19,19,19,19,19,18,18,18,18,18,18,17,17,17,17, +17,16,16,16,15,14,14,14,14,13,13,13,13,12,12,12, +10,10,10,10,9,9,9,8,8,8,8,7,7,7,6,6, +6,4,4,4,0,0,0,0,1,1,1,2,2,2,3,24, +24,24,25,25,25,26,26,26,26,27,27,27,29,29,28,28, +30,30,30,30,30,31,31,31,31,32,32,32,32,32,32,33, +33,33,33,35,35,35,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,36,36,36,36,36,36,36,34,34,34,34,34, +34,34,34,34,34,34,34,34,35,35,35,33,33,33,33,32, +32,32,32,32,32,31,31,31,31,31,30,30,30,30,28,28, +29,29,27,27,27,27,26,26,26,25,25,25,25,24,24,3, +2,2,2,1,1,1,1,0,0,0,4,4,4,6,6,6, +7,7,7,7,8,8,8,9,9,9,11,10,10,10,12,12, +12,13,13,13,13,14,14,14,14,15,16,16,16,16,17,17, +17,17,17,18,18,18,18,18,18,19,19,19,19,19,19,20, +20,20,20,21,21,21,21,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,21,21,21,21,20, +20,20,20,19,19,19,19,19,19,19,18,18,18,18,18,17, +17,17,17,17,16,16,16,15,14,14,14,14,13,13,13,12, +12,12,12,10,10,10,9,9,9,9,8,8,8,7,7,7, +6,6,6,4,4,4,0,0,0,1,1,1,1,2,2,2, +3,24,24,25,25,25,26,26,26,26,27,27,27,27,29,28, +28,30,30,30,30,30,31,31,31,31,32,32,32,32,32,33, +33,33,33,35,35,34,34,34,34,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,34,34,34,34,34,34,34,34,34,35,35,33, +33,33,33,32,32,32,32,32,31,31,31,31,31,30,30,30, +30,28,28,29,29,27,27,27,26,26,26,26,25,25,25,24, +24,24,2,2,2,2,1,1,1,0,0,0,4,4,4,6, +6,6,7,7,7,8,8,8,8,9,9,9,10,10,10,12, +12,12,12,13,13,13,13,14,14,14,15,16,16,16,16,17, +17,17,17,17,18,18,18,18,18,19,19,19,19,19,19,20, +20,20,21,21,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,21,21,20,20,20,19,19,19,19,19,19,18,18,18, +18,18,17,17,17,17,17,16,16,16,15,14,14,14,13,13, +13,13,12,12,12,10,10,10,11,9,9,9,8,8,8,7, +7,7,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,2,24,24,24,25,25,25,26,26,26,27,27,27,27,29, +29,28,28,30,30,30,30,31,31,31,31,32,32,32,32,32, +33,33,33,35,35,35,34,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,35,35,33,33,33,32,32,32,32,32,31,31,31,31,31, +30,30,30,30,28,28,29,27,27,27,27,26,26,26,25,25, +25,24,24,24,3,2,2,2,1,1,1,0,0,0,4,4, +4,6,6,6,7,7,7,8,8,8,9,9,9,9,10,10, +10,12,12,12,13,13,13,13,14,14,14,14,15,16,16,16, +16,17,17,17,17,18,18,18,18,18,19,19,19,19,19,19, +20,20,21,21,21,21,21,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,21,21,21,21,21,20,20,20,19,19,19,19,19, +18,18,18,18,18,17,17,17,17,16,16,16,15,14,14,14, +14,13,13,13,12,12,12,12,10,10,10,9,9,9,8,8, +8,7,7,7,6,6,6,5,4,4,0,0,0,1,1,1, +2,2,2,3,24,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,30,31,31,31,31,32,32,32,32, +32,33,33,33,35,35,34,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,34,34,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,32,31,31, +31,31,30,30,30,30,28,28,29,29,27,27,27,26,26,26, +25,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,4,6,6,6,7,7,7,8,8,8,9,9,9,11, +10,10,10,12,12,12,13,13,13,14,14,14,14,15,16,16, +16,16,17,17,17,17,18,18,18,18,18,19,19,19,19,19, +20,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,20,20,20,19,19, +19,19,19,18,18,18,18,18,17,17,17,17,16,16,16,15, +14,14,14,13,13,13,13,12,12,12,10,10,10,9,9,9, +8,8,8,7,7,7,6,6,6,5,4,4,0,0,0,1, +1,1,2,2,2,3,24,24,25,25,25,26,26,26,26,27, +27,27,29,29,28,28,30,30,30,31,31,31,31,32,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,36,36,36,36,36,36,36,36,36,36, +36,34,34,34,34,34,34,35,35,33,33,33,32,32,32,32, +32,31,31,31,31,30,30,30,30,28,28,29,27,27,27,27, +26,26,26,25,25,25,24,24,3,2,2,2,1,1,1,0, +0,0,4,4,4,6,6,6,7,7,7,8,8,8,9,9, +9,10,10,10,12,12,12,12,13,13,13,14,14,14,14,16, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,20,20, +20,19,19,19,19,19,18,18,18,18,17,17,17,17,16,16, +16,15,14,14,14,14,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,6,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36, +36,36,36,36,34,34,34,34,34,34,35,35,33,33,33,32, +32,32,32,31,31,31,31,30,30,30,30,28,28,29,29,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,10,10, +10,11,9,9,9,8,8,8,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,14,14,15,16,16,17,17,17,17,18,18,18,18,18, +19,19,19,19,19,20,20,21,21,21,21,22,22,22,22,22, +22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22, +21,21,21,21,20,20,19,19,19,19,19,18,18,18,18,17, +17,17,17,16,16,16,15,14,14,14,13,13,13,13,12,12, +12,10,10,10,9,9,9,8,8,8,7,7,7,6,6,6, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,25,25, +25,26,26,26,27,27,27,27,29,28,28,30,30,30,30,31, +31,31,32,32,32,32,32,33,33,35,35,34,34,34,34,34, +34,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,36,36,36,36,36,36,36,36,34,34,34,34,34,35, +35,33,33,33,32,32,32,32,31,31,31,31,30,30,30,30, +28,28,29,27,27,27,26,26,26,25,25,25,25,24,24,3, +2,2,1,1,1,0,0,0,4,4,4,6,6,6,7,7, +7,8,8,8,9,9,9,10,10,10,12,12,12,13,13,13, +13,13,14,14,14,14,15,16,16,17,17,17,17,18,18,18, +18,18,19,19,19,19,20,20,20,21,21,21,22,22,22,22, +22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,22,22,22,22,22,22, +22,22,22,21,21,21,20,20,20,19,19,19,19,18,18,18, +18,18,17,17,17,17,16,16,15,14,14,14,14,13,13,13, +12,12,12,10,10,10,9,9,9,8,8,8,7,7,7,6, +6,6,4,4,4,0,0,1,1,1,2,2,2,3,24,24, +25,25,25,26,26,26,27,27,27,27,29,28,28,30,30,30, +30,31,31,31,32,32,32,32,33,33,33,35,35,34,34,34, +34,34,34,36,36,36,36,36,36,36,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,36,36,36,36,36,36,36,34,34,34,34, +34,34,35,35,33,33,33,32,32,32,32,31,31,31,30,30, +30,30,28,28,29,27,27,27,27,26,26,26,25,25,25,24, +24,3,2,2,2,1,1,1,0,0,4,4,4,6,6,6, +7,7,7,8,8,8,9,9,9,10,10,10,12,12,12,13, +12,13,13,13,13,14,14,14,15,16,16,16,17,17,17,18, +18,18,18,18,19,19,19,19,20,20,20,21,21,21,22,22, +22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22, +22,22,22,22,22,21,21,21,21,20,20,19,19,19,19,19, +18,18,18,18,17,17,17,17,16,16,16,14,14,14,14,13, +13,13,12,12,12,10,10,10,9,9,9,8,8,8,7,7, +7,6,6,6,4,4,4,0,0,0,1,1,1,2,2,3, +24,24,25,25,25,26,26,26,26,27,27,27,29,28,28,30, +30,30,30,31,31,31,32,32,32,32,32,33,33,35,35,34, +34,34,34,34,34,36,36,36,36,36,36,36,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,36,36,36,36,36,36,36,36,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,31,31,31, +31,30,30,30,28,28,29,29,27,27,27,26,26,26,25,25, +25,24,24,3,2,2,2,1,1,1,0,0,0,4,4,6, +6,6,7,7,7,8,8,8,9,9,9,10,10,10,12,12, +12,12,12,12,13,13,13,14,14,14,15,16,16,16,17,17, +17,17,18,18,18,18,19,19,19,19,19,20,20,21,21,21, +21,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22, +22,22,22,22,22,22,22,21,21,21,21,20,20,19,19,19, +19,19,18,18,18,18,17,17,17,17,16,16,16,15,14,14, +14,13,13,13,12,12,12,10,10,10,11,9,9,8,8,8, +7,7,7,6,6,6,4,4,4,0,0,0,1,1,1,2, +2,2,3,24,24,25,25,25,26,26,26,27,27,27,29,28, +28,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33, +35,34,34,34,34,34,34,36,36,36,36,36,36,36,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36, +36,34,34,34,34,34,35,35,33,33,33,32,32,32,32,31, +31,31,31,30,30,30,30,28,29,29,27,27,27,26,26,26, +25,25,25,24,24,3,2,2,2,1,1,1,0,0,0,4, +4,5,6,6,6,7,7,7,8,8,9,9,9,11,10,10, +10,10,10,12,12,12,13,13,13,14,14,14,14,16,16,16, +17,17,17,17,18,18,18,18,19,19,19,19,19,20,20,21, +21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,22,22,22,22,22,22,22,22,22,21,21,21,20,20,19, +19,19,19,19,18,18,18,18,17,17,17,17,16,16,16,15, +14,14,14,13,13,13,13,12,12,12,10,10,10,9,9,9, +8,8,8,7,7,7,6,6,5,4,4,0,0,0,1,1, +1,2,2,2,3,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,31,31,31,31,32,32,32,32,33, +33,33,35,35,34,34,34,34,34,36,36,36,36,36,36,36, +36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36, +36,36,36,34,34,34,34,34,34,35,35,33,33,32,32,32, +32,31,31,31,31,30,30,30,30,28,28,29,27,27,27,26, +26,26,26,25,25,25,24,24,3,2,2,1,1,1,0,0, +0,4,4,4,6,6,6,7,7,7,8,8,8,9,9,9, +9,9,10,10,10,12,12,12,13,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,22,22,22,22,22,22,22,22,22,21,21,21,20, +20,20,19,19,19,19,18,18,18,18,18,17,17,17,17,16, +16,15,14,14,14,14,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,6,6,6,4,4,4,0,0, +0,1,1,1,2,2,3,24,24,25,25,25,26,26,26,26, +27,27,27,29,28,28,30,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,31,31,31,31,30,30,30,28,28,29,27,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,6,6,6,7,7,7,8,8,8,9, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,16,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,20,20,20,21,21,21,22,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,20,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +7,8,8,8,9,9,9,10,10,10,12,12,12,12,13,13, +13,14,14,14,15,16,16,16,17,17,17,17,18,18,18,18, +19,19,19,19,19,20,20,20,21,21,21,21,22,22,22,22, +22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,22,22, +21,21,21,20,20,20,19,19,19,19,19,18,18,18,18,17, +17,17,17,16,16,16,15,14,14,14,13,13,13,13,12,12, +12,10,10,10,9,9,9,8,8,8,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,2,2,3,24,24,25,25, +25,25,26,26,26,27,27,27,29,29,28,30,30,30,30,31, +31,31,31,32,32,32,32,33,33,33,35,35,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,36,36,36,36,36,36,36,36,34,34,34,34,34,34,35, +35,33,33,33,32,32,32,32,31,31,31,31,30,30,30,30, +28,28,29,27,27,27,26,26,26,26,25,25,25,24,24,3, +2,2,2,1,1,1,0,0,4,4,4,6,6,6,7,7, +6,7,7,7,8,8,8,9,9,9,10,10,10,12,12,12, +13,13,13,13,14,14,14,15,16,16,16,17,17,17,17,18, +18,18,18,19,19,19,19,19,20,20,20,21,21,21,21,22, +22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23, +23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22, +22,21,21,21,21,20,20,20,19,19,19,19,19,18,18,18, +18,17,17,17,17,16,16,16,15,14,14,14,14,13,13,13, +12,12,12,10,10,10,9,9,9,8,8,8,7,7,7,6, +6,6,5,4,4,0,0,0,1,1,1,2,2,2,3,24, +24,25,25,25,26,26,26,27,27,27,27,29,28,28,30,30, +30,30,31,31,31,31,32,32,32,32,33,33,33,35,35,34, +34,34,34,34,34,36,36,36,36,36,36,36,36,36,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,36,36,36,36,36,36,36,36,34,34,34,34,34, +34,35,35,33,33,33,32,32,32,32,31,31,31,31,30,30, +30,30,28,28,29,29,27,27,27,26,26,26,25,25,25,24, +24,3,2,2,2,1,1,1,0,0,0,4,4,4,6,6, +5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10, +12,12,12,13,13,13,14,14,14,14,15,16,16,16,17,17, +17,17,18,18,18,18,19,19,19,19,19,19,20,20,21,21, +21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,22,22,22,22,22,22,22,22,22, +22,22,22,21,21,21,21,20,20,20,19,19,19,19,19,18, +18,18,18,18,17,17,17,17,16,16,16,15,14,14,14,13, +13,13,12,12,12,12,10,10,10,9,9,9,8,8,8,7, +7,7,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,3,24,24,25,25,25,26,26,26,27,27,27,27,29,28, +28,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33, +35,35,34,34,34,34,34,34,36,36,36,36,36,36,36,36, +36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,36,36,36,36,36,36,36,36,36,34,34,34, +34,34,34,35,35,33,33,33,32,32,32,32,32,31,31,31, +31,30,30,30,28,28,29,29,27,27,27,26,26,26,26,25, +25,25,24,24,3,2,2,2,1,1,1,0,0,0,4,4, +4,4,4,6,6,6,7,7,7,8,8,8,9,9,9,11, +10,10,10,12,12,12,13,13,13,14,14,14,14,15,16,16, +16,17,17,17,17,18,18,18,18,19,19,19,19,19,19,20, +20,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,21,21,21,21,20,20,20,19,19,19,19, +19,18,18,18,18,18,17,17,17,17,16,16,16,15,14,14, +14,14,13,13,13,12,12,12,10,10,10,9,9,9,9,8, +8,8,7,7,7,6,6,6,4,4,4,0,0,0,1,1, +1,2,2,2,24,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,30,31,31,31,31,32,32,32,32, +33,33,33,35,35,34,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,36,36,36,36,36,36,36,36,36,36,34, +34,34,34,34,34,34,35,35,33,33,33,32,32,32,32,31, +31,31,31,30,30,30,30,28,28,29,27,27,27,27,26,26, +26,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +0,0,0,4,4,4,6,6,6,7,7,7,8,8,8,9, +9,9,11,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,20,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,21,20,20,19,19, +19,19,19,19,18,18,18,18,17,17,17,17,17,16,16,16, +15,14,14,14,13,13,13,13,12,12,12,10,10,10,9,9, +9,8,8,8,7,7,7,6,6,6,5,4,4,0,0,0, +1,1,1,2,2,2,3,24,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37, +37,37,37,37,37,36,36,36,36,36,36,36,36,36,36,36, +36,34,34,34,34,34,34,34,35,35,33,33,33,32,32,32, +32,32,31,31,31,31,30,30,30,28,28,29,29,27,27,27, +26,26,26,26,25,25,25,24,24,3,2,2,2,1,1,1, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,12,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,18, +19,19,19,19,19,19,20,20,21,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,21,21,21,21,21,20,20, +19,19,19,19,19,19,18,18,18,18,18,17,17,17,17,16, +16,16,15,14,14,14,14,13,13,13,12,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,25, +26,26,26,27,27,27,29,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,32,33,33,33,35,35,34,34,34,34, +34,34,34,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,35,35,33,33,33,32, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,25,24,24,3,2,2,2, +2,2,2,1,1,1,0,0,0,4,4,5,6,6,6,7, +7,7,8,8,8,9,9,9,10,10,10,12,12,12,12,13, +13,13,14,14,14,14,15,16,16,16,17,17,17,17,18,18, +18,18,18,19,19,19,19,19,19,20,20,20,21,21,21,21, +21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,18,17,17,17, +17,17,16,16,16,15,14,14,14,13,13,13,13,12,12,12, +10,10,10,11,9,9,9,8,8,8,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,2,2,2,3,24,24,25, +25,25,25,26,26,26,27,27,27,29,29,28,28,30,30,30, +30,31,31,31,31,32,32,32,32,32,33,33,33,35,35,35, +34,34,34,34,34,34,34,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,34,35,35,33,33, +33,33,32,32,32,32,31,31,31,31,31,30,30,30,30,28, +28,29,27,27,27,27,26,26,26,25,25,25,24,24,24,3, +24,3,2,2,2,1,1,1,1,0,0,0,4,4,5,6, +6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,12, +12,12,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,17,18,18,18,18,18,19,19,19,19,19,19,20,20,20, +21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,21,21,21,21, +21,21,20,20,20,19,19,19,19,19,19,18,18,18,18,18, +17,17,17,17,16,16,16,15,14,14,14,14,13,13,13,13, +12,12,12,10,10,10,9,9,9,9,8,8,8,7,7,7, +6,6,6,4,4,4,0,0,0,1,1,1,2,2,2,3, +24,24,25,25,25,25,26,26,26,27,27,27,27,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,32,33,33,33, +33,35,35,34,34,34,34,34,34,34,34,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,34,34,35, +35,33,33,33,32,32,32,32,32,31,31,31,31,30,30,30, +30,28,28,29,29,27,27,27,27,26,26,26,25,25,25,24, +25,25,24,24,3,2,2,2,1,1,1,0,0,0,0,4, +4,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10, +10,10,12,12,12,13,13,13,13,14,14,14,15,16,16,16, +17,17,17,17,17,18,18,18,18,18,19,19,19,19,19,19, +20,20,20,21,21,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,21,21,20,20,20,19,19,19,19,19,19,18,18,18, +18,18,18,17,17,17,17,16,16,16,15,14,14,14,14,13, +13,13,12,12,12,12,10,10,10,9,9,9,8,8,8,8, +7,7,7,6,6,6,4,4,4,0,0,0,1,1,1,2, +2,2,3,24,24,25,25,25,25,26,26,26,27,27,27,27, +29,28,28,30,30,30,30,31,31,31,31,31,32,32,32,32, +32,33,33,33,35,35,34,34,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,35,35,33,33,33,33,32,32,32,32,32,31,31,31,31, +30,30,30,30,28,28,29,29,27,27,27,26,26,26,26,25, +26,26,25,25,25,24,24,3,2,2,2,1,1,1,0,0, +0,0,4,4,5,6,6,6,7,7,7,8,8,8,9,9, +9,11,10,10,10,12,12,12,13,13,13,13,14,14,14,14, +16,16,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,19,19,20,20,20,21,21,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +21,21,21,21,21,21,20,20,20,20,19,19,19,19,19,19, +18,18,18,18,18,17,17,17,17,17,16,16,16,15,14,14, +14,14,13,13,13,12,12,12,12,10,10,10,9,9,9,8, +8,8,7,7,7,7,6,6,6,4,4,4,0,0,0,1, +1,1,2,2,2,3,24,24,25,25,25,25,26,26,26,27, +27,27,27,29,28,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,32,33,33,33,35,35,35,34,34,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,34,34,34,34,34, +34,34,34,35,35,35,33,33,33,32,32,32,32,32,31,31, +31,31,31,30,30,30,30,28,28,29,29,27,27,27,26,26, +27,26,26,26,25,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,4,5,6,6,6,7,7,7,8,8, +8,9,9,9,11,10,10,10,12,12,12,13,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,18, +18,19,19,19,19,19,19,20,20,20,21,21,21,21,21,21, +21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,21,21,21,21,21,21,21,20,20,20,19,19,19,19, +19,19,19,18,18,18,18,18,17,17,17,17,17,16,16,16, +15,14,14,14,14,13,13,13,12,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,24,25,25,25,26, +26,26,27,27,27,27,29,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,32,33,33,33,33,35,35,34,34,34, +34,34,34,34,34,34,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,34,34, +34,34,34,34,34,34,35,35,35,33,33,33,32,32,32,32, +32,31,31,31,31,31,30,30,30,30,28,28,29,27,27,27, +27,27,27,27,26,26,26,25,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,0,4,4,4,5,6,6,6,7,7, +7,8,8,8,9,9,9,11,10,10,10,12,12,12,13,13, +13,13,14,14,14,14,15,16,16,16,17,17,17,17,18,18, +18,18,18,18,19,19,19,19,19,19,20,20,20,20,21,21, +21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,21,21,21,21,21,21,21,20,20,20,19, +19,19,19,19,19,19,18,18,18,18,18,17,17,17,17,16, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,7,6,6,6,4, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,24,25, +25,25,26,26,26,27,27,27,27,29,29,28,28,30,30,30, +30,31,31,31,31,32,32,32,32,32,33,33,33,33,35,35, +34,34,34,34,34,34,34,34,34,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +34,34,34,34,34,34,34,34,34,35,35,33,33,33,33,32, +32,32,32,32,31,31,31,31,30,30,30,30,30,28,28,29, +28,28,29,27,27,27,27,26,26,26,25,25,25,25,24,24, +3,2,2,2,1,1,1,0,0,0,4,4,4,5,6,6, +6,7,7,7,8,8,8,9,9,9,11,10,10,10,12,12, +12,13,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,17,18,18,18,18,18,19,19,19,19,19,19,20,20,20, +20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,21,21,21,21,21,21,21,20,20, +20,20,19,19,19,19,19,19,18,18,18,18,18,18,17,17, +17,17,16,16,16,15,14,14,14,14,13,13,13,13,12,12, +12,10,10,10,10,9,9,9,8,8,8,7,7,7,7,6, +6,6,4,4,4,0,0,0,1,1,1,2,2,2,3,24, +24,24,25,25,25,26,26,26,27,27,27,27,29,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,32,33,33,33, +33,35,35,35,34,34,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,34,34,35,35,33,33, +33,33,32,32,32,32,32,31,31,31,31,30,30,30,30,28, +30,30,28,28,28,29,27,27,27,27,26,26,26,25,25,25, +25,24,24,3,2,2,2,1,1,1,0,0,0,4,4,4, +6,6,6,6,7,7,7,8,8,8,9,9,9,11,10,10, +10,12,12,12,13,13,13,13,14,14,14,14,15,16,16,16, +17,17,17,17,18,18,18,18,18,18,19,19,19,19,19,19, +20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,21,21, +21,20,20,20,20,19,19,19,19,19,19,18,18,18,18,18, +18,17,17,17,17,16,16,16,15,14,14,14,14,13,13,13, +13,12,12,12,10,10,10,10,9,9,9,8,8,8,7,7, +7,6,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,3,24,24,25,25,25,25,26,26,26,27,27,27,27,29, +29,28,28,30,30,30,30,31,31,31,31,32,32,32,32,32, +33,33,33,33,35,35,34,34,34,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,34,34,34,35, +35,33,33,33,33,32,32,32,32,32,31,31,31,31,30,30, +31,30,30,30,30,28,28,29,29,27,27,27,27,26,26,26, +25,25,25,24,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,4,6,6,6,6,7,7,7,8,8,8,9,9,9, +10,10,10,10,12,12,12,13,13,13,13,14,14,14,14,16, +16,16,16,17,17,17,17,18,18,18,18,18,18,19,19,19, +19,19,19,20,20,20,20,21,21,21,21,21,21,21,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,21,21,21, +21,21,21,21,20,20,20,20,19,19,19,19,19,19,18,18, +18,18,18,18,17,17,17,17,16,16,16,15,14,14,14,14, +13,13,13,13,12,12,12,10,10,10,11,9,9,9,8,8, +8,7,7,7,6,6,6,5,4,4,4,0,0,0,1,1, +1,2,2,2,3,24,24,25,25,25,25,26,26,26,27,27, +27,27,29,28,28,28,30,30,30,30,31,31,31,31,32,32, +32,32,32,33,33,33,33,35,35,34,34,34,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,34,35,35,33,33,33,33,32,32,32,32,32,31,31,31, +31,31,31,31,30,30,30,30,28,28,29,29,27,27,27,27, +26,26,26,25,25,25,24,24,24,3,2,2,2,1,1,1, +0,0,0,4,4,4,6,6,6,7,7,7,8,8,8,8, +9,9,9,10,10,10,12,12,12,12,13,13,13,14,14,14, +14,15,16,16,16,17,17,17,17,17,18,18,18,18,18,19, +19,19,19,19,19,19,20,20,20,21,21,21,21,21,21,21, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,21,21,21,21,21,21,20,20,20,20,19,19,19,19,19, +19,18,18,18,18,18,18,17,17,17,17,16,16,16,15,14, +14,14,14,13,13,13,13,12,12,12,10,10,10,11,9,9, +9,8,8,8,7,7,7,6,6,6,5,4,4,0,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +26,27,27,27,29,29,28,28,30,30,30,30,31,31,31,31, +31,32,32,32,32,32,33,33,33,35,35,35,34,34,34,34, +34,34,34,34,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,34,34,34,34, +34,34,34,34,34,35,35,33,33,33,33,32,32,32,32,32, +32,32,32,31,31,31,31,30,30,30,30,28,28,29,29,27, +27,27,26,26,26,26,25,25,25,24,24,24,2,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,9,10,10,10,12,12,12,13,13,13,13, +14,14,14,14,15,16,16,16,17,17,17,17,17,18,18,18, +18,18,19,19,19,19,19,19,20,20,20,21,21,21,21,21, +21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,21,21,21,21,21,21,21,20,20,20,19,19, +19,19,19,19,18,18,18,18,18,17,17,17,17,17,16,16, +16,15,14,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,2,24,24,24,25,25,25, +26,26,26,26,27,27,27,29,29,28,28,30,30,30,30,31, +31,31,31,32,32,32,32,32,33,33,33,33,35,35,34,34, +34,34,34,34,34,34,34,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,34, +34,34,34,34,34,34,34,34,35,35,33,33,33,33,32,32, +33,32,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,29,27,27,27,26,26,26,26,25,25,25,24,24,3,2, +2,2,1,1,1,0,0,0,4,4,4,5,6,6,6,7, +7,7,8,8,8,9,9,9,10,10,10,10,12,12,12,13, +13,13,13,14,14,14,15,16,16,16,17,17,17,17,17,18, +18,18,18,18,19,19,19,19,19,19,20,20,20,20,21,21, +21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,21,21,21,21,21,21,20,20, +20,19,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +17,16,16,16,15,14,14,14,14,13,13,13,12,12,12,12, +10,10,10,9,9,9,8,8,8,7,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,2,2,2,3,24,24,25, +25,25,25,26,26,26,27,27,27,27,29,28,28,30,30,30, +30,31,31,31,31,31,32,32,32,32,32,33,33,33,35,35, +35,34,34,34,34,34,34,34,34,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,34,35,35,33,33,33, +33,33,33,33,32,32,32,32,31,31,31,31,31,30,30,30, +30,28,28,29,27,27,27,27,26,26,26,25,25,25,25,24, +24,3,2,2,2,1,1,1,0,0,0,4,4,4,6,6, +6,7,7,7,8,8,8,8,9,9,9,10,10,10,12,12, +12,13,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,18,18,18,18,18,19,19,19,19,19,19,20,20,20,21, +21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,21, +21,20,20,20,19,19,19,19,19,19,18,18,18,18,18,17, +17,17,17,17,16,16,16,15,14,14,14,13,13,13,13,12, +12,12,10,10,10,10,9,9,9,8,8,8,7,7,7,6, +6,6,5,4,4,0,0,0,1,1,1,1,2,2,2,3, +24,24,25,25,25,26,26,26,26,27,27,27,29,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,32,33,33,33, +35,35,35,34,34,34,34,34,34,34,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,34,34,35,35, +34,35,35,33,33,33,32,32,32,32,32,31,31,31,31,30, +30,30,30,28,28,29,29,27,27,27,26,26,26,26,25,25, +25,24,24,3,2,2,2,1,1,1,0,0,0,0,4,4, +5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10, +12,12,12,12,13,13,13,14,14,14,14,15,16,16,16,17, +17,17,17,18,18,18,18,18,19,19,19,19,19,19,20,20, +20,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,21, +21,21,21,21,20,20,20,19,19,19,19,19,19,18,18,18, +18,18,17,17,17,17,16,16,16,15,14,14,14,14,13,13, +13,13,12,12,12,10,10,10,9,9,9,8,8,8,8,7, +7,7,6,6,6,4,4,4,0,0,0,1,1,1,2,2, +2,3,24,24,25,25,25,25,26,26,26,27,27,27,29,29, +28,28,30,30,30,30,31,31,31,31,32,32,32,32,32,33, +33,33,35,35,35,34,34,34,34,34,34,34,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,34,34,34,34,34,34,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,32,31,31, +31,31,30,30,30,30,28,28,29,29,27,27,27,26,26,26, +25,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,4,6,6,6,7,7,7,8,8,8,9,9,9,11, +10,10,10,12,12,12,13,13,13,14,14,14,14,15,16,16, +16,17,17,17,17,18,18,18,18,18,19,19,19,19,19,19, +20,20,20,21,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,21,21,21,21,21,20,20,20,19,19,19,19,19,19, +18,18,18,18,18,17,17,17,17,16,16,16,15,14,14,14, +14,13,13,13,12,12,12,12,10,10,10,9,9,9,8,8, +8,7,7,7,6,6,6,4,4,4,0,0,0,1,1,1, +2,2,2,3,24,24,24,25,25,25,26,26,26,27,27,27, +27,29,28,28,30,30,30,30,31,31,31,31,32,32,32,32, +32,33,33,33,35,35,35,34,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,34,34,34,34, +34,34,34,34,34,34,35,35,33,33,33,33,32,32,32,32, +31,31,31,31,31,30,30,30,28,28,29,29,27,27,27,26, +26,26,26,25,25,25,24,24,3,2,2,2,1,1,1,0, +0,0,4,4,4,6,6,6,7,7,7,8,8,8,9,9, +9,9,10,10,10,12,12,12,13,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,18,19,19,19,19, +19,19,20,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,21,21,21,21,21,20,20,20,19,19,19, +19,19,18,18,18,18,18,17,17,17,17,17,16,16,15,14, +14,14,14,13,13,13,13,12,12,12,10,10,10,9,9,9, +8,8,8,7,7,7,6,6,6,5,4,4,0,0,0,1, +1,1,1,2,2,2,24,24,24,25,25,25,26,26,26,27, +27,27,27,29,28,28,30,30,30,30,31,31,31,31,32,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,34, +36,36,34,34,34,34,34,34,34,35,35,33,33,33,32,32, +32,32,32,31,31,31,31,30,30,30,30,28,28,29,27,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,20,21,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,21,21,21,21,21,20,20,19, +19,19,19,19,19,18,18,18,18,18,17,17,17,17,16,16, +16,15,14,14,14,13,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,24,24,24,25,25,25,26,26, +26,27,27,27,27,29,28,28,30,30,30,30,31,31,31,31, +32,32,32,32,32,33,33,33,35,35,34,34,34,34,34,34, +34,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,11,10,10,10,12,12,12,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,19, +19,19,19,19,19,20,20,21,21,21,21,21,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,0,1,1,1,2,2,2,24,24,24,25,25,25, +26,26,26,27,27,27,29,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,33,33,33,35,35,34,34,34,34,34, +34,34,36,36,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,34,34,34,34,34,34,35,35, +33,33,33,32,32,32,32,31,31,31,31,30,30,30,30,28, +28,29,27,27,27,27,26,26,26,25,25,25,24,24,3,2, +2,2,1,1,1,0,0,0,4,4,4,6,6,6,7,7, +7,8,8,8,9,9,9,10,10,10,12,12,12,12,13,13, +13,14,14,14,14,16,16,16,17,17,17,17,18,18,18,18, +18,19,19,19,19,19,20,20,20,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,19,18,18,18,18,17,17, +17,17,16,16,16,15,14,14,14,14,13,13,13,12,12,12, +10,10,10,9,9,9,9,8,8,8,7,7,7,6,6,5, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,25,25, +25,25,26,26,26,27,27,27,29,29,28,28,30,30,30,31, +31,31,31,32,32,32,32,32,33,33,33,35,35,34,34,34, +34,34,34,36,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +37,36,36,36,36,36,36,36,36,36,34,34,34,34,34,34, +35,35,33,33,33,32,32,32,32,31,31,31,31,30,30,30, +30,28,28,29,27,27,27,27,26,26,26,25,25,25,24,24, +3,2,2,2,1,1,1,0,0,0,4,4,4,6,6,6, +7,7,7,8,8,8,9,9,9,10,10,10,12,12,12,13, +13,13,13,14,14,14,15,16,16,16,17,17,17,17,18,18, +18,18,19,19,19,19,19,19,20,20,21,21,21,21,22,22, +22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,22,22, +21,21,21,21,20,20,20,19,19,19,19,19,18,18,18,18, +17,17,17,17,16,16,16,15,14,14,14,14,13,13,13,12, +12,12,10,10,10,9,9,9,8,8,8,7,7,7,6,6, +6,5,4,4,0,0,0,1,1,1,2,2,2,3,24,24, +25,25,25,26,26,26,27,27,27,27,29,28,28,30,30,30, +30,31,31,31,31,32,32,32,32,33,33,33,35,35,34,34, +34,34,34,34,36,36,36,36,36,36,36,36,36,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,36,36,36,36,36,36,36,36,36,34,34,34,34, +34,34,35,35,33,33,33,32,32,32,32,31,31,31,31,30, +30,30,30,28,28,29,27,27,27,26,26,26,26,25,25,25, +24,24,3,2,2,2,1,1,1,0,0,0,4,4,5,6, +6,7,7,7,8,8,8,9,9,9,11,10,10,10,12,12, +12,13,13,13,14,14,14,14,16,16,16,17,17,17,17,18, +18,18,18,18,19,19,19,19,19,20,20,21,21,21,21,22, +22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,22,22,22,22,22,22,22,22, +22,22,21,21,21,21,20,20,20,19,19,19,19,19,18,18, +18,18,17,17,17,17,16,16,16,15,14,14,14,13,13,13, +13,12,12,12,10,10,10,9,9,9,8,8,8,7,7,7, +6,6,6,4,4,4,0,0,0,1,1,1,2,2,2,24, +24,24,25,25,25,26,26,26,27,27,27,29,29,28,28,30, +30,30,31,31,31,31,32,32,32,32,32,33,33,33,35,34, +34,34,34,34,34,36,36,36,36,36,36,36,36,36,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,36,36,36,36,36,36,36,36,34,34, +34,34,34,34,35,35,33,33,33,32,32,32,32,31,31,31, +31,30,30,30,28,28,29,29,27,27,27,26,26,26,25,25, +25,24,24,24,2,2,2,1,1,1,0,0,0,4,4,4, +6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,12, +12,12,13,13,13,14,14,14,14,15,16,16,16,17,17,17, +17,18,18,18,18,19,19,19,19,19,20,20,21,21,21,21, +22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,22,22,22,22,22, +22,22,22,22,21,21,21,21,20,20,19,19,19,19,19,18, +18,18,18,18,17,17,17,17,16,16,16,15,14,14,14,13, +13,13,12,12,12,10,10,10,11,9,9,9,8,8,8,7, +7,6,6,6,5,4,4,0,0,0,1,1,1,2,2,2, +3,24,24,25,25,25,26,26,26,27,27,27,27,29,28,28, +30,30,30,30,31,31,31,31,32,32,32,32,33,33,33,35, +35,34,34,34,34,34,36,36,36,36,36,36,36,36,36,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,36,36,36,36,36,36,36,36, +34,34,34,34,34,35,35,33,33,33,32,32,32,32,31,31, +31,31,30,30,30,30,28,28,29,27,27,27,27,26,26,26, +25,25,25,24,24,3,2,2,2,1,1,1,0,0,0,4, +4,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10, +12,12,12,13,13,13,13,14,14,14,15,16,16,16,17,17, +17,17,18,18,18,18,19,19,19,19,19,20,20,21,21,21, +21,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,22,22,22, +22,22,22,22,22,22,21,21,21,21,20,20,19,19,19,19, +19,18,18,18,18,17,17,17,17,16,16,16,15,14,14,14, +13,13,13,13,12,12,12,10,10,10,9,9,9,8,8,8, +7,7,7,6,6,6,4,4,4,0,0,0,1,1,1,2, +2,3,24,24,25,25,25,25,26,26,26,27,27,27,29,28, +28,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33, +35,35,34,34,34,34,34,36,36,36,36,36,36,36,36,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,36,36,36,36,36,36, +36,34,34,34,34,34,34,35,35,33,33,33,32,32,32,32, +31,31,31,31,30,30,30,28,28,29,29,27,27,27,26,26, +26,25,25,25,24,24,3,2,2,2,1,1,1,0,0,0, +4,4,5,6,6,6,7,7,8,8,8,9,9,9,11,10, +10,10,12,12,12,13,13,13,14,14,14,15,16,16,16,17, +17,17,17,18,18,18,18,19,19,19,19,19,20,20,21,21, +21,21,22,22,22,22,22,22,22,22,22,23,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,22, +22,22,22,22,22,22,22,22,21,21,21,20,20,20,19,19, +19,19,19,18,18,18,18,17,17,17,17,16,16,16,14,14, +14,14,13,13,13,12,12,12,10,10,10,9,9,9,8,8, +8,7,7,7,6,6,6,4,4,4,0,0,0,1,1,1, +2,2,2,3,24,24,25,25,25,26,26,26,27,27,27,29, +29,28,30,30,30,30,31,31,31,31,32,32,32,32,33,33, +33,35,35,34,34,34,34,34,36,36,36,36,36,36,36,36, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36, +36,36,36,34,34,34,34,34,35,35,33,33,33,32,32,32, +32,31,31,31,31,30,30,30,30,28,29,29,27,27,27,26, +26,26,25,25,25,24,24,3,2,2,2,1,1,1,0,0, +0,4,4,4,6,6,6,7,7,7,8,8,8,9,9,9, +10,10,10,12,12,12,13,13,13,14,14,14,15,16,16,16, +17,17,17,17,18,18,18,18,19,19,19,19,19,20,20,21, +21,21,21,22,22,22,22,22,22,22,22,22,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +22,22,22,22,22,22,22,22,22,21,21,21,21,20,20,19, +19,19,19,19,18,18,18,18,17,17,17,17,16,16,16,15, +14,14,14,13,13,13,12,12,12,10,10,10,11,9,9,9, +8,8,7,7,7,6,6,6,5,4,4,0,0,0,1,1, +1,2,2,2,3,24,24,25,25,25,26,26,26,27,27,27, +29,29,28,28,30,30,30,31,31,31,31,32,32,32,32,33, +33,33,35,35,34,34,34,34,34,36,36,36,36,36,36,36, +36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36, +36,36,36,36,34,34,34,34,34,35,35,33,33,33,32,32, +32,32,31,31,31,31,30,30,30,30,28,29,29,27,27,27, +26,26,26,25,25,25,24,24,3,2,2,2,1,1,1,0, +0,0,4,4,4,6,6,6,7,7,7,8,8,8,9,9, +9,10,10,10,12,12,12,13,13,13,14,14,14,15,16,16, +16,17,17,17,17,18,18,18,18,19,19,19,19,19,20,20, +21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,22,22,22,22,22,22,22,22,21,21,21,21,20,20, +19,19,19,19,19,18,18,18,18,17,17,17,17,16,16,16, +15,14,14,14,13,13,13,12,12,12,12,10,10,11,9,9, +9,8,8,7,7,7,6,6,6,5,4,4,0,0,0,1, +1,1,2,2,2,3,24,24,25,25,25,26,26,26,27,27, +27,29,29,28,28,30,30,30,31,31,31,31,32,32,32,32, +33,33,33,35,35,34,34,34,34,34,36,36,36,36,36,36, +36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,34,34,34,34,34,35,35,33,33,33,32, +32,32,32,31,31,31,31,30,30,30,30,28,29,29,27,27, +27,26,26,26,25,25,25,24,24,3,2,2,2,1,1,1, +0,0,0,4,4,5,6,6,6,7,7,7,8,8,9,9, +9,11,10,10,12,12,12,12,13,13,13,14,14,14,15,16, +16,16,17,17,17,17,18,18,18,18,19,19,19,19,19,20, +20,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,19,19,19,19,19,18,18,18,18,17,17,17,17,16,16, +16,15,14,14,14,13,13,13,12,12,12,10,10,10,11,9, +9,8,8,8,7,7,7,6,6,6,4,4,4,0,0,0, +1,1,1,2,2,2,3,24,24,25,25,25,26,26,26,27, +27,27,29,29,28,30,30,30,30,31,31,31,31,32,32,32, +32,33,33,33,35,35,34,34,34,34,34,36,36,36,36,36, +36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,36,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,31,31,31,31,30,30,30,28,28,29,29,27, +27,27,26,26,26,25,25,25,24,24,3,2,2,2,1,1, +1,0,0,0,4,4,5,6,6,7,7,7,8,8,8,9, +9,9,10,10,10,12,12,12,13,13,13,13,14,14,14,15, +16,16,16,17,17,17,18,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,22,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,17,17,17,17,16, +16,16,14,14,14,14,13,13,13,12,12,12,10,10,10,9, +9,9,8,8,8,7,7,7,6,6,6,4,4,4,0,0, +0,1,1,1,2,2,2,24,24,25,25,25,25,26,26,26, +27,27,27,29,28,28,30,30,30,30,31,31,31,32,32,32, +32,32,33,33,35,35,34,34,34,34,34,34,36,36,36,36, +36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,12,12,12,12,10,10, +11,9,9,9,8,8,7,7,7,6,6,6,5,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,20,20,20,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,20,20,20,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,15,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,27,29,28,28,30,30,30,30,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,5,6,6,6,7,7,8,8, +8,9,9,9,11,10,10,12,12,12,12,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,24,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,30,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,12,10, +10,11,9,9,9,8,8,8,7,7,7,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,34,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,15,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,23,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +32,32,32,32,32,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,34,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,27,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,27,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,0,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,26,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37, +36,36,36,36,36,36,36,36,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,26,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,23,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,26,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,28, +29,27,27,27,26,26,26,25,25,25,25,24,24,3,2,2, +2,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,22,21,21, +21,21,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,13,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,30,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,22,21,21, +21,20,20,20,19,19,19,19,19,18,18,18,18,17,17,17, +17,16,16,16,15,14,14,14,13,13,13,12,12,12,12,10, +10,11,9,9,9,8,8,8,7,7,7,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +32,32,32,32,32,33,33,33,35,35,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,33,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,14,14, +14,14,15,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,20,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,16,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,32,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,34,35,35,33, +33,32,32,32,32,32,31,31,31,31,30,30,30,28,28,29, +29,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,5,6,6,6,7,7,7,8, +8,9,9,9,11,10,10,10,12,12,12,13,13,13,14,14, +14,14,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,18,17,17,17, +17,16,16,15,14,14,14,14,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,12,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,20,21,21,21,22,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,24,24,25,25,25,25,26, +26,26,27,27,27,29,28,28,30,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,36,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,26,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +21,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,27,29,28,28,30,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,24,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,12,12,12,12,10,10, +11,9,9,9,8,8,8,7,7,6,6,6,5,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,28,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,5,6,6,6,7,7,7,8,8, +9,9,9,11,10,10,10,12,12,12,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,19,18,18,18,18,17,17,17,17, +16,16,16,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,28,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +33,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,5,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,24,24,24,25,25,25,26,26, +26,27,27,27,29,28,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,36,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +15,16,16,16,17,17,17,18,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,20,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,30,31,31,31,32,32, +32,32,32,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,20,21,21,21,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,26,25,25,25,24,24,3,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,25,25,25,25,24,24,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,17,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,25,25,25,25,24,24,2,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,15, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,17,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,31,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,26,25,25,25,24,24,3,2,2,1,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,21,21,21,21,22,22,22,22,22,22,22,22,23,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,5,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,36,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,26,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,0,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,14,14,14,14, +15,16,16,17,17,17,17,18,18,18,18,19,19,19,19,19, +20,20,20,21,21,21,22,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,22,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,13,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,0,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,29,29,28,28,30,30,30,31,31,31,31,32,32, +32,32,33,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,4,4,4,6,6,6,7,7,7,8,8,8, +9,9,9,10,10,10,12,12,12,13,13,13,13,14,14,14, +15,16,16,16,17,17,17,18,18,18,18,18,19,19,19,19, +19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,23, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,23,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,16, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,1,1,1,2,2,2,3,24,24,25,25,25,26,26,26, +27,27,27,27,29,28,28,30,30,30,30,31,31,31,32,32, +32,32,32,33,33,35,35,34,34,34,34,34,34,36,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,37,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,35,33,33, +32,32,32,32,32,31,31,31,30,30,30,30,28,28,29,27, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,5,6,6,7,7,7,8,8,8, +9,9,9,11,10,10,12,12,12,12,13,13,13,14,14,14, +15,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19, +19,20,20,20,21,21,21,22,22,22,22,22,22,22,22,22, +23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,23,22,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,4,4,4,0, +0,0,1,1,1,2,2,2,24,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,30,30,30,30,31,31,31,31,32, +32,32,32,33,33,33,35,35,34,34,34,34,34,34,36,36, +36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,37,36,36,36, +36,36,36,36,36,36,34,34,34,34,34,34,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,3,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,13,13,13,14,14,14, +14,15,16,16,17,17,17,17,18,18,18,18,18,19,19,19, +19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22, +22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23, +23,23,22,22,22,22,22,22,22,22,22,22,21,21,21,21, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +9,9,9,8,8,8,7,7,7,6,6,6,5,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,26,26, +26,27,27,27,29,29,28,28,30,30,30,30,31,31,31,32, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,36,36, +36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,37,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,25,25,25,24,24,24,2,2,2,1, +1,1,0,0,0,4,4,4,6,6,6,7,7,7,8,8, +8,9,9,9,10,10,10,12,12,12,12,13,13,13,14,14, +14,15,16,16,16,17,17,17,17,18,18,18,18,18,19,19, +19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22, +22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,15,14,14,14,14,13,13,13,12,12,12,10,10,10, +11,9,9,9,8,8,8,7,7,7,6,6,6,4,4,0, +0,0,1,1,1,2,2,2,3,24,24,25,25,25,25,26, +26,26,27,27,27,29,29,28,28,30,30,30,31,31,31,31, +32,32,32,32,33,33,33,35,35,34,34,34,34,34,34,34, +36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37, +37,37,37,37,37,37,37,37,37,37,37,36,36,36,36,36, +36,36,36,36,36,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,5,6,6,6,7,7,7,8, +8,8,9,9,9,10,10,10,12,12,12,13,13,13,13,14, +14,14,15,16,16,16,17,17,17,17,18,18,18,18,18,19, +19,19,19,19,20,20,20,21,21,21,21,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,21,21,21,21,20, +20,20,19,19,19,19,19,18,18,18,18,18,17,17,17,17, +16,16,16,14,14,14,14,13,13,13,12,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,4,4,4, +0,0,0,1,1,1,2,2,2,3,24,24,25,25,25,26, +26,26,27,27,27,27,29,28,28,30,30,30,30,31,31,31, +31,32,32,32,32,33,33,33,33,35,35,34,34,34,34,34, +34,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37, +37,37,37,37,37,37,37,36,36,36,36,36,36,36,36,36, +36,36,36,36,34,34,34,34,34,34,34,35,35,33,33,33, +32,32,32,32,32,31,31,31,31,30,30,30,28,28,29,29, +27,27,27,26,26,26,26,25,25,25,24,24,3,2,2,2, +1,1,1,0,0,0,4,4,4,6,6,6,7,7,7,8, +8,8,9,9,9,11,10,10,10,12,12,12,13,13,13,14, +14,14,14,15,16,16,16,17,17,17,17,18,18,18,18,18, +19,19,19,19,19,20,20,20,21,21,21,21,21,22,22,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,22,22,21,21,21,21,20,20, +20,19,19,19,19,19,19,18,18,18,18,17,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,9,9,9,8,8,8,7,7,7,6,6,6,5,4,4, +0,0,0,1,1,1,2,2,2,2,24,24,24,25,25,25, +26,26,26,27,27,27,27,29,28,28,30,30,30,30,31,31, +31,31,32,32,32,32,32,33,33,33,35,35,34,34,34,34, +34,34,34,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,34,34,34,34,34,34,34,35,35,33,33,33,33, +32,32,32,32,31,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,24,24,24,2,2,2, +2,1,1,1,0,0,0,4,4,5,6,6,6,7,7,7, +8,8,8,9,9,9,10,10,10,12,12,12,12,13,13,13, +14,14,14,14,15,16,16,16,17,17,17,17,18,18,18,18, +18,19,19,19,19,19,19,20,20,20,21,21,21,21,21,22, +22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,22,22,21,21,21,21,21,21,20,20, +20,19,19,19,19,19,18,18,18,18,18,17,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,10,10, +10,11,9,9,9,8,8,8,7,7,7,6,6,6,4,4, +4,0,0,0,1,1,1,2,2,2,3,24,24,25,25,25, +25,26,26,26,27,27,27,27,29,28,28,30,30,30,30,31, +31,31,31,32,32,32,32,32,33,33,33,35,35,35,34,34, +34,34,34,34,34,36,36,36,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, +36,34,34,34,34,34,34,34,34,35,35,35,33,33,33,32, +32,32,32,32,31,31,31,31,31,30,30,30,30,28,28,29, +27,27,27,27,26,26,26,25,25,25,25,24,24,3,2,2, +2,1,1,1,0,0,0,4,4,4,6,6,6,6,7,7, +7,8,8,8,9,9,9,10,10,10,12,12,12,12,13,13, +13,13,14,14,14,15,16,16,16,17,17,17,17,17,18,18, +18,18,18,19,19,19,19,19,19,20,20,20,20,21,21,21, +21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22, +22,22,22,22,22,22,21,21,21,21,21,21,20,20,20,20, +19,19,19,19,19,19,18,18,18,18,18,17,17,17,17,17, +16,16,16,15,14,14,14,13,13,13,13,12,12,12,12,10, +10,10,9,9,9,8,8,8,7,7,7,6,6,6,6,4, +4,4,0,0,0,1,1,1,2,2,2,3,24,24,25,25, +25,25,26,26,26,27,27,27,27,29,28,28,30,30,30,30, +31,31,31,31,31,32,32,32,32,32,33,33,33,35,35,35, +34,34,34,34,34,34,34,34,36,36,36,36,36,36,36,36, +36,36,36,36,36,36,36,36,36,36,36,36,36,36,34,34, +34,34,34,34,34,34,34,34,35,35,35,33,33,33,33,32, +32,32,32,32,31,31,31,31,31,30,30,30,30,28,28,29, +29,27,27,27,26,26,26,26,25,25,25,24,24,24,2,2, +2,2,1,1,1,0,0,0,4,4,4,6,6,6,7,7, +7,7,8,8,8,9,9,9,10,10,10,10,12,12,12,13, +13,13,13,14,14,14,14,15,16,16,16,17,17,17,17,17, +18,18,18,18,18,19,19,19,19,19,19,19,20,20,20,20, +21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22, +22,21,21,21,21,21,21,21,21,21,20,20,20,20,19,19, +19,19,19,19,19,18,18,18,18,18,18,17,17,17,17,16, +16,16,16,15,14,14,14,14,13,13,13,12,12,12,12,10, +10,10,9,9,9,9,8,8,8,7,7,7,6,6,6,5, +4,4,0,0,0,0,1,1,1,2,2,2,3,24,24,25, +25,25,25,26,26,26,27,27,27,27,29,29,28,28,30,30, +30,30,31,31,31,31,31,32,32,32,32,32,33,33,33,33, +35,35,34,34,34,34,34,34,34,34,34,34,36,36,36,36, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,34,34,34,35,35,35,33,33,33,33,32,32, +32,32,32,31,31,31,31,31,30,30,30,30,30,28,28,29, +29,27,27,27,26,26,26,26,25,25,25,25,24,24,3,2, +2,2,1,1,1,1,0,0,0,4,4,4,6,6,6,7, +7,7,7,8,8,8,9,9,9,11,10,10,10,12,12,12, +13,13,13,13,14,14,14,14,15,16,16,16,16,17,17,17, +17,17,18,18,18,18,18,18,19,19,19,19,19,19,19,19, +20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21, +21,21,21,21,21,21,21,20,20,20,20,20,19,19,19,19, +19,19,19,19,18,18,18,18,18,18,17,17,17,17,17,16, +16,16,16,15,14,14,14,14,13,13,13,13,12,12,12,10, +10,10,11,9,9,9,8,8,8,7,7,7,7,6,6,6, +4,4,4,0,0,0,1,1,1,1,2,2,2,3,24,24, +25,25,25,25,26,26,26,26,27,27,27,29,29,28,28,30, +30,30,30,30,31,31,31,31,31,32,32,32,32,32,33,33, +33,33,35,35,35,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +34,34,34,34,35,35,35,35,33,33,33,33,33,32,32,32, +32,32,32,31,31,31,31,31,30,30,30,30,30,28,28,29, +29,27,27,27,27,26,26,26,25,25,25,25,24,24,24,2, +2,2,2,1,1,1,0,0,0,0,4,4,4,6,6,6, +7,7,7,7,8,8,8,9,9,9,9,10,10,10,12,12, +12,12,13,13,13,13,14,14,14,14,15,16,16,16,16,17, +17,17,17,17,18,18,18,18,18,18,18,19,19,19,19,19, +19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20, +20,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19, +19,19,19,18,18,18,18,18,18,17,17,17,17,17,17,16, +16,16,15,14,14,14,14,14,13,13,13,13,12,12,12,10, +10,10,10,9,9,9,9,8,8,8,7,7,7,6,6,6, +6,4,4,4,0,0,0,1,1,1,1,2,2,2,3,24, +24,24,25,25,25,26,26,26,26,27,27,27,27,29,29,28, +28,30,30,30,30,30,31,31,31,31,31,32,32,32,32,32, +32,33,33,33,33,33,35,35,35,35,34,34,34,34,34,34, +34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34, +35,35,35,35,35,33,33,33,33,33,33,32,32,32,32,32, +32,32,31,31,31,31,31,30,30,30,30,30,28,28,28,29, +29,27,27,27,27,26,26,26,26,25,25,25,24,24,24,3, +2,2,2,2,1,1,1,0,0,0,0,4,4,4,6,6, +6,7,7,7,7,8,8,8,9,9,9,9,10,10,10,10, +12,12,12,12,13,13,13,13,14,14,14,14,15,15,16,16, +16,16,17,17,17,17,17,18,18,18,18,18,18,18,18,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +19,18,18,18,18,18,18,18,17,17,17,17,17,17,16,16, +16,16,15,14,14,14,14,14,13,13,13,13,12,12,12,12, +10,10,10,11,9,9,9,8,8,8,8,7,7,7,6,6, +6,6,4,4,4,0,0,0,0,1,1,1,2,2,2,2, +3,24,24,25,25,25,25,26,26,26,26,27,27,27,27,29, +29,28,28,30,30,30,30,30,31,31,31,31,31,31,32,32, +32,32,32,32,32,33,33,33,33,33,33,35,35,35,35,35, +33,33,35,35,35,35,35,35,35,35,35,35,35,35,35,35, +33,33,33,33,33,33,33,33,32,32,32,32,32,32,32,32, +32,31,31,31,31,31,31,30,30,30,30,30,28,28,28,29, +29,27,27,27,27,26,26,26,26,25,25,25,25,24,24,24, +3,2,2,2,1,1,1,1,0,0,0,4,4,4,4,6, +6,6,6,7,7,7,8,8,8,8,9,9,9,9,10,10, +10,10,12,12,12,12,13,13,13,13,14,14,14,14,14,15, +16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18, +18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19, +19,19,19,19,19,19,19,19,19,19,19,19,19,19,18,18, +18,18,18,18,18,18,18,17,17,17,17,17,17,16,16,16, +16,15,15,14,14,14,14,13,13,13,13,13,12,12,12,12, +10,10,10,10,9,9,9,9,8,8,8,7,7,7,7,6, +6,6,6,4,4,4,0,0,0,0,1,1,1,2,2,2, +2,3,24,24,24,25,25,25,25,26,26,26,26,27,27,27, +27,29,29,28,28,28,30,30,30,30,30,31,31,31,31,31, +31,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33, +33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33, +33,33,33,33,32,32,32,32,32,32,32,32,32,32,32,31, +31,31,31,31,31,31,30,30,30,30,30,28,28,28,29,29, +27,27,27,27,27,26,26,26,26,25,25,25,25,24,24,24, +3,2,2,2,2,1,1,1,1,0,0,0,0,4,4,4, +6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9, +10,10,10,10,12,12,12,12,13,13,13,13,13,14,14,14, +14,14,15,16,16,16,16,16,17,17,17,17,17,17,17,17, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19, +19,19,19,19,19,19,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,17,17,17,17,17,17,17,17,16,16,16,16, +16,15,14,14,14,14,14,13,13,13,13,13,12,12,12,12, +10,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7, +6,6,6,6,4,4,4,0,0,0,0,1,1,1,1,2, +2,2,2,3,24,24,24,25,25,25,25,26,26,26,26,27, +27,27,27,27,29,29,28,28,28,30,30,30,30,30,31,31, +31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,32,32,32,32,32,32,31,31,31,31, +31,31,31,31,30,30,30,30,30,30,28,28,28,28,29,29, +27,27,27,27,27,26,26,26,26,25,25,25,25,25,24,24, +24,3,2,2,2,2,1,1,1,1,0,0,0,0,4,4, +4,5,6,6,6,6,7,7,7,8,8,8,8,8,9,9, +9,9,10,10,10,10,12,12,12,12,12,13,13,13,13,13, +14,14,14,14,14,15,15,16,16,16,16,16,17,17,17,17, +17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,17,17,17,17,17,17,17,17,17,16,16,16,16,16,15, +15,14,14,14,14,14,14,13,13,13,13,13,12,12,12,12, +10,10,10,10,11,9,9,9,9,8,8,8,8,7,7,7, +7,6,6,6,6,4,4,4,4,0,0,0,0,1,1,1, +2,2,2,2,2,3,24,24,24,25,25,25,25,26,26,26, +26,26,27,27,27,27,29,29,29,28,28,28,30,30,30,30, +30,30,31,31,31,31,31,31,31,31,31,32,32,32,32,32, +31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32, +32,32,32,32,32,32,31,31,31,31,31,31,31,31,31,31, +31,31,30,30,30,30,30,30,30,28,28,28,28,29,29,27, +27,27,27,27,27,26,26,26,26,26,25,25,25,25,24,24, +24,3,3,2,2,2,2,1,1,1,1,0,0,0,0,4, +4,4,4,6,6,6,6,7,7,7,7,8,8,8,8,8, +9,9,9,9,10,10,10,10,10,12,12,12,12,12,13,13, +13,13,13,14,14,14,14,14,14,14,15,16,16,16,16,16, +16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,16,16,16,16,16,16,16,15,14, +14,14,14,14,14,14,13,13,13,13,13,12,12,12,12,12, +10,10,10,10,10,9,9,9,9,8,8,8,8,8,7,7, +7,7,6,6,6,6,4,4,4,4,0,0,0,0,1,1, +1,1,2,2,2,2,2,3,24,24,24,25,25,25,25,25, +26,26,26,26,27,27,27,27,27,27,29,29,28,28,28,28, +30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31, +31,31,31,31,31,31,31,31,31,31,31,31,31,31,30,30, +30,30,30,30,30,30,30,28,28,28,28,29,29,29,27,27, +27,27,27,27,26,26,26,26,26,26,25,25,25,25,25,24, +24,24,3,2,2,2,2,2,1,1,1,1,0,0,0,0, +4,4,4,4,5,6,6,6,6,7,7,7,7,7,8,8, +8,8,9,9,9,9,9,10,10,10,10,10,12,12,12,12, +12,13,13,13,13,13,13,14,14,14,14,14,14,14,14,15, +15,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,16,16,16,16,16,16,16,16,16,15,15,14,14,14, +14,14,14,14,14,13,13,13,13,13,13,12,12,12,12,12, +10,10,10,10,10,9,9,9,9,9,8,8,8,8,8,7, +7,7,7,6,6,6,6,5,4,4,4,4,0,0,0,0, +1,1,1,1,2,2,2,2,2,3,24,24,24,24,25,25, +25,25,25,26,26,26,26,26,27,27,27,27,27,27,29,29, +29,28,28,28,28,30,30,30,30,30,30,30,30,30,31,31, +30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31, +31,31,31,31,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,28,28,28,28,28,29,29,29,29,27,27,27, +27,27,27,27,26,26,26,26,26,25,25,25,25,25,25,24, +24,24,3,3,2,2,2,2,2,1,1,1,1,0,0,0, +0,0,4,4,4,4,5,6,6,6,6,7,7,7,7,7, +8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,12, +12,12,12,12,12,13,13,13,13,13,13,13,13,14,14,14, +14,14,14,14,14,14,15,15,15,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,15,15,15,14,14,14,14,14,14, +14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +10,10,10,10,10,9,9,9,9,9,9,8,8,8,8,7, +7,7,7,7,6,6,6,6,6,4,4,4,4,0,0,0, +0,0,1,1,1,1,1,2,2,2,2,2,3,24,24,24, +24,25,25,25,25,25,26,26,26,26,26,26,27,27,27,27, +27,27,27,29,29,29,28,28,28,28,28,30,30,30,30,30, +28,28,28,28,30,30,30,30,30,30,30,30,30,30,30,30, +30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,28, +28,28,28,28,28,28,29,29,29,29,27,27,27,27,27,27, +27,27,26,26,26,26,26,26,26,25,25,25,25,25,25,24, +24,24,24,3,2,2,2,2,2,2,1,1,1,1,1,0, +0,0,0,0,4,4,4,4,5,6,6,6,6,6,7,7, +7,7,7,8,8,8,8,8,9,9,9,9,9,9,10,10, +10,10,10,10,12,12,12,12,12,12,12,13,13,13,13,13, +13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +15,15,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +13,13,13,13,13,13,13,13,13,12,12,12,12,12,12,10, +10,10,10,10,10,11,9,9,9,9,9,8,8,8,8,8, +7,7,7,7,7,6,6,6,6,6,5,4,4,4,4,0, +0,0,0,0,1,1,1,1,1,2,2,2,2,2,2,3, +24,24,24,24,25,25,25,25,25,25,26,26,26,26,26,26, +26,27,27,27,27,27,27,27,27,29,29,29,29,28,28,28, +27,29,29,29,29,29,29,29,28,28,28,28,28,28,28,28, +28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29, +29,29,29,29,29,29,27,27,27,27,27,27,27,27,27,27, +26,26,26,26,26,26,26,26,25,25,25,25,25,25,25,24, +24,24,24,3,3,2,2,2,2,2,2,1,1,1,1,1, +0,0,0,0,0,0,4,4,4,4,5,6,6,6,6,6, +7,7,7,7,7,7,8,8,8,8,8,8,9,9,9,9, +9,9,10,10,10,10,10,10,10,12,12,12,12,12,12,12, +12,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14, +14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +14,14,14,14,14,14,14,14,14,14,14,13,13,13,13,13, +13,13,13,13,13,13,12,12,12,12,12,12,12,12,10,10, +10,10,10,10,10,9,9,9,9,9,9,9,8,8,8,8, +8,7,7,7,7,7,7,6,6,6,6,6,5,4,4,4, +4,4,0,0,0,0,0,1,1,1,1,1,1,2,2,2, +2,2,3,3,24,24,24,24,25,25,25,25,25,25,25,26, +26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,29,29,29,29,29,29,29,29,29,29,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,26,26,26, +26,26,26,26,26,26,26,25,25,25,25,25,25,25,25,24, +24,24,24,24,3,3,2,2,2,2,2,2,1,1,1,1, +1,1,0,0,0,0,0,0,4,4,4,4,4,5,6,6, +6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,8, +9,9,9,9,9,9,9,11,10,10,10,10,10,10,10,12, +12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, +13,13,12,12,12,12,12,12,12,12,12,12,12,10,10,10, +10,10,10,10,11,9,9,9,9,9,9,9,8,8,8,8, +8,8,7,7,7,7,7,7,6,6,6,6,6,6,5,4, +4,4,4,4,0,0,0,0,0,0,1,1,1,1,1,1, +2,2,2,2,2,2,3,3,24,24,24,24,24,25,25,25, +25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,27, +26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27, +27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27, +27,27,27,27,27,27,27,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,25,25,25,25,25,25,25,25,25,24,24, +24,24,24,24,3,3,2,2,2,2,2,2,2,1,1,1, +1,1,1,1,0,0,0,0,0,0,4,4,4,4,4,4, +5,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8, +8,8,8,8,8,9,9,9,9,9,9,9,9,9,10,10, +10,10,10,10,10,10,10,10,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13, +13,13,13,13,13,13,13,13,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,10,10,10,10,10,10, +10,10,10,11,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6, +6,4,4,4,4,4,4,0,0,0,0,0,0,0,1,1, +1,1,1,1,2,2,2,2,2,2,2,2,3,3,24,24, +24,24,24,24,25,25,25,25,25,25,25,25,25,26,26,26, +25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26, +25,25,25,25,25,25,25,25,25,25,25,25,25,24,24,24, +24,24,24,24,3,3,2,2,2,2,2,2,2,2,2,1, +1,1,1,1,1,1,0,0,0,0,0,0,0,4,4,4, +4,4,4,4,5,6,6,6,6,6,6,6,7,7,7,7, +7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9, +9,9,9,9,9,9,11,10,10,10,10,10,10,10,10,10, +10,10,10,10,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,10,10,10,10,10,10,10,10,10,10,10,10, +10,11,9,9,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6, +6,6,6,6,4,4,4,4,4,4,4,0,0,0,0,0, +0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2, +2,2,3,3,24,24,24,24,24,24,24,25,25,25,25,25, +24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,24,24,24,24,24,24, +24,24,24,3,3,3,2,2,2,2,2,2,2,2,2,2, +1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, +0,4,4,4,4,4,4,4,5,6,6,6,6,6,6,6, +6,6,7,7,7,7,7,7,7,7,7,8,8,8,8,8, +8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9, +9,9,11,10,10,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +10,10,10,10,10,10,10,10,10,10,10,10,10,11,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,8,8,8,8, +8,8,8,8,8,8,7,7,7,7,7,7,7,7,7,6, +6,6,6,6,6,6,6,5,4,4,4,4,4,4,4,0, +0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, +2,2,2,2,2,2,2,2,2,2,3,3,3,24,24,24, +2,2,2,2,2,3,3,3,3,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25, +25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, +25,25,25,25,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,3,3,3,3,2,2,2,2,2,2,2,2,2,2, +2,2,1,1,1,1,1,1,1,1,1,1,0,0,0,0, +0,0,0,0,0,0,4,4,4,4,4,4,4,4,5,6, +6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7, +7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,11,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,8,8,8,8,8,8, +8,8,8,8,8,8,8,7,7,7,7,7,7,7,7,7, +7,7,6,6,6,6,6,6,6,6,6,5,4,4,4,4, +4,4,4,4,4,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2, +1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,3,3,3,3,3,3,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24, +24,24,24,24,24,24,24,24,24,24,24,24,24,24,3,3, +3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2, +2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1, +1,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4, +4,4,4,4,4,4,5,6,6,6,6,6,6,6,6,6, +6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,8, +8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +9,9,9,9,9,9,8,8,8,8,8,8,8,8,8,8, +8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7, +7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6, +6,5,4,4,4,4,4,4,4,4,4,4,0,0,0,0, +0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1 +}; diff --git a/ops.h b/ops.h new file mode 100644 index 0000000..7e7075a --- /dev/null +++ b/ops.h @@ -0,0 +1,479 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +case 0x00: /* BRK */ + _PC++; + PUSH(_PC>>8); + PUSH(_PC); + PUSH(_P|U_FLAG|B_FLAG); + _P|=I_FLAG; + _PC=RdMem(0xFFFE); + _PC|=RdMem(0xFFFF)<<8; + break; + +case 0x40: /* RTI */ + _P=POP(); + _PI=_P; + _PC=POP(); + _PC|=POP()<<8; + break; + +case 0x60: /* RTS */ + _PC=POP(); + _PC|=POP()<<8; + _PC++; + break; + +case 0x48: /* PHA */ + PUSH(_A); + break; +case 0x08: /* PHP */ + PUSH(_P|U_FLAG|B_FLAG); + break; +case 0x68: /* PLA */ + _A=POP(); + X_ZN(_A); + break; +case 0x28: /* PLP */ + _P=POP(); + break; +case 0x4C: + { + uint16 ptmp=_PC; + unsigned int npc; + + npc=RdMem(ptmp); + ptmp++; + npc|=RdMem(ptmp)<<8; + _PC=npc; + } + break; /* JMP ABSOLUTE */ +case 0x6C: + { + uint32 tmp; + GetAB(tmp); + _PC=RdMem(tmp); + _PC|=RdMem( ((tmp+1)&0x00FF) | (tmp&0xFF00))<<8; + } + break; +case 0x20: /* JSR */ + { + uint8 npc; + npc=RdMem(_PC++); + PUSH(_PC>>8); + PUSH(_PC); + _PC=RdMem(_PC)<<8; + _PC|=npc; + } + break; + +case 0xAA: /* TAX */ + _X=_A; + X_ZN(_A); + break; + +case 0x8A: /* TXA */ + _A=_X; + X_ZN(_A); + break; + +case 0xA8: /* TAY */ + _Y=_A; + X_ZN(_A); + break; +case 0x98: /* TYA */ + _A=_Y; + X_ZN(_A); + break; + +case 0xBA: /* TSX */ + _X=_S; + X_ZN(_X); + break; +case 0x9A: /* TXS */ + _S=_X; + break; + +case 0xCA: /* DEX */ + _X--; + X_ZN(_X); + break; +case 0x88: /* DEY */ + _Y--; + X_ZN(_Y); + break; + +case 0xE8: /* INX */ + _X++; + X_ZN(_X); + break; +case 0xC8: /* INY */ + _Y++; + X_ZN(_Y); + break; + +case 0x18: /* CLC */ + _P&=~C_FLAG; + break; +case 0xD8: /* CLD */ + _P&=~D_FLAG; + break; +case 0x58: /* CLI */ + _P&=~I_FLAG; + break; +case 0xB8: /* CLV */ + _P&=~V_FLAG; + break; + +case 0x38: /* SEC */ + _P|=C_FLAG; + break; +case 0xF8: /* SED */ + _P|=D_FLAG; + break; +case 0x78: /* SEI */ + _P|=I_FLAG; + break; + +case 0xEA: /* NOP */ + break; + +case 0x0A: RMW_A(ASL); +case 0x06: RMW_ZP(ASL); +case 0x16: RMW_ZPX(ASL); +case 0x0E: RMW_AB(ASL); +case 0x1E: RMW_ABX(ASL); + +case 0xC6: RMW_ZP(DEC); +case 0xD6: RMW_ZPX(DEC); +case 0xCE: RMW_AB(DEC); +case 0xDE: RMW_ABX(DEC); + +case 0xE6: RMW_ZP(INC); +case 0xF6: RMW_ZPX(INC); +case 0xEE: RMW_AB(INC); +case 0xFE: RMW_ABX(INC); + +case 0x4A: RMW_A(LSR); +case 0x46: RMW_ZP(LSR); +case 0x56: RMW_ZPX(LSR); +case 0x4E: RMW_AB(LSR); +case 0x5E: RMW_ABX(LSR); + +case 0x2A: RMW_A(ROL); +case 0x26: RMW_ZP(ROL); +case 0x36: RMW_ZPX(ROL); +case 0x2E: RMW_AB(ROL); +case 0x3E: RMW_ABX(ROL); + +case 0x6A: RMW_A(ROR); +case 0x66: RMW_ZP(ROR); +case 0x76: RMW_ZPX(ROR); +case 0x6E: RMW_AB(ROR); +case 0x7E: RMW_ABX(ROR); + +case 0x69: LD_IM(ADC); +case 0x65: LD_ZP(ADC); +case 0x75: LD_ZPX(ADC); +case 0x6D: LD_AB(ADC); +case 0x7D: LD_ABX(ADC); +case 0x79: LD_ABY(ADC); +case 0x61: LD_IX(ADC); +case 0x71: LD_IY(ADC); + +case 0x29: LD_IM(AND); +case 0x25: LD_ZP(AND); +case 0x35: LD_ZPX(AND); +case 0x2D: LD_AB(AND); +case 0x3D: LD_ABX(AND); +case 0x39: LD_ABY(AND); +case 0x21: LD_IX(AND); +case 0x31: LD_IY(AND); + +case 0x24: LD_ZP(BIT); +case 0x2C: LD_AB(BIT); + +case 0xC9: LD_IM(CMP); +case 0xC5: LD_ZP(CMP); +case 0xD5: LD_ZPX(CMP); +case 0xCD: LD_AB(CMP); +case 0xDD: LD_ABX(CMP); +case 0xD9: LD_ABY(CMP); +case 0xC1: LD_IX(CMP); +case 0xD1: LD_IY(CMP); + +case 0xE0: LD_IM(CPX); +case 0xE4: LD_ZP(CPX); +case 0xEC: LD_AB(CPX); + +case 0xC0: LD_IM(CPY); +case 0xC4: LD_ZP(CPY); +case 0xCC: LD_AB(CPY); + +case 0x49: LD_IM(EOR); +case 0x45: LD_ZP(EOR); +case 0x55: LD_ZPX(EOR); +case 0x4D: LD_AB(EOR); +case 0x5D: LD_ABX(EOR); +case 0x59: LD_ABY(EOR); +case 0x41: LD_IX(EOR); +case 0x51: LD_IY(EOR); + +case 0xA9: LD_IM(LDA); +case 0xA5: LD_ZP(LDA); +case 0xB5: LD_ZPX(LDA); +case 0xAD: LD_AB(LDA); +case 0xBD: LD_ABX(LDA); +case 0xB9: LD_ABY(LDA); +case 0xA1: LD_IX(LDA); +case 0xB1: LD_IY(LDA); + +case 0xA2: LD_IM(LDX); +case 0xA6: LD_ZP(LDX); +case 0xB6: LD_ZPY(LDX); +case 0xAE: LD_AB(LDX); +case 0xBE: LD_ABY(LDX); + +case 0xA0: LD_IM(LDY); +case 0xA4: LD_ZP(LDY); +case 0xB4: LD_ZPX(LDY); +case 0xAC: LD_AB(LDY); +case 0xBC: LD_ABX(LDY); + +case 0x09: LD_IM(ORA); +case 0x05: LD_ZP(ORA); +case 0x15: LD_ZPX(ORA); +case 0x0D: LD_AB(ORA); +case 0x1D: LD_ABX(ORA); +case 0x19: LD_ABY(ORA); +case 0x01: LD_IX(ORA); +case 0x11: LD_IY(ORA); + +case 0xEB: /* (undocumented) */ +case 0xE9: LD_IM(SBC); +case 0xE5: LD_ZP(SBC); +case 0xF5: LD_ZPX(SBC); +case 0xED: LD_AB(SBC); +case 0xFD: LD_ABX(SBC); +case 0xF9: LD_ABY(SBC); +case 0xE1: LD_IX(SBC); +case 0xF1: LD_IY(SBC); + +case 0x85: ST_ZP(_A); +case 0x95: ST_ZPX(_A); +case 0x8D: ST_AB(_A); +case 0x9D: ST_ABX(_A); +case 0x99: ST_ABY(_A); +case 0x81: ST_IX(_A); +case 0x91: ST_IY(_A); + +case 0x86: ST_ZP(_X); +case 0x96: ST_ZPY(_X); +case 0x8E: ST_AB(_X); + +case 0x84: ST_ZP(_Y); +case 0x94: ST_ZPX(_Y); +case 0x8C: ST_AB(_Y); + +/* BCC */ +case 0x90: if(_P&C_FLAG) _PC++; else {JR();} break; + +/* BCS */ +case 0xB0: if(_P&C_FLAG) {JR();} else _PC++; break; + +/* BEQ */ +case 0xF0: if(_P&Z_FLAG) {JR();} else _PC++; break; + +/* BNE */ +case 0xD0: if(_P&Z_FLAG) _PC++; else {JR();} break; + +/* BMI */ +case 0x30: if(_P&N_FLAG) {JR();} else _PC++; break; + +/* BPL */ +case 0x10: if(_P&N_FLAG) _PC++; else {JR();} break; + +/* BVC */ +case 0x50: if(_P&V_FLAG) _PC++; else {JR();} break; + +/* BVS */ +case 0x70: if(_P&V_FLAG) {JR();} else _PC++; break; + +/* Here comes the undocumented instructions. Note that this implementation + may be "wrong". If so, please tell me. +*/ + +/* AAC */ +case 0x2B: +case 0x0B: LD_IM(AND;_P&=~C_FLAG;_P|=_A>>7); + +/* AAX */ +case 0x87: ST_ZP(_A&_X); +case 0x97: ST_ZPY(_A&_X); +case 0x8F: ST_AB(_A&_X); +case 0x83: ST_IX(_A&_X); + +/* ARR - ARGH, MATEY! */ +case 0x6B: { + uint8 arrtmp; + LD_IM(AND;_P&=~V_FLAG;_P|=(_A^(_A>>1))&0x40;arrtmp=_A>>7;_A>>=1;_A|=(_P&C_FLAG)<<7;_P&=~C_FLAG;_P|=arrtmp;X_ZN(_A)); + } +/* ASR */ +case 0x4B: LD_IM(AND;LSRA); + +/* ATX(OAL) Is this(OR with $EE) correct? */ +case 0xAB: LD_IM(_A|=0xEE;AND;_X=_A); + +/* AXS */ +case 0xCB: LD_IM(AXS); + +/* DCP */ +case 0xC7: LD_ZP(DEC;CMP); +case 0xD7: LD_ZPX(DEC;CMP); +case 0xCF: LD_AB(DEC;CMP); +case 0xDF: LD_ABX(DEC;CMP); +case 0xDB: LD_ABY(DEC;CMP); +case 0xC3: LD_IX(DEC;CMP); +case 0xD3: LD_IY(DEC;CMP); + +/* ISC */ +case 0xE7: LD_ZP(INC;SBC); +case 0xF7: LD_ZPX(INC;SBC); +case 0xEF: LD_AB(INC;SBC); +case 0xFF: LD_ABX(INC;SBC); +case 0xFB: LD_ABY(INC;SBC); +case 0xE3: LD_IX(INC;SBC); +case 0xF3: LD_IY(INC;SBC); + +/* DOP */ + +case 0x04: _PC++;break; +case 0x14: _PC++;break; +case 0x34: _PC++;break; +case 0x44: _PC++;break; +case 0x54: _PC++;break; +case 0x64: _PC++;break; +case 0x74: _PC++;break; + +case 0x80: _PC++;break; +case 0x82: _PC++;break; +case 0x89: _PC++;break; +case 0xC2: _PC++;break; +case 0xD4: _PC++;break; +case 0xE2: _PC++;break; +case 0xF4: _PC++;break; + +/* KIL */ + +case 0x02: +case 0x12: +case 0x22: +case 0x32: +case 0x42: +case 0x52: +case 0x62: +case 0x72: +case 0x92: +case 0xB2: +case 0xD2: +case 0xF2:ADDCYC(0xFF); + _jammed=1; + _PC--; + break; + +/* LAR */ +case 0xBB: RMW_ABY(_S&=x;_A=_X=_S;X_ZN(_X)); + +/* LAX */ +case 0xA7: LD_ZP(LDA;LDX); +case 0xB7: LD_ZPY(LDA;LDX); +case 0xAF: LD_AB(LDA;LDX); +case 0xBF: LD_ABY(LDA;LDX); +case 0xA3: LD_IX(LDA;LDX); +case 0xB3: LD_IY(LDA;LDX); + +/* NOP */ +case 0x1A: +case 0x3A: +case 0x5A: +case 0x7A: +case 0xDA: +case 0xFA: break; + +/* RLA */ +case 0x27: RMW_ZP(ROL;AND); +case 0x37: RMW_ZPX(ROL;AND); +case 0x2F: RMW_AB(ROL;AND); +case 0x3F: RMW_ABX(ROL;AND); +case 0x3B: RMW_ABY(ROL;AND); +case 0x23: RMW_IX(ROL;AND); +case 0x33: RMW_IY(ROL;AND); + +/* RRA */ +case 0x67: RMW_ZP(ROR;ADC); +case 0x77: RMW_ZPX(ROR;ADC); +case 0x6F: RMW_AB(ROR;ADC); +case 0x7F: RMW_ABX(ROR;ADC); +case 0x7B: RMW_ABY(ROR;ADC); +case 0x63: RMW_IX(ROR;ADC); +case 0x73: RMW_IY(ROR;ADC); + +/* SLO */ +case 0x07: RMW_ZP(ASL;ORA); +case 0x17: RMW_ZPX(ASL;ORA); +case 0x0F: RMW_AB(ASL;ORA); +case 0x1F: RMW_ABX(ASL;ORA); +case 0x1B: RMW_ABY(ASL;ORA); +case 0x03: RMW_IX(ASL;ORA); +case 0x13: RMW_IY(ASL;ORA); + +/* SRE */ +case 0x47: RMW_ZP(LSR;EOR); +case 0x57: RMW_ZPX(LSR;EOR); +case 0x4F: RMW_AB(LSR;EOR); +case 0x5F: RMW_ABX(LSR;EOR); +case 0x5B: RMW_ABY(LSR;EOR); +case 0x43: RMW_IX(LSR;EOR); +case 0x53: RMW_IY(LSR;EOR); + +/* AXA - SHA */ +case 0x93: ST_IY(_A&_X&(((A-_Y)>>8)+1)); +case 0x9F: ST_ABY(_A&_X&(((A-_Y)>>8)+1)); + +/* SYA */ +case 0x9C: ST_ABX(_Y&(((A-_X)>>8)+1)); + +/* SXA */ +case 0x9E: ST_ABY(_X&(((A-_Y)>>8)+1)); + +/* XAS */ +case 0x9B: _S=_A&_X;ST_ABY(_S& (((A-_Y)>>8)+1) ); + +/* TOP */ +case 0x0C: LD_AB(;); +case 0x1C: +case 0x3C: +case 0x5C: +case 0x7C: +case 0xDC: +case 0xFC: LD_ABX(;); + +/* XAA - BIG QUESTION MARK HERE */ +case 0x8B: _A|=0xEE; _A&=_X; LD_IM(AND); diff --git a/palette.h b/palette.h new file mode 100644 index 0000000..9a4647c --- /dev/null +++ b/palette.h @@ -0,0 +1,120 @@ +pal rp2c04001[64] = { + #include "palettes/rp2c04001.h" +}; + +pal NSFPalette[39] = { + #include "palettes/nsfnew.h" +}; + +pal palettevseb[64] = { +#include "palettes/vseb.h" +}; + +pal palettevsslalom[64] = { +#include "palettes/vsslalom.h" +}; + +pal palettevsgoon[64] = { +#include "palettes/vsgoonies.h" +}; + +pal palettevsgrad[64] = { +#include "palettes/vsplatoon.h" +}; + +pal palettevscv[64] = { +#include "palettes/vscv.h" +}; + +pal palettevssmb[64] = { +#include "palettes/vssmb.h" +}; + +pal palettevsmar[64] = { +#include "palettes/vsmar.h" +}; + +pal unvpalette[6] = { +{ 0x00<<2,0x00<<2,0x00<<2}, // Black +{ 0x3F<<2,0x3F<<2,0x34<<2}, // White +{ 0x00<<2,0x00<<2,0x00<<2}, // Black +{ 0x1d<<2,0x1d<<2,0x24<<2}, // Greyish +{ 190,0,0 }, // Redish +{ 51,255,51}, // Bright green +}; + + +/* These are dynamically filled/generated palettes: */ +pal palettei[64]; // Custom palette for an individual game. +pal palettec[64]; // Custom "global" palette. +pal paletten[64]; // Mathematically generated palette. + + +/* Default palette */ +pal palette[64] = { + + { 0x1D<<2, 0x1D<<2, 0x1D<<2 }, /* Value 0 */ + { 0x09<<2, 0x06<<2, 0x23<<2 }, /* Value 1 */ + { 0x00<<2, 0x00<<2, 0x2A<<2 }, /* Value 2 */ + { 0x11<<2, 0x00<<2, 0x27<<2 }, /* Value 3 */ + { 0x23<<2, 0x00<<2, 0x1D<<2 }, /* Value 4 */ + { 0x2A<<2, 0x00<<2, 0x04<<2 }, /* Value 5 */ + { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 6 */ + { 0x1F<<2, 0x02<<2, 0x00<<2 }, /* Value 7 */ + { 0x10<<2, 0x0B<<2, 0x00<<2 }, /* Value 8 */ + { 0x00<<2, 0x11<<2, 0x00<<2 }, /* Value 9 */ + { 0x00<<2, 0x14<<2, 0x00<<2 }, /* Value 10 */ + { 0x00<<2, 0x0F<<2, 0x05<<2 }, /* Value 11 */ + { 0x06<<2, 0x0F<<2, 0x17<<2 }, /* Value 12 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 13 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 14 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 15 */ + { 0x2F<<2, 0x2F<<2, 0x2F<<2 }, /* Value 16 */ + { 0x00<<2, 0x1C<<2, 0x3B<<2 }, /* Value 17 */ + { 0x08<<2, 0x0E<<2, 0x3B<<2 }, /* Value 18 */ + { 0x20<<2, 0x00<<2, 0x3C<<2 }, /* Value 19 */ + { 0x2F<<2, 0x00<<2, 0x2F<<2 }, /* Value 20 */ + { 0x39<<2, 0x00<<2, 0x16<<2 }, /* Value 21 */ + { 0x36<<2, 0x0A<<2, 0x00<<2 }, /* Value 22 */ + { 0x32<<2, 0x13<<2, 0x03<<2 }, /* Value 23 */ + { 0x22<<2, 0x1C<<2, 0x00<<2 }, /* Value 24 */ + { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 25 */ + { 0x00<<2, 0x2A<<2, 0x00<<2 }, /* Value 26 */ + { 0x00<<2, 0x24<<2, 0x0E<<2 }, /* Value 27 */ + { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 28 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 29 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 30 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 31 */ + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 32 */ + { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 33 */ + { 0x17<<2, 0x25<<2, 0x3F<<2 }, /* Value 34 */ + { 0x10<<2, 0x22<<2, 0x3F<<2 }, /* Value 35 */ + { 0x3D<<2, 0x1E<<2, 0x3F<<2 }, /* Value 36 */ + { 0x3F<<2, 0x1D<<2, 0x2D<<2 }, /* Value 37 */ + { 0x3F<<2, 0x1D<<2, 0x18<<2 }, /* Value 38 */ + { 0x3F<<2, 0x26<<2, 0x0E<<2 }, /* Value 39 */ + { 0x3C<<2, 0x2F<<2, 0x0F<<2 }, /* Value 40 */ + { 0x20<<2, 0x34<<2, 0x04<<2 }, /* Value 41 */ + { 0x13<<2, 0x37<<2, 0x12<<2 }, /* Value 42 */ + { 0x16<<2, 0x3E<<2, 0x26<<2 }, /* Value 43 */ + { 0x00<<2, 0x3A<<2, 0x36<<2 }, /* Value 44 */ + { 0x1E<<2, 0x1E<<2, 0x1E<<2 }, /* Value 45 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 46 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 47 */ + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 48 */ + { 0x2A<<2, 0x39<<2, 0x3F<<2 }, /* Value 49 */ + { 0x31<<2, 0x35<<2, 0x3F<<2 }, /* Value 50 */ + { 0x35<<2, 0x32<<2, 0x3F<<2 }, /* Value 51 */ + { 0x3F<<2, 0x31<<2, 0x3F<<2 }, /* Value 52 */ + { 0x3F<<2, 0x31<<2, 0x36<<2 }, /* Value 53 */ + { 0x3F<<2, 0x2F<<2, 0x2C<<2 }, /* Value 54 */ + { 0x3F<<2, 0x36<<2, 0x2A<<2 }, /* Value 55 */ + { 0x3F<<2, 0x39<<2, 0x28<<2 }, /* Value 56 */ + { 0x38<<2, 0x3F<<2, 0x28<<2 }, /* Value 57 */ + { 0x2A<<2, 0x3C<<2, 0x2F<<2 }, /* Value 58 */ + { 0x2C<<2, 0x3F<<2, 0x33<<2 }, /* Value 59 */ + { 0x27<<2, 0x3F<<2, 0x3C<<2 }, /* Value 60 */ + { 0x31<<2, 0x31<<2, 0x31<<2 }, /* Value 61 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 62 */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 63 */ +}; diff --git a/palettes/nsfnew.h b/palettes/nsfnew.h new file mode 100644 index 0000000..7ee0068 --- /dev/null +++ b/palettes/nsfnew.h @@ -0,0 +1,39 @@ +{0<<2,14<<2,3<<2}, +{0<<2,13<<2,3<<2}, +{0<<2,12<<2,3<<2}, +{0<<2,11<<2,3<<2}, +{0<<2,15<<2,3<<2}, +{0<<2,15<<2,4<<2}, +{0<<2,16<<2,4<<2}, +{0<<2,17<<2,4<<2}, +{0<<2,18<<2,4<<2}, +{0<<2,19<<2,4<<2}, +{0<<2,20<<2,5<<2}, +{0<<2,20<<2,4<<2}, +{0<<2,21<<2,5<<2}, +{0<<2,22<<2,5<<2}, +{0<<2,23<<2,5<<2}, +{0<<2,24<<2,5<<2}, +{0<<2,24<<2,6<<2}, +{0<<2,25<<2,6<<2}, +{0<<2,26<<2,6<<2}, +{0<<2,27<<2,6<<2}, +{0<<2,28<<2,6<<2}, +{0<<2,28<<2,7<<2}, +{0<<2,29<<2,7<<2}, +{0<<2,30<<2,7<<2}, +{0<<2,11<<2,2<<2}, +{0<<2,10<<2,2<<2}, +{0<<2,9<<2,2<<2}, +{0<<2,8<<2,2<<2}, +{0<<2,7<<2,1<<2}, +{0<<2,7<<2,2<<2}, +{0<<2,6<<2,1<<2}, +{0<<2,5<<2,1<<2}, +{0<<2,4<<2,1<<2}, +{0<<2,3<<2,1<<2}, +{0<<2,2<<2,0<<2}, +{0<<2,3<<2,0<<2}, +{0<<2,1<<2,0<<2}, +{0<<2,0<<2,0<<2}, +{3<<2,0<<2,43<<2}, diff --git a/palettes/rp2c04001.h b/palettes/rp2c04001.h new file mode 100644 index 0000000..f2526b9 --- /dev/null +++ b/palettes/rp2c04001.h @@ -0,0 +1,64 @@ +{ 0x3f<<2, 0x31<<2, 0x36<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x36<<2, 0x0a<<2, 0x00<<2, }, +{ 0x17<<2, 0x25<<2, 0x3f<<2, }, +{ 0x00<<2, 0x20<<2, 0x22<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x39<<2, 0x00<<2, 0x16<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x1d<<2, 0x1d<<2, 0x1d<<2, }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2, }, +{ 0x2a<<2, 0x00<<2, 0x04<<2, }, +{ 0x23<<2, 0x00<<2, 0x1d<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x10<<2, 0x0b<<2, 0x00<<2, }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2, }, +{ 0x0f<<2, 0x2f<<2, 0x3f<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x20<<2, 0x34<<2, 0x04<<2, }, +{ 0x27<<2, 0x3f<<2, 0x3c<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x3f<<2, 0x2f<<2, 0x2c<<2, }, +{ 0x08<<2, 0x0e<<2, 0x3b<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x16<<2, 0x3e<<2, 0x26<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x09<<2, 0x06<<2, 0x23<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x2a<<2, 0x39<<2, 0x3f<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x13<<2, 0x37<<2, 0x12<<2, }, +{ 0x00<<2, 0x3a<<2, 0x36<<2, }, +{ 0x06<<2, 0x0f<<2, 0x17<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x1f<<2, 0x02<<2, 0x00<<2, }, +{ 0x3f<<2, 0x31<<2, 0x3f<<2, }, +{ 0x29<<2, 0x00<<2, 0x00<<2, }, +{ 0x20<<2, 0x00<<2, 0x3c<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x3f<<2, 0x1d<<2, 0x18<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x25<<2, 0x00<<2, }, +{ 0x2f<<2, 0x2f<<2, 0x2f<<2, }, +{ 0x00<<2, 0x14<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x32<<2, 0x13<<2, 0x03<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x1c<<2, 0x3b<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, +{ 0x3f<<2, 0x1d<<2, 0x2d<<2, }, +{ 0x22<<2, 0x1c<<2, 0x00<<2, }, +{ 0x00<<2, 0x00<<2, 0x00<<2, }, diff --git a/palettes/vscv.h b/palettes/vscv.h new file mode 100644 index 0000000..8220f30 --- /dev/null +++ b/palettes/vscv.h @@ -0,0 +1,64 @@ +{127,127,127}, +{255,163,71}, +{0,0,191}, +{71,43,191}, +{151,0,135}, +{248,88,152}, +{171,19,0}, +{248,184,248}, +{191,0,0}, +{0,120,0}, +{0,107,0}, +{0,91,0}, +{255,255,255}, +{152,120,248}, +{0,0,0}, +{0,0,0}, +{191,191,191}, +{0,120,248}, +{171,19,0}, +{107,71,255}, +{0,174,0}, +{231,0,91}, +{248,56,0}, +{119,119,255}, +{175,127,0}, +{0,184,0}, +{0,171,0}, +{0,171,71}, +{0,139,139}, +{0,0,0}, +{0,0,0}, +{71,43,191}, +{248,248,248}, +{255,227,171}, +{248,120,88}, +{152,120,248}, +{0,120,248}, +{248,88,152}, +{191,191,191}, +{255,163,71}, +{200,0,200}, +{184,248,24}, +{127,127,127}, +{0,120,0}, +{0,235,219}, +{0,0,0}, +{0,0,0}, +{255,255,255}, +{255,255,255}, +{167,231,255}, +{91,219,87}, +{231,95,19}, +{0,67,88}, +{0,0,255}, +{231,0,91}, +{0,184,0}, +{251,219,123}, +{216,248,120}, +{139,23,0}, +{255,227,171}, +{0,255,255}, +{171,0,35}, +{0,0,0}, +{0,0,0}, diff --git a/palettes/vseb.h b/palettes/vseb.h new file mode 100644 index 0000000..234d993 --- /dev/null +++ b/palettes/vseb.h @@ -0,0 +1,64 @@ +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x2a<<2, 0x00<<2}, +{0x3f<<2, 0x3f<<2, 0x3f<<2}, +{0x27<<2, 0x3f<<2, 0x3c<<2}, +{0x00<<2, 0x11<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x2a<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x3f<<2, 0x3f<<2, 0x3f<<2}, +{0x31<<2, 0x35<<2, 0x3f<<2}, +{0x31,0xa6,0xf6}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x08<<2, 0x0e<<2, 0x3b<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x22<<2, 0x1c<<2, 0x00<<2}, +{0x32<<2, 0x13<<2, 0x03<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x06<<2, 0x0f<<2, 0x17<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x2a<<2}, +{0x36<<2, 0x0a<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x3f<<2, 0x36<<2, 0x2a<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0xd8,0xd6,0xb1}, +{0x3f<<2, 0x26<<2, 0x0e<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x20<<2, 0x34<<2, 0x04<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x0f<<2, 0x2f<<2, 0x3f<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x1c<<2, 0x3b<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x2a<<2, 0x39<<2, 0x3f<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0xc6,0x9D,0x62}, +{0x13<<2, 0x37<<2, 0x12<<2}, +{0x3c<<2, 0x2f<<2, 0x0f<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x20<<2, 0x00<<2, 0x3c<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, +{0x00<<2, 0x00<<2, 0x00<<2}, diff --git a/palettes/vsgoonies.h b/palettes/vsgoonies.h new file mode 100644 index 0000000..9dd272e --- /dev/null +++ b/palettes/vsgoonies.h @@ -0,0 +1,65 @@ + { 0x1D<<2, 0x1D<<2, 0x1D <<2}, /* Value 0 */ + { 0x09<<2, 0x06<<2, 0x23 <<2}, /* Value 1 */ + { 0x00<<2, 0x00<<2, 0x2A <<2}, /* Value 2 */ + { 0x2f<<2, 0x2f<<2, 0x2f <<2}, /* Value 3 */ + { 0x23<<2, 0x00<<2, 0x1D <<2}, /* Value 4 */ + { 0x3f<<2, 0x3f<<2, 0x3f <<2}, /* Value 5 */ + { 0x2a<<2, 0x39<<2, 0x3f <<2}, /* Value 6 */ + { 0x1F<<2, 0x02<<2, 0x00 <<2}, /* Value 7 */ + { 0x09<<2, 0x06<<2, 0x23 <<2}, /* Value 8 */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 9 */ + { 0x3f<<2, 0x2f<<2, 0x2c <<2}, /* Value a */ + { 0x00<<2, 0x0F<<2, 0x05 <<2}, /* Value b */ + { 0x06<<2, 0x0F<<2, 0x17 <<2}, /* Value c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value e */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value f */ + { 0x2F<<2, 0x2F<<2, 0x2F <<2}, /* Value 10 */ + { 0x00<<2, 0x1C<<2, 0x3B <<2}, /* Value 11 */ + { 0x08<<2, 0x0E<<2, 0x3B <<2}, /* Value 12 */ + { 0x08<<2, 0x0e<<2, 0x3b <<2}, /* Value 13 */ + { 0x00<<2, 0x25<<2, 0x00 <<2}, /* Value 14 */ + { 0x39<<2, 0x00<<2, 0x16 <<2}, /* Value 15 */ + { 0x32<<2, 0x13<<2, 0x03 <<2}, /* Value 16 */ + { 0x32<<2, 0x13<<2, 0x03 <<2}, /* Value 17 */ + { 0x1d<<2, 0x1d<<2, 0x1d <<2}, /* Value 18 */ + { 0x00<<2, 0x25<<2, 0x00 <<2}, /* Value 19 */ + { 0x00<<2, 0x2A<<2, 0x00 <<2}, /* Value 1a */ + { 0x00<<2, 0x00<<2, 0x2a <<2}, /* Value 1b */ + { 0x36<<2, 0x0a<<2, 0x00 <<2}, /* Value 1c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 1d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 1e */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 1f */ + { 0x3F<<2, 0x3F<<2, 0x3F <<2}, /* Value 20 */ + { 0x0F<<2, 0x2F<<2, 0x3F <<2}, /* Value 21 */ + { 0x17<<2, 0x25<<2, 0x3F <<2}, /* Value 22 */ + { 0x10<<2, 0x22<<2, 0x3F <<2}, /* Value 23 */ + { 0x3D<<2, 0x1E<<2, 0x3F <<2}, /* Value 24 */ + { 0x3F<<2, 0x26<<2, 0x0e <<2}, /* Value 25 */ + { 0x3F<<2, 0x1D<<2, 0x18 <<2}, /* Value 26 */ + { 0x3F<<2, 0x3f<<2, 0x3f <<2}, /* Value 27 */ + { 0x3C<<2, 0x2F<<2, 0x0F <<2}, /* Value 28 */ + { 0x20<<2, 0x34<<2, 0x04 <<2}, /* Value 29 */ + { 0x17<<2, 0x25<<2, 0x3f <<2}, /* Value 2a */ + { 0x16<<2, 0x3E<<2, 0x26 <<2}, /* Value 2b */ + { 0x00<<2, 0x1c<<2, 0x3b <<2}, /* Value 2c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 2d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 2e */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 2f */ + { 0x3F<<2, 0x3F<<2, 0x3F <<2}, /* Value 30 */ + { 0x2A<<2, 0x39<<2, 0x3F <<2}, /* Value 31 */ + { 0x31<<2, 0x35<<2, 0x3F <<2}, /* Value 32 */ + { 0x35<<2, 0x32<<2, 0x3F <<2}, /* Value 33 */ + { 0x1F<<2, 0x02<<2, 0x00 <<2}, /* Value 34 */ + { 0x3F<<2, 0x31<<2, 0x36 <<2}, /* Value 35 */ + { 0x3F<<2, 0x2F<<2, 0x2C <<2}, /* Value 36 */ + { 0x3F<<2, 0x36<<2, 0x2A <<2}, /* Value 37 */ + { 0x3F<<2, 0x39<<2, 0x28 <<2}, /* Value 38 */ + { 0x38<<2, 0x3F<<2, 0x28 <<2}, /* Value 39 */ + { 0x2A<<2, 0x3C<<2, 0x2F <<2}, /* Value 3a */ + { 0x2C<<2, 0x3F<<2, 0x33 <<2}, /* Value 3b */ + { 0x27<<2, 0x3F<<2, 0x3C <<2}, /* Value 3c */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 3d */ + { 0x00<<2, 0x00<<2, 0x00 <<2}, /* Value 3e */ + { 0x06<<2, 0x0f<<2, 0x17 <<2}, /* Value 3f */ + diff --git a/palettes/vsgrad.h b/palettes/vsgrad.h new file mode 100644 index 0000000..3f8c6fe --- /dev/null +++ b/palettes/vsgrad.h @@ -0,0 +1,65 @@ + { 0x3f<<2, 0x31<<2, 0x36<<2 }, /* Value 0 */ // Done + { 0x09<<2, 0x06<<2, 0x23<<2 }, /* Value 1 */ + { 0x36<<2, 0x0a<<2, 0x00<<2 }, /* Value 2 */ // Done + { 0x11<<2, 0x00<<2, 0x27<<2 }, /* Value 3 */ + { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 4 */ // Done + { 0x2A<<2, 0x00<<2, 0x04<<2 }, /* Value 5 */ + { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 6 */ + { 0x1F<<2, 0x02<<2, 0x00<<2 }, /* Value 7 */ + { 0x10<<2, 0x0B<<2, 0x00<<2 }, /* Value 8 */ + { 0x00<<2, 0x11<<2, 0x00<<2 }, /* Value 9 */ + { 0x3f<<2, 0x26<<2, 0x0e<<2 }, /* Value a */ // Done + { 0x00<<2, 0x0F<<2, 0x05<<2 }, /* Value b */ + { 0x06<<2, 0x0F<<2, 0x17<<2 }, /* Value c */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value d */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value e */ + { 0x3f<<2, 0x3f<<2, 0x3f<<2 }, /* Value f */ // Done + { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 10 */ //Done + { 0x00<<2, 0x1C<<2, 0x3B<<2 }, /* Value 11 */ + { 0x08<<2, 0x0E<<2, 0x3B<<2 }, /* Value 12 */ + { 0x20<<2, 0x00<<2, 0x3C<<2 }, /* Value 13 */ + { 0x2F<<2, 0x00<<2, 0x2F<<2 }, /* Value 14 */ + { 0x39<<2, 0x00<<2, 0x16<<2 }, /* Value 15 */ + { 0x36<<2, 0x0A<<2, 0x00<<2 }, /* Value 16 */ + { 0x08<<2, 0x0e<<2, 0x3b<<2 }, /* Value 17 */ // Done + { 0x22<<2, 0x1C<<2, 0x00<<2 }, /* Value 18 */ + { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 19 */ + { 0x00<<2, 0x2A<<2, 0x00<<2 }, /* Value 1a */ + { 0x00<<2, 0x24<<2, 0x0E<<2 }, /* Value 1b */ + { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 1c */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 1d */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 1e */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 1f */ + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 20 */ + { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 21 */ + { 0x17<<2, 0x25<<2, 0x3F<<2 }, /* Value 22 */ + { 0x0f<<2, 0x2f<<2, 0x3f<<2 }, /* Value 23 */ // Done + { 0x00<<2, 0x3a<<2, 0x36<<2 }, /* Value 24 */ // Done + { 0x06<<2, 0x0f<<2, 0x17<<2 }, /* Value 25 */ // Done + { 0x3F<<2, 0x1D<<2, 0x18<<2 }, /* Value 26 */ + { 0x3F<<2, 0x26<<2, 0x0E<<2 }, /* Value 27 */ + { 0x3C<<2, 0x2F<<2, 0x0F<<2 }, /* Value 28 */ + { 0x1f<<2, 0x02<<2, 0x00<<2 }, /* Value 29 */ // Done + { 0x13<<2, 0x37<<2, 0x12<<2 }, /* Value 2a */ + { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 2b */ // Done + { 0x00<<2, 0x3A<<2, 0x36<<2 }, /* Value 2c */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 2d */ + { 0x3f<<2, 0x1d<<2, 0x18<<2 }, /* Value 2e */ // done + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 2f */ // done + { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 30 */ + { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 31 */ // Done + { 0x2f<<2, 0x2f<<2, 0x2F<<2 }, /* Value 32 */ // Done + { 0x35<<2, 0x32<<2, 0x3F<<2 }, /* Value 33 */ + { 0x3F<<2, 0x31<<2, 0x3F<<2 }, /* Value 34 */ + { 0x3F<<2, 0x31<<2, 0x36<<2 }, /* Value 35 */ + { 0x3F<<2, 0x2F<<2, 0x2C<<2 }, /* Value 36 */ + { 0x32<<2, 0x13<<2, 0x03<<2 }, /* Value 37 */ // Done + { 0x3F<<2, 0x39<<2, 0x28<<2 }, /* Value 38 */ + { 0x38<<2, 0x3F<<2, 0x28<<2 }, /* Value 39 */ + { 0x2A<<2, 0x3C<<2, 0x2F<<2 }, /* Value 3a */ + { 0x2C<<2, 0x3F<<2, 0x33<<2 }, /* Value 3b */ + { 0x27<<2, 0x3F<<2, 0x3C<<2 }, /* Value 3c */ + { 0x3f<<2, 0x1d<<2, 0x2d<<2 }, /* Value 3d */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 3e */ + { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 3f */ + diff --git a/palettes/vsmar.h b/palettes/vsmar.h new file mode 100644 index 0000000..5eb7143 --- /dev/null +++ b/palettes/vsmar.h @@ -0,0 +1,64 @@ +{ 0x1d<<2, 0x1d<<2, 0x1d<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x11<<2, 0x00<<2, 0x27<<2 }, +{ 0x00<<2, 0x2a<<2, 0x00<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x2a<<2, 0x39<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x39<<2, 0x00<<2, 0x16<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x17<<2, 0x25<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x22<<2, 0x1c<<2, 0x00<<2 }, +{ 0x32<<2, 0x13<<2, 0x03<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x1d<<2, 0x1d<<2, 0x1d<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x20<<2, 0x34<<2, 0x04<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x0f<<2, 0x2f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x1c<<2, 0x3b<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x3a<<2, 0x36<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x3a<<2, 0x36<<2 }, +{ 0x3c<<2, 0x2f<<2, 0x0f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x14<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x31<<2, 0x35<<2, 0x3f<<2 }, +{ 0x3f<<2, 0x36<<2, 0x2a<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, diff --git a/palettes/vsplatoon.h b/palettes/vsplatoon.h new file mode 100644 index 0000000..7d7dce4 --- /dev/null +++ b/palettes/vsplatoon.h @@ -0,0 +1,64 @@ +{63<<2,49<<2,54<<2}, +{9<<2,6<<2,35<<2}, +{54<<2,10<<2,0<<2}, +{17<<2,0<<2,39<<2}, +{0<<2,32<<2,34<<2}, +{0<<2,17<<2,0<<2}, +{41<<2,0<<2,0<<2}, +{63<<2,29<<2,45<<2}, +{63<<2,63<<2,63<<2}, +{0<<2,17<<2,0<<2}, +{63<<2,38<<2,14<<2}, +{42<<2,0<<2,4<<2}, +{6<<2,15<<2,23<<2}, +{32<<2,52<<2,4<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,63<<2,63<<2}, +{15<<2,47<<2,63<<2}, +{0<<2,28<<2,59<<2}, +{8<<2,14<<2,59<<2}, +{63<<2,63<<2,63<<2}, +{47<<2,0<<2,47<<2}, +{57<<2,0<<2,22<<2}, +{63<<2,47<<2,44<<2}, +{8<<2,14<<2,59<<2}, +{34<<2,28<<2,0<<2}, +{0<<2,37<<2,0<<2}, +{0<<2,42<<2,0<<2}, +{0<<2,36<<2,14<<2}, +{0<<2,32<<2,34<<2}, +{0<<2,0<<2,0<<2}, +{0<<2,0<<2,0<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,63<<2,63<<2}, +{15<<2,47<<2,63<<2}, +{23<<2,37<<2,63<<2}, +{19<<2,55<<2,18<<2}, +{0<<2,58<<2,54<<2}, +{6<<2,15<<2,23<<2}, +{63<<2,29<<2,24<<2}, +{63<<2,38<<2,14<<2}, +{60<<2,47<<2,15<<2}, +{31<<2,2<<2,0<<2}, +{19<<2,55<<2,18<<2}, +{41<<2,0<<2,0<<2}, +{0<<2,58<<2,54<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,29<<2,24<<2}, +{0<<2,0<<2,0<<2}, +{63<<2,63<<2,63<<2}, +{0<<2,37<<2,0<<2}, +{47<<2,47<<2,47<<2}, +{0<<2,20<<2,0<<2}, +{63<<2,49<<2,63<<2}, +{63<<2,49<<2,54<<2}, +{63<<2,38<<2,14<<2}, +{54<<2,10<<2,0<<2}, +{63<<2,57<<2,40<<2}, +{15<<2,47<<2,63<<2}, +{42<<2,60<<2,47<<2}, +{44<<2,63<<2,51<<2}, +{39<<2,63<<2,60<<2}, +{63<<2,29<<2,45<<2}, +{34<<2,28<<2,0<<2}, +{0<<2,0<<2,0<<2} diff --git a/palettes/vsslalom.h b/palettes/vsslalom.h new file mode 100644 index 0000000..4328b1c --- /dev/null +++ b/palettes/vsslalom.h @@ -0,0 +1,64 @@ +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x1d<<2, 0x18<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x1d<<2, 0x2d<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x2f<<2, 0x00<<2, 0x2f<<2 }, +{ 0x27<<2, 0x3f<<2, 0x3c<<2 }, +{ 0x00<<2, 0x25<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x0f<<2, 0x2f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x16<<2, 0x3e<<2, 0x26<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3d<<2, 0x1e<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x2c<<2, 0x3f<<2, 0x33<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x09<<2, 0x06<<2, 0x23<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x35<<2, 0x32<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x2f<<2, 0x2f<<2, 0x2f<<2 }, +{ 0x17<<2, 0x25<<2, 0x3f<<2 }, +{ 0x2f<<2, 0x00<<2, 0x2f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x17<<2, 0x25<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x3f<<2, 0x3f<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x13<<2, 0x37<<2, 0x12<<2 }, +{ 0x3f<<2, 0x26<<2, 0x0e<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x27<<2, 0x3f<<2, 0x3c<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x3f<<2, 0x39<<2, 0x28<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, +{ 0x00<<2, 0x00<<2, 0x00<<2 }, diff --git a/palettes/vssmb.h b/palettes/vssmb.h new file mode 100644 index 0000000..f942610 --- /dev/null +++ b/palettes/vssmb.h @@ -0,0 +1,65 @@ +{98,106,0}, // 0 +{0,0,255}, // 1 +{0,106,119}, // 2 +{71,43,191}, // 3 +{151,0,135}, // 4 +{171,0,35}, // 5 +{0x24,0x18,0x8c}, // 6 +{0xc8,0x4c,0x0c}, // 7 +{162,162,162},// 8 +{0,120,0}, // 9 +{0x4c,0xDC,0x48}, // a +{0,91,0}, // b +{255,213,153},// c +{255,255,0}, // d +{0,153,0}, // e +{0,0,0}, // f +{255,102,255},// 10 +{0,120,248}, // 11 +{0x20,0x38,0xec}, // 12 +{107,71,255}, // 13 +{0,0,0}, // 14 +{231,0,91}, // 15 +{248,56,0}, // 16 +{251,0x74,0x60}, // 17 +{175,127,0}, // 18 +{0,184,0}, // 19 +{81,115,255}, // 1a +{0,171,71}, // 1b +{0,139,139}, // 1c +{0,0,0}, // 1d +{145,255,136},// 1e +{0x3F,0xbF,0xFF}, // 1f +{248,248,248},// 20 +{0,0x50,0}, // 21 +{107,0,0}, // 22 +{72,85,248}, // 23 +{248,120,248},// 24 +{248,88,152}, // 25 +{89,89,88}, // 26 +{249,0,0x58}, // 27 +{0,47,47}, // 28 +{184,248,24}, // 29 + +{0x3f,0xbf,0xff}, // 2a +{88,248,152}, // 2b +{0,235,219}, // 2c +{120,120,120},// 2d +{0,0,0}, // 2e +{0,0,0}, // 2f +{255,255,255},// 30 +{167,231,255},// 31 +{89,4,0}, // 32 +{187,0,0}, // 33 +{248,184,248},// 34 +{251,167,195},// 35 +{255,255,255},// 36 +{0,227,225}, // 37 +{251,219,123},// 38 +{255,174,15}, // 39 +{184,248,184},// 3a +{184,248,216},// 3b +{0x80,0xd0,0x10}, // 3c +{248,216,248},// 3d +{255,170,170},// 3e +{0,64,0}, // 3f diff --git a/sound.c b/sound.c new file mode 100644 index 0000000..76f85a0 --- /dev/null +++ b/sound.c @@ -0,0 +1,1029 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/********************************************************/ +/******* sound.c */ +/******* */ +/******* Sound emulation code and waveform synthesis */ +/******* routines. A few ideas were inspired */ +/******* by code from Marat Fayzullin's EMUlib */ +/******* */ +/********************************************************/ + +#include +#include + +#include + +#include "types.h" +#include "x6502.h" + +#include "fce.h" +#include "svga.h" +#include "sound.h" + +uint32 soundtsinc; +uint32 soundtsi; + +uint32 Wave[2048]; +int32 WaveFinal[2048]; + +EXPSOUND GameExpSound={0,0,0}; + +uint8 trimode=0; +uint8 tricoop=0; +uint8 PSG[0x18]; + +uint8 decvolume[3]; +uint8 realvolume[3]; + +static int32 count[5]; +static int64 sqacc[2]={0,0}; +uint8 sqnon=0; + +#undef printf +uint16 nreg; + +int32 lengthcount[4]; + +static const uint8 Slengthtable[0x20]= +{ + 0x5,0x7f,0xA,0x1,0x14,0x2,0x28,0x3,0x50,0x4,0x1E,0x5,0x7,0x6,0x0E,0x7, + 0x6,0x08,0xC,0x9,0x18,0xa,0x30,0xb,0x60,0xc,0x24,0xd,0x8,0xe,0x10,0xf +}; + +static uint32 lengthtable[0x20]; + +static const uint32 SNoiseFreqTable[0x10]= +{ + 2,4,8,0x10,0x20,0x30,0x40,0x50,0x65,0x7f,0xbe,0xfe,0x17d,0x1fc,0x3f9,0x7f2 +}; +static uint32 NoiseFreqTable[0x10]; + +int64 nesincsizeLL; + +static const uint8 NTSCPCMTable[0x10]= +{ + 0xd6,0xbe,0xaa,0xa0,0x8f,0x7f,0x71,0x6b, + 0x5f,0x50,0x47,0x40,0x35,0x2a,0x24,0x1b +}; + +static const uint8 PALPCMTable[0x10]= // These values are just guessed. +{ + 0xc6,0xb0,0x9d,0x94,0x84,0x75,0x68,0x63, + 0x58,0x4a,0x41,0x3b,0x31,0x27,0x21,0x19 +}; + +uint32 PSG_base; + +// $4010 - Frequency +// $4011 - Actual data outputted +// $4012 - Address register: $c000 + V*64 +// $4013 - Size register: Size in bytes = (V+1)*64 + + +static int64 PCMacc=0; +static int PCMfreq; +int32 PCMIRQCount; +uint8 PCMBitIndex=0; +uint32 PCMAddressIndex=0; +int32 PCMSizeIndex=0; +uint8 PCMBuffer=0; +int vdis=0; + +static void Dummyfunc(void) {}; + +static void (*DoNoise)(void)=Dummyfunc; +static void (*DoTriangle)(void)=Dummyfunc; +static void (*DoPCM)(void)=Dummyfunc; +static void (*DoSQ1)(void)=Dummyfunc; +static void (*DoSQ2)(void)=Dummyfunc; + +static void CalcDPCMIRQ(void) +{ + uint32 freq; + uint32 honk; + uint32 cycles; + + if(PAL) + freq=(PALPCMTable[PSG[0x10]&0xF]<<4); + else + freq=(NTSCPCMTable[PSG[0x10]&0xF]<<4); + + cycles=(((PSG[0x13]<<4)+1)); + cycles*=freq/14; + honk=((PSG[0x13]<<4)+1)*freq; + honk-=cycles; + //if(PAL) honk/=107; + //else honk/=(double)113.66666666; + PCMIRQCount=honk*48; + //PCMIRQCount=honk*3; //180; + //if(PAL) PCMIRQCount*=.93; + vdis=0; +} + +static void PrepDPCM() +{ + PCMAddressIndex=0x4000+(PSG[0x12]<<6); + PCMSizeIndex=(PSG[0x13]<<4)+1; + PCMBitIndex=0; + //PCMBuffer=ARead[0x8000+PCMAddressIndex](0x8000+PCMAddressIndex); + if(PAL) + PCMfreq=PALPCMTable[PSG[0x10]&0xF]; + else + PCMfreq=NTSCPCMTable[PSG[0x10]&0xF]; + PCMacc=(int64)PCMfreq<<50; +} + +uint8 sweepon[2]={0,0}; +int32 curfreq[2]={0,0}; + + +uint8 SIRQStat=0; + +uint8 SweepCount[2]; +uint8 DecCountTo1[3]; + +uint8 fcnt=0; +int32 fhcnt=0; +int32 fhinc; + +static uint8 laster; + +/* Instantaneous? Maybe the new freq value is being calculated all of the time... */ +static int FASTAPASS(2) CheckFreq(uint32 cf, uint8 sr) +{ + uint32 mod; + if(!(sr&0x8)) + { + mod=cf>>(sr&7); + if((mod+cf)&0x800) + return(0); + } + return(1); +} + +static DECLFW(Write0x11) +{ + DoPCM(); + PSG[0x11]=V&0x7F; +} + +static uint8 DutyCount[2]={0,0}; + +static DECLFW(Write_PSG) +{ + //if((A>=0x4004 && A<=0x4007) || A==0x4015) + //printf("$%04x:$%02x, %d\n",A,V,timestamp); + A&=0x1f; + switch(A) + { + case 0x0: + DoSQ1(); + if(V&0x10) + realvolume[0]=V&0xF; + break; + case 0x1: + sweepon[0]=V&0x80; + break; + case 0x2: + DoSQ1(); + curfreq[0]&=0xFF00; + curfreq[0]|=V; + break; + case 0x3: + if(PSG[0x15]&1) + { + DoSQ1(); + lengthcount[0]=lengthtable[(V>>3)&0x1f]; + sqnon|=1; + } + sweepon[0]=PSG[1]&0x80; + curfreq[0]=PSG[0x2]|((V&7)<<8); + decvolume[0]=0xF; + DecCountTo1[0]=(PSG[0]&0xF)+1; + SweepCount[0]=((PSG[0x1]>>4)&7)+1; + DutyCount[0]=0; + sqacc[0]=((int64)curfreq[0]+1)<<50; + break; + + case 0x4: + DoSQ2(); + if(V&0x10) + realvolume[1]=V&0xF; + break; + case 0x5: + sweepon[1]=V&0x80; + break; + case 0x6: + DoSQ2(); + curfreq[1]&=0xFF00; + curfreq[1]|=V; + break; + case 0x7: + if(PSG[0x15]&2) + { + DoSQ2(); + lengthcount[1]=lengthtable[(V>>3)&0x1f]; + sqnon|=2; + } + sweepon[1]=PSG[0x5]&0x80; + curfreq[1]=PSG[0x6]|((V&7)<<8); + decvolume[1]=0xF; + DecCountTo1[1]=(PSG[0x4]&0xF)+1; + SweepCount[1]=((PSG[0x5]>>4)&7)+1; + DutyCount[1]=0; + sqacc[1]=((int64)curfreq[1]+1)<<50; + break; + case 0x8: + DoTriangle(); + if(laster&0x80) + { + tricoop=V&0x7F; + trimode=V&0x80; + } + if(!(V&0x7F)) + tricoop=0; + laster=V&0x80; + break; + case 0xa:DoTriangle(); + break; + case 0xb: + if(PSG[0x15]&0x4) + { + DoTriangle(); + sqnon|=4; + lengthcount[2]=lengthtable[(V>>3)&0x1f]; + } + laster=0x80; + tricoop=PSG[0x8]&0x7f; + trimode=PSG[0x8]&0x80; + break; + case 0xC:DoNoise(); + if(V&0x10) + realvolume[2]=V&0xF; + break; + case 0xE:DoNoise();break; + case 0xF: + if(PSG[0x15]&8) + { + DoNoise(); + sqnon|=8; + lengthcount[3]=lengthtable[(V>>3)&0x1f]; + } + decvolume[2]=0xF; + DecCountTo1[2]=(PSG[0xC]&0xF)+1; + break; + case 0x10:DoPCM(); + if(!(V&0x80)) + X6502_IRQEnd(FCEU_IQDPCM); + break; + case 0x15: + { + int t=V^PSG[0x15]; + + if(t&1) + DoSQ1(); + if(t&2) + DoSQ2(); + if(t&4) + DoTriangle(); + if(t&8) + DoNoise(); + if(t&0x10) + DoPCM(); + sqnon&=V; + if(V&0x10) + { + if(!(PSG[0x15]&0x10)) + { + PrepDPCM(); + CalcDPCMIRQ(); + } + else if(vdis) + CalcDPCMIRQ(); + } + else + PCMIRQCount=0; + X6502_IRQEnd(FCEU_IQDPCM); + } + break; + case 0x17: + V&=0xC0; + fcnt=0; + if(V&0x80) + FrameSoundUpdate(); + fhcnt=fhinc; + X6502_IRQEnd(FCEU_IQFCOUNT); + SIRQStat&=~0x40; + break; + } + PSG[A]=V; +} + +DECLFR(Read_PSG) +{ + uint8 ret; + if(PSG[0x15]&0x10) + DoPCM(); + ret=(PSG[0x15]&(sqnon|0x10))|SIRQStat; + SIRQStat&=~0x40; + X6502_IRQEnd(/*FCEU_IQDPCM|*/FCEU_IQFCOUNT); + return ret; +} + +DECLFR(Read_PSGDummy) +{ + uint8 ret; + + ret=(PSG[0x15]&sqnon)|SIRQStat; + SIRQStat&=~0x40; + X6502_IRQEnd(/*FCEU_IQDPCM|*/FCEU_IQFCOUNT); + return ret; +} + +static void FASTAPASS(1) FrameSoundStuff(int V) +{ + int P; + + DoSQ1(); + DoSQ2(); + DoNoise(); + + switch((V&1)) + { + case 1: /* Envelope decay, linear counter, length counter, freq sweep */ + if(PSG[0x15]&4 && sqnon&4) + if(!(PSG[8]&0x80)) + { + if(lengthcount[2]>0) + { + lengthcount[2]--; + if(lengthcount[2]<=0) + { + DoTriangle(); + sqnon&=~4; + } + } + } + + for(P=0;P<2;P++) + { + if(PSG[0x15]&(P+1) && sqnon&(P+1)) + { + if(!(PSG[P<<2]&0x20)) + { + if(lengthcount[P]>0) + { + lengthcount[P]--; + if(lengthcount[P]<=0) + { + sqnon&=~(P+1); + } + } + } + } + /* Frequency Sweep Code Here */ + /* xxxx 0000 */ + /* xxxx = hz. 120/(x+1)*/ + if(sweepon[P]) + { + int32 mod=0; + + if(SweepCount[P]>0) SweepCount[P]--; + if(SweepCount[P]<=0) + { + SweepCount[P]=((PSG[(P<<2)+0x1]>>4)&7)+1; //+1; + { + if(PSG[(P<<2)+0x1]&0x8) + { + mod-=(P^1)+((curfreq[P])>>(PSG[(P<<2)+0x1]&7)); + + if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/) + { + curfreq[P]+=mod; + } + } + else + { + mod=curfreq[P]>>(PSG[(P<<2)+0x1]&7); + if((mod+curfreq[P])&0x800) + { + sweepon[P]=0; + curfreq[P]=0; + } + else + { + if(curfreq[P] && (PSG[(P<<2)+0x1]&7)/* && sweepon[P]&0x80*/) + { + curfreq[P]+=mod; + } + } + } + } + } + } + } + + if(PSG[0x15]&0x8 && sqnon&8) + { + if(!(PSG[0xC]&0x20)) + { + if(lengthcount[3]>0) + { + lengthcount[3]--; + if(lengthcount[3]<=0) + { + sqnon&=~8; + } + } + } + } + + case 0: /* Envelope decay + linear counter */ + if(!trimode) + { + laster=0; + if(tricoop) + { + if(tricoop==1) DoTriangle(); + tricoop--; + } + } + + for(P=0;P<2;P++) + { + if(DecCountTo1[P]>0) DecCountTo1[P]--; + if(DecCountTo1[P]<=0) + { + DecCountTo1[P]=(PSG[P<<2]&0xF)+1; + if(decvolume[P] || PSG[P<<2]&0x20) + { + decvolume[P]--; + /* Step from 0 to full volume seems to take twice as long + as the other steps. I don't know if this is the correct + way to double its length, though(or if it even matters). + */ + if((PSG[P<<2]&0x20) && (decvolume[P]==0)) + DecCountTo1[P]<<=1; + decvolume[P]&=15; + } + } + if(!(PSG[P<<2]&0x10)) + realvolume[P]=decvolume[P]; + } + + if(DecCountTo1[2]>0) DecCountTo1[2]--; + if(DecCountTo1[2]<=0) + { + DecCountTo1[2]=(PSG[0xC]&0xF)+1; + if(decvolume[2] || PSG[0xC]&0x20) + { + decvolume[2]--; + /* Step from 0 to full volume seems to take twice as long + as the other steps. I don't know if this is the correct + way to double its length, though(or if it even matters). + */ + if((PSG[0xC]&0x20) && (decvolume[2]==0)) + DecCountTo1[2]<<=1; + decvolume[2]&=15; + } + } + if(!(PSG[0xC]&0x10)) + realvolume[2]=decvolume[2]; + + break; + } + +} + +void FrameSoundUpdate(void) +{ + // Linear counter: Bit 0-6 of $4008 + // Length counter: Bit 4-7 of $4003, $4007, $400b, $400f + + if(fcnt==3) + { + if(PSG[0x17]&0x80) + fhcnt+=fhinc; + if(!(PSG[0x17]&0xC0)) + { + SIRQStat|=0x40; + X6502_IRQBegin(FCEU_IQFCOUNT); + } + } + //if(SIRQStat&0x40) X6502_IRQBegin(FCEU_IQFCOUNT); + FrameSoundStuff(fcnt); + fcnt=(fcnt+1)&3; +} + +static uint32 ChannelBC[5]; + +static uint32 RectAmp[2][8]; + +static void FASTAPASS(1) CalcRectAmp(int P) +{ + static int tal[4]={1,2,4,6}; + int V; + int x; + uint32 *b=RectAmp[P]; + int m; + + //if(PSG[P<<2]&0x10) + V=realvolume[P]<<4; + //V=(PSG[P<<2]&15)<<4; + //else + // V=decvolume[P]<<4; + m=tal[(PSG[P<<2]&0xC0)>>6]; + for(x=0;x>4]+=PSG[0x11]<<3; + goto endopcmo; + } + } + else + { + PCMBuffer=ARead[0x8000+PCMAddressIndex](0x8000+PCMAddressIndex); + PCMAddressIndex=(PCMAddressIndex+1)&0x7fff; + } + } + + { + int t=(((PCMBuffer>>PCMBitIndex)&1)<<2)-2; + uint8 bah=PSG[0x11]; + + PCMacc+=freq; + PSG[0x11]+=t; + if(PSG[0x11]&0x80) + PSG[0x11]=bah; + else + out=PSG[0x11]<<3; + } + PCMBitIndex=(PCMBitIndex+1)&7; + } + Wave[V>>4]+=out; //(PSG[0x11]-64)<<3; + } + } + else + { + if((end-start)>64) + { + for(V=start;V<=(start|15);V++) + Wave[V>>4]+=out; + out<<=4; + for(V=(start>>4)+1;V<(end>>4);V++) + Wave[V]+=out; + out>>=4; + for(V=end&(~15);V>4]+=out; + } + else + for(V=start;V>4]+=out; + } + endopcmo:; +} + +static void RDoSQ1(void) +{ + int32 V; + int32 start,end; + int64 freq; + + CalcRectAmp(0); + start=ChannelBC[0]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[0]=end; + + if(curfreq[0]<8 || curfreq[0]>0x7ff) + return; + if(!CheckFreq(curfreq[0],PSG[0x1])) + return; + + if(PSG[0x15]&1 && sqnon&1) + { + uint32 out=RectAmp[0][DutyCount[0]]; + freq=curfreq[0]+1; + { + freq<<=50; + for(V=start;V>4]+=out; + sqacc[0]-=nesincsizeLL; + if(sqacc[0]<=0) + { + rea: + sqacc[0]+=freq; + DutyCount[0]++; + if(sqacc[0]<=0) goto rea; + + DutyCount[0]&=7; + out=RectAmp[0][DutyCount[0]]; + } + + } + } + } +} + +static void RDoSQ2(void) +{ + int32 V; + int32 start,end; + int64 freq; + + CalcRectAmp(1); + start=ChannelBC[1]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[1]=end; + + if(curfreq[1]<8 || curfreq[1]>0x7ff) + return; + if(!CheckFreq(curfreq[1],PSG[0x5])) + return; + + if(PSG[0x15]&2 && sqnon&2) + { + uint32 out=RectAmp[1][DutyCount[1]]; + freq=curfreq[1]+1; + + { + freq<<=50; + for(V=start;V>4]+=out; + sqacc[1]-=nesincsizeLL; + if(sqacc[1]<=0) + { + rea: + sqacc[1]+=freq; + DutyCount[1]++; + if(sqacc[1]<=0) goto rea; + + DutyCount[1]&=7; + out=RectAmp[1][DutyCount[1]]; + } + + } + } + } +} + + +static void RDoTriangle(void) +{ + static uint32 tcout=0; + int32 V; + int32 start,end; //,freq; + int64 freq=(((PSG[0xa]|((PSG[0xb]&7)<<8))+1)); + + start=ChannelBC[2]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[2]=end; + + if(! (PSG[0x15]&0x4 && sqnon&4 && tricoop) ) + { // Counter is halted, but we still need to output. + for(V=start;V>4]+=tcout; + } + else if(freq<=4) // 55.9Khz - Might be barely audible on a real NES, but + // it's too costly to generate audio at this high of a frequency + // (55.9Khz * 32 for the stepping). + // The same could probably be said for ~27.8Khz, so we'll + // take care of that too. We'll just output the average + // value(15/2 - scaled properly for our output format, of course). + // We'll also take care of ~18Khz and ~14Khz too, since they should be barely audible. + // (Some proof or anything to confirm/disprove this would be nice.). + { + for(V=start;V>4]+=((0xF<<4)+(0xF<<2))>>1; + } + else + { + static int64 triacc=0; + static uint8 tc=0; + + freq<<=49; + for(V=start;V>4]+=tcout; + } + } +} + +static void RDoNoise(void) +{ + int32 inc,V; + int32 start,end; + + start=ChannelBC[3]; + end=(timestamp<<16)/soundtsinc; + if(end<=start) return; + ChannelBC[3]=end; + + if(PSG[0x15]&0x8 && sqnon&8) + { + uint32 outo; + uint32 amptab[2]; + uint8 amplitude; + + amplitude=realvolume[2]; + //if(PSG[0xC]&0x10) + // amplitude=(PSG[0xC]&0xF); + //else + // amplitude=decvolume[2]&0xF; + + inc=NoiseFreqTable[PSG[0xE]&0xF]; + amptab[0]=((amplitude<<2)+amplitude+amplitude)<<1; + amptab[1]=0; + outo=amptab[nreg&1]; + + if(amplitude) + { + if(PSG[0xE]&0x80) // "short" noise + for(V=start;V>4]+=outo; + if(count[3]>=inc) + { + uint8 feedback; + + feedback=((nreg>>8)&1)^((nreg>>14)&1); + nreg=(nreg<<1)+feedback; + nreg&=0x7fff; + outo=amptab[nreg&1]; + count[3]-=inc; + } + count[3]+=0x1000; + } + else + for(V=start;V>4]+=outo; + if(count[3]>=inc) + { + uint8 feedback; + + feedback=((nreg>>13)&1)^((nreg>>14)&1); + nreg=(nreg<<1)+feedback; + nreg&=0x7fff; + outo=amptab[nreg&1]; + count[3]-=inc; + } + count[3]+=0x1000; + } + } + + } +} + +void SetNESSoundMap(void) +{ + SetWriteHandler(0x4000,0x4013,Write_PSG); + SetWriteHandler(0x4011,0x4011,Write0x11); + SetWriteHandler(0x4015,0x4015,Write_PSG); + SetWriteHandler(0x4017,0x4017,Write_PSG); + SetReadHandler(0x4015,0x4015,Read_PSG); +} + +static int32 WaveNSF[256]; + +int64 highp; // 0 through 65536, 0 = no high pass, 65536 = max high pass + +int64 lowp; // 0 through 65536, 65536 = max low pass(total attenuation) + // 65536 = no low pass +static void FilterSound(uint32 *in, int32 *out, int count) +{ + static int64 acc=0, acc2=0; + + for(;count;count--,in++,out++) + { + int64 diff; + + diff=((int64)*in<<24)-acc; + + acc+=(diff*highp)>>16; + acc2+=((diff-acc2)*lowp)>>16; + *in=0; + *out=(acc2*(int64)FSettings.SoundVolume)>>(24+16); + if(*out<-32767) *out=-32767; + if(*out>32767) *out=32767; + //*out=((int64)(acc2>>24)*(int64)FSettings.SoundVolume)>>16; //acc2>>24; + } +} + +int FlushEmulateSound(void) +{ + uint32 end; + int x; + + if(!timestamp) return(0); + + if(!FSettings.SndRate) + { + end=0; + goto nosoundo; + } + + end=(timestamp<<16)/soundtsinc; + DoSQ1(); + DoSQ2(); + DoTriangle(); + DoNoise(); + DoPCM(); + + if(GameExpSound.Fill) + GameExpSound.Fill(end&0xF); + + FilterSound(Wave,WaveFinal,end>>4); + + if(FCEUGameInfo.type==GIT_NSF) + { + int x,s=0,si=end/1024; // Only want 1/4 of the output buffer to be displayed + for(x=0;x<256;x++) + { + WaveNSF[x]=WaveFinal[s>>4]; + s+=si; + } + } + + if(end&0xF) + Wave[0]=Wave[(end>>4)]; + Wave[(end>>4)]=0; + + nosoundo: + for(x=0;x<5;x++) + ChannelBC[x]=end&0xF; + timestampbase+=timestamp; + timestamp=(soundtsinc*(end&0xF))>>16; + timestampbase-=timestamp; + return(end>>4); +} + +void GetSoundBuffer(int32 **W) +{ + *W=WaveNSF; +} + +void PowerSound(void) +{ + int x; + + SetNESSoundMap(); + + for(x=0;x<0x16;x++) + if(x!=0x14) + BWrite[0x4000+x](0x4000+x,0); + PSG[0x17]=0; //x40; + fhcnt=fhinc; + fcnt=0; + nreg=1; +} + +void ResetSound(void) +{ + int x; + for(x=0;x<0x16;x++) + if(x!=1 && x!=5 && x!=0x14) BWrite[0x4000+x](0x4000+x,0); + PSG[0x17]=0; + fhcnt=fhinc; + fcnt=0; + nreg=1; +} + +void SetSoundVariables(void) +{ + int x; + + fhinc=PAL?16626:14915; // *2 CPU clock rate + fhinc*=24; + for(x=0;x<0x20;x++) + lengthtable[x]=Slengthtable[x]<<1; + + if(FSettings.SndRate) + { + DoNoise=RDoNoise; + DoTriangle=RDoTriangle; + DoPCM=RDoPCM; + DoSQ1=RDoSQ1; + DoSQ2=RDoSQ2; + } + else + { + DoNoise=DoTriangle=DoPCM=DoSQ1=DoSQ2=Dummyfunc; + } + + if(!FSettings.SndRate) return; + if(GameExpSound.RChange) + GameExpSound.RChange(); + + nesincsizeLL=(int64)((int64)562949953421312*(long double)(PAL?PAL_CPU:NTSC_CPU)/(FSettings.SndRate OVERSAMPLE)); + PSG_base=(uint32)(PAL?(long double)PAL_CPU/16:(long double)NTSC_CPU/16); + + for(x=0;x<0x10;x++) + { + long double z; + z=SNoiseFreqTable[x]<<1; + z=(PAL?PAL_CPU:NTSC_CPU)/z; + z=(long double)((uint32)((FSettings.SndRate OVERSAMPLE)<<12))/z; + NoiseFreqTable[x]=z; + } + soundtsinc=(uint32)((uint64)(PAL?(long double)PAL_CPU*65536:(long double)NTSC_CPU*65536)/(FSettings.SndRate OVERSAMPLE)); + memset(Wave,0,2048*4); + for(x=0;x<5;x++) + ChannelBC[x]=0; + highp=(250<<16)/FSettings.SndRate; // Arbitrary + lowp=((int64)25000<<16)/FSettings.SndRate; // Arbitrary + + if(highp>(1<<16)) highp=1<<16; + if(lowp>(1<<16)) lowp=1<<16; +} + +void FixOldSaveStateSFreq(void) +{ + int x; + for(x=0;x<2;x++) + { + curfreq[x]=PSG[0x2+(x<<2)]|((PSG[0x3+(x<<2)]&7)<<8); + } +} + +void FCEUI_Sound(int Rate) +{ + FSettings.SndRate=Rate; + SetSoundVariables(); +} + +void FCEUI_SetSoundVolume(uint32 volume) +{ + FSettings.SoundVolume=(volume<<16)/100; +} diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..53c397b --- /dev/null +++ b/sound.h @@ -0,0 +1,73 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 OVERSAMPLESHIFT 4 +#define OVERSAMPLE *16 +#define SND_BUFSIZE 256 + + +typedef struct { + void (*Fill)(int Count); + void (*RChange)(void); + void (*Kill)(void); +} EXPSOUND; + +extern EXPSOUND GameExpSound; + +extern int64 nesincsizeLL; +extern uint8 PSG[]; +extern uint32 PSG_base; +extern int32 PCMIRQCount; + +void SetSoundVariables(void); +void PowerSound(void); +void ResetSound(void); +extern uint8 decvolume[]; + +extern int vdis; +extern uint8 sqnon; +extern uint16 nreg; + +extern uint8 trimode; +extern uint8 tricoop; +extern uint8 PCMBitIndex; +extern uint32 PCMAddressIndex; +extern int32 PCMSizeIndex; +extern uint8 PCMBuffer; + +extern uint8 sweepon[2]; +extern int32 curfreq[2]; + +extern uint8 SweepCount[2]; +extern uint8 DecCountTo1[3]; + +extern uint8 fcnt; +extern int32 fhcnt; +extern int32 fhinc; + +void GetSoundBuffer(int32 **W); +int FlushEmulateSound(void); +extern uint32 Wave[2048]; +extern int32 WaveFinal[2048]; +extern uint32 soundtsinc; + +void SetNESSoundMap(void); +void FrameSoundUpdate(void); +void FixOldSaveStateSFreq(void); diff --git a/state.c b/state.c new file mode 100644 index 0000000..ac48818 --- /dev/null +++ b/state.c @@ -0,0 +1,607 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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: Add (better) file io error checking */ +/* TODO: Change save state file format. */ + +#include +#include +#include + +#include "types.h" +#include "x6502.h" +#include "version.h" +#include "fce.h" +#include "sound.h" +#define INESPRIV // Take this out when old save state support is removed in a future version. +#include "ines.h" +#include "svga.h" +#include "endian.h" +#include "fds.h" +#include "general.h" +#include "state.h" +#include "memory.h" + +static SFORMAT SFMDATA[64]; +static int SFEXINDEX; +static int stateversion; + +#define RLSB 0x80000000 + +#define SFCPUELEMENTS 7 + +SFORMAT SFCPU[SFCPUELEMENTS]={ + { &X.PC, 2|RLSB, "PC\0"}, + { &X.A, 1, "A\0\0"}, + { &X.P, 1, "P\0\0"}, + { &X.X, 1, "X\0\0"}, + { &X.Y, 1, "Y\0\0"}, + { &X.S, 1, "S\0\0"}, + { RAM, 0x800, "RAM"} +}; + +#define SFCPUCELEMENTS 6 +SFORMAT SFCPUC[SFCPUCELEMENTS]={ + { &X.jammed, 1, "JAMM"}, + { &X.IRQlow, 1, "IRQL"}, + { &X.tcount, 4|RLSB, "ICoa"}, + { &X.count, 4|RLSB, "ICou"}, + { ×tamp, 4|RLSB, "TIME"}, + { ×tampbase, 8|RLSB, "TMEB"} +}; + +static uint16 TempAddrT,RefreshAddrT; + +#define SFPPUELEMENTS 10 +SFORMAT SFPPU[SFPPUELEMENTS]={ + { NTARAM, 0x800, "NTAR"}, + { PALRAM, 0x20, "PRAM"}, + { SPRAM, 0x100, "SPRA"}, + { PPU, 0x4, "PPUR"}, + { &XOffset, 1, "XOFF"}, + { &vtoggle, 1, "VTOG"}, + { &RefreshAddrT, 2|RLSB, "RADD"}, + { &TempAddrT, 2|RLSB, "TADD"}, + { &VRAMBuffer, 1, "VBUF"}, + { &PPUGenLatch, 1, "PGEN"}, +}; + +// Is this chunk necessary? I'll fix it later. +//#define SFCTLRELEMENTS 2 +//SFORMAT SFCTLR[SFCTLRELEMENTS]={ +// { &joy_readbit, 1, "J1RB"}, +// { &joy2_readbit, 1, "J2RB"} +//}; + +#define SFSNDELEMENTS 18 +SFORMAT SFSND[SFSNDELEMENTS]={ + { &fhcnt, 4|RLSB,"FHCN"}, + { &fcnt, 1, "FCNT"}, + { PSG, 14, "PSG"}, + { &PSG[0x15], 1, "P15"}, + { &PSG[0x17], 1, "P17"}, + { decvolume, 3, "DECV"}, + { &sqnon, 1, "SQNO"}, + { &nreg, 2|RLSB, "NREG"}, + { &trimode, 1, "TRIM"}, + { &tricoop, 1, "TRIC"}, + { sweepon, 2, "SWEE"}, + { &curfreq[0], 4|RLSB,"CRF1"}, + { &curfreq[1], 4|RLSB,"CRF2"}, + { SweepCount, 2,"SWCT"}, + { DecCountTo1, 3,"DCT1"}, + { &PCMBitIndex, 1,"PBIN"}, + { &PCMAddressIndex, 4|RLSB, "PAIN"}, + { &PCMSizeIndex, 4|RLSB, "PSIN"} +}; + + +int WriteStateChunk(FILE *st, int type, SFORMAT *sf, int count) +{ + int bsize; + int x; + + fputc(type,st); + + for(x=bsize=0;x=0;z--) + { + fputc(*(uint8*)sf[x].v,st); + } + } + else + fwrite((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st); + } + #endif + } + return (bsize+5); +} + +int ReadStateChunk(FILE *st, SFORMAT *sf, int count, int size) +{ + uint8 tmpyo[16]; + int bsize; + int x; + + for(x=bsize=0;x=53) + bsize+=count<<3; + else + { + if(bsize!=size) + { + fseek(st,size,SEEK_CUR); + return 0; + } + } + + if(stateversion<56) + memcpy(tmpyo,mapbyte3,16); + + if(stateversion>=53) + { + int temp; + temp=ftell(st); + + while(ftell(st)=0;z--) + *(uint8*)sf[x].v=fgetc(st); + } + else + #endif + { + fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st); + } + goto bloo; + } + } + nkayo: + fseek(st,tsize,SEEK_CUR); + bloo:; + } // while(...) + } // >=53 + else + { + for(x=0;x=0;z--) + { + *(uint8*)sf[x].v=fgetc(st); + } + else + fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st); + #endif + } + } + if(stateversion<56) + { + for(x=0;x<16;x++) + #ifdef LSB_FIRST + mapbyte1[x]=mapbyte1[x<<1]; + #else + mapbyte1[x]=mapbyte1[(x<<1)+1]; + #endif + memcpy(mapbyte3,tmpyo,16); + } + return 1; +} + +int ReadStateChunks(FILE *st) +{ + int t; + uint32 size; + int ret=1; + +for(;;) + { + t=fgetc(st); + if(t==EOF) break; + if(!read32(&size,st)) break; + switch(t) + { + case 1:if(!ReadStateChunk(st,SFCPU,SFCPUELEMENTS,size)) ret=0;break; + case 2:if(!ReadStateChunk(st,SFCPUC,SFCPUCELEMENTS,size)) ret=0; + else + { + X.mooPI=X.P; // Quick and dirty hack. + } + break; + case 3:if(!ReadStateChunk(st,SFPPU,SFPPUELEMENTS,size)) ret=0;break; +// case 4:if(!ReadStateChunk(st,SFCTLR,SFCTLRELEMENTS,size)) ret=0;break; + case 5:if(!ReadStateChunk(st,SFSND,SFSNDELEMENTS,size)) ret=0;break; + case 0x10:if(!ReadStateChunk(st,SFMDATA,SFEXINDEX,size)) ret=0;break; + default: if(fseek(st,size,SEEK_CUR)<0) goto endo;break; + } + } + endo: + return ret; +} + + +int CurrentState=0; +extern int geniestage; +void SaveState(void) +{ + FILE *st=NULL; + + TempAddrT=TempAddr; + RefreshAddrT=RefreshAddr; + + if(geniestage==1) + { + FCEU_DispMessage("Cannot save FCS in GG screen."); + return; + } + + st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"wb"); + + if(st!=NULL) + { + static uint32 totalsize; + static uint8 header[16]="FCS"; + memset(header+4,0,13); + header[3]=VERSION_NUMERIC; + fwrite(header,1,16,st); + + totalsize=WriteStateChunk(st,1,SFCPU,SFCPUELEMENTS); + totalsize+=WriteStateChunk(st,2,SFCPUC,SFCPUCELEMENTS); + totalsize+=WriteStateChunk(st,3,SFPPU,SFPPUELEMENTS); + // totalsize+=WriteStateChunk(st,4,SFCTLR,SFCTLRELEMENTS); + totalsize+=WriteStateChunk(st,5,SFSND,SFSNDELEMENTS); + totalsize+=WriteStateChunk(st,0x10,SFMDATA,SFEXINDEX); + + fseek(st,4,SEEK_SET); + write32(totalsize,st); + SaveStateStatus[CurrentState]=1; + fclose(st); + FCEU_DispMessage("State %d saved.",CurrentState); + } + else + FCEU_DispMessage("State %d save error.",CurrentState); +} + +static int LoadStateOld(FILE *st); +void LoadState(void) +{ + int x; + FILE *st=NULL; + + if(geniestage==1) + { + FCEU_DispMessage("Cannot load FCS in GG screen."); + return; + } + + st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"rb"); + + if(st!=NULL) + { + uint8 header[16]; + + fread(&header,1,16,st); + if(memcmp(header,"FCS",3)) + { + fseek(st,0,SEEK_SET); + if(!LoadStateOld(st)) + goto lerror; + goto okload; + } + stateversion=header[3]; + if(stateversion<53) + FixOldSaveStateSFreq(); + x=ReadStateChunks(st); + if(GameStateRestore) GameStateRestore(header[3]); + if(x) + { + okload: + TempAddr=TempAddrT; + RefreshAddr=RefreshAddrT; + + SaveStateStatus[CurrentState]=1; + FCEU_DispMessage("State %d loaded.",CurrentState); + SaveStateStatus[CurrentState]=1; + } + else + { + SaveStateStatus[CurrentState]=1; + FCEU_DispMessage("Error(s) reading state %d!",CurrentState); + } + } + else + { + lerror: + FCEU_DispMessage("State %d load error.",CurrentState); + SaveStateStatus[CurrentState]=0; + return; + } + fclose(st); +} + +char SaveStateStatus[10]; +void CheckStates(void) +{ + FILE *st=NULL; + int ssel; + + if(SaveStateStatus[0]==-1) + for(ssel=0;ssel<10;ssel++) + { + st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb"); + if(st) + { + SaveStateStatus[ssel]=1; + fclose(st); + } + else + SaveStateStatus[ssel]=0; + } +} + +void SaveStateRefresh(void) +{ + SaveStateStatus[0]=-1; +} + +void ResetExState(void) +{ + int x; + for(x=0;x=31) + PSG[0x17]=MapperExRAM[115]; + else + PSG[0x17]|=0x40; + PSG[0x15]&=0xF; + sqnon=PSG[0x15]; + + X.IRQlow=0; + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&nada,1,1); + afread(&XOffset,1,1); + PPUCHRRAM=0; + for(x=0;x<8;x++) + { + nada=0; + afread(&nada,1,1); + PPUCHRRAM|=(nada?1:0)< +#include +#include +#include + +#include + + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "types.h" +#include "svga.h" +#include "fce.h" +#include "general.h" +#include "video.h" +#include "sound.h" +#include "version.h" +#include "nsf.h" +#include "palette.h" +#include "fds.h" +#include "netplay.h" +#include "state.h" +#include "cart.h" +#include "input.h" + +FCEUS FSettings; + +static int howlong; +static char errmsg[65]; + +void FCEU_PrintError(char *format, ...) +{ + char temp[2048]; + + va_list ap; + + va_start(ap,format); + vsprintf(temp,format,ap); + FCEUD_PrintError(temp); + + va_end(ap); +} + +void FCEU_DispMessage(char *format, ...) +{ + va_list ap; + + va_start(ap,format); + vsprintf(errmsg,format,ap); + va_end(ap); + + howlong=180; +} + +void FCEUI_SetRenderedLines(int ntscf, int ntscl, int palf, int pall) +{ + FSettings.UsrFirstSLine[0]=ntscf; + FSettings.UsrLastSLine[0]=ntscl; + FSettings.UsrFirstSLine[1]=palf; + FSettings.UsrLastSLine[1]=pall; + if(PAL) + { + FSettings.FirstSLine=FSettings.UsrFirstSLine[1]; + FSettings.LastSLine=FSettings.UsrLastSLine[1]; + } + else + { + FSettings.FirstSLine=FSettings.UsrFirstSLine[0]; + FSettings.LastSLine=FSettings.UsrLastSLine[0]; + } + +} + +void FCEUI_SetVidSystem(int a) +{ + FSettings.PAL=a?1:0; + FCEU_ResetVidSys(); + FCEU_ResetPalette(); +} + +int FCEUI_GetCurrentVidSystem(int *slstart, int *slend) +{ + if(slstart) + *slstart=FSettings.FirstSLine; + if(slend) + *slend=FSettings.LastSLine; + return(PAL); +} + +#ifdef NETWORK +void FCEUI_SetNetworkPlay(int type) +{ + FSettings.NetworkPlay=type; +} +#endif + +void FCEUI_SetGameGenie(int a) +{ + FSettings.GameGenie=a?1:0; +} + +static void CalculatePalette(void); +static void ChoosePalette(void); +static void WritePalette(void); + +#ifndef NETWORK +#define netplay 0 +#endif + +static uint8 StateShow=0; + +uint8 Exit=0; + +uint8 DIPS=0; +uint8 vsdip=0; +int coinon=0; + +uint8 pale=0; +uint8 CommandQueue=0; + +static int controlselect=0; +static int ntsccol=0; +static int ntsctint=46+10; +static int ntschue=72; +static int controllength=0; + +pal *palo; +static pal *palpoint[8]= + { + palette, + palettevscv, + palettevssmb, + palettevsmar, + palettevsgoon, + palettevsslalom, + palettevseb, + rp2c04001 + }; + +void FCEUI_SetSnapName(int a) +{ + FSettings.SnapName=a; +} + +void FCEUI_SaveExtraDataUnderBase(int a) +{ + FSettings.SUnderBase=a; +} + +void FCEUI_SetPaletteArray(uint8 *pal) +{ + if(!pal) + palpoint[0]=palette; + else + { + int x; + palpoint[0]=palettec; + for(x=0;x<64;x++) + { + palpoint[0][x].r=*((uint8 *)pal+x+x+x); + palpoint[0][x].g=*((uint8 *)pal+x+x+x+1); + palpoint[0][x].b=*((uint8 *)pal+x+x+x+2); + } + } + FCEU_ResetPalette(); +} + +void FCEUI_SelectState(int w) +{ + if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF) + CommandQueue=42+w; +} + +void FCEUI_SaveState(void) +{ + if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF) + CommandQueue=40; +} + +void FCEUI_LoadState(void) +{ + if(netplay!=2 && FCEUGameInfo.type!=GIT_NSF) + CommandQueue=41; +} + +int32 FCEUI_GetDesiredFPS(void) +{ + if(PAL) + return(838977920); // ~50.007 + else + return(1008307711); // ~60.1 +} + +static int dosnapsave=0; +void FCEUI_SaveSnapshot(void) +{ + dosnapsave=1; +} + +/* I like the sounds of breaking necks. */ +static void ReallySnap(void) +{ + int x=SaveSnapshot(); + if(!x) + FCEU_DispMessage("Error saving screen snapshot."); + else + FCEU_DispMessage("Screen snapshot %d saved.",x-1); +} + +void DriverInterface(int w, void *d) +{ + switch(w) + { + case DES_NTSCCOL:ntsccol=*(int *)d;FCEU_ResetPalette();break; + case DES_RESET:if(netplay!=2) CommandQueue=30;break; + case DES_POWER:if(netplay!=2) CommandQueue=31;break; + case DES_GETNTSCTINT:*(int*)d=ntsctint;break; + case DES_GETNTSCHUE:*(int*)d=ntschue;break; + case DES_SETNTSCTINT:ntsctint=*(int*)d;if(ntsccol)FCEU_ResetPalette();break; + case DES_SETNTSCHUE:ntschue=*(int*)d;if(ntsccol)FCEU_ResetPalette();break; + + case DES_FDSINSERT:if(netplay!=2) CommandQueue=2;break; + case DES_FDSEJECT:if(netplay!=2) CommandQueue=3;break; + case DES_FDSSELECT:if(netplay!=2) CommandQueue=1;break; + + case DES_NSFINC:NSFControl(1);break; + case DES_NSFDEC:NSFControl(2);break; + case DES_NSFRES:NSFControl(0);break; + + case DES_VSUNIDIPSET:CommandQueue=10+(int)d;break; + case DES_VSUNITOGGLEDIPVIEW:CommandQueue=10;break; + case DES_VSUNICOIN:CommandQueue=19;break; + case DES_NTSCSELHUE:if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF){controlselect=1;controllength=360;}break; + case DES_NTSCSELTINT:if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF){controlselect=2;controllength=360;}break; + + case DES_NTSCDEC: + if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF) + { + char which; + if(controlselect) + { + if(controllength) + { + which=controlselect==1?ntschue:ntsctint; + which--; + if(which<0) which=0; + if(controlselect==1) + ntschue=which; + else ntsctint=which; + CalculatePalette(); + } + controllength=360; + } + } + break; + case DES_NTSCINC: + if(ntsccol && FCEUGameInfo.type!=GIT_VSUNI && !PAL && FCEUGameInfo.type!=GIT_NSF) + if(controlselect) + { + if(controllength) + { + switch(controlselect) + { + case 1:ntschue++; + if(ntschue>128) ntschue=128; + CalculatePalette(); + break; + case 2:ntsctint++; + if(ntsctint>128) ntsctint=128; + CalculatePalette(); + break; + } + } + controllength=360; + } + break; + } +} + +static uint8 lastd=0; +void SetNESDeemph(uint8 d, int force) +{ + static uint16 rtmul[7]={32768*1.239,32768*.794,32768*1.019,32768*.905,32768*1.023,32768*.741,32768*.75}; + static uint16 gtmul[7]={32768*.915,32768*1.086,32768*.98,32768*1.026,32768*.908,32768*.987,32768*.75}; + static uint16 btmul[7]={32768*.743,32768*.882,32768*.653,32768*1.277,32768*.979,32768*.101,32768*.75}; + uint32 r,g,b; + int x; + + /* If it's not forced(only forced when the palette changes), + don't waste cpu time if the same deemphasis bits are set as the last call. + */ + if(!force) + { + if(d==lastd) + return; + } + else /* Only set this when palette has changed. */ + { + r=rtmul[6]; + g=rtmul[6]; + b=rtmul[6]; + + for(x=0;x<0x40;x++) + { + uint32 m,n,o; + m=palo[x].r; + n=palo[x].g; + o=palo[x].b; + m=(m*r)>>15; + n=(n*g)>>15; + o=(o*b)>>15; + if(m>0xff) m=0xff; + if(n>0xff) n=0xff; + if(o>0xff) o=0xff; + FCEUD_SetPalette(x|0x40,m,n,o); + } + } + if(!d) return; /* No deemphasis, so return. */ + + r=rtmul[d-1]; + g=gtmul[d-1]; + b=btmul[d-1]; + + for(x=0;x<0x40;x++) + { + uint32 m,n,o; + + m=palo[x].r; + n=palo[x].g; + o=palo[x].b; + m=(m*r)>>15; + n=(n*g)>>15; + o=(o*b)>>15; + if(m>0xff) m=0xff; + if(n>0xff) n=0xff; + if(o>0xff) o=0xff; + + FCEUD_SetPalette(x|0xC0,m,n,o); + } + + lastd=d; +} + +#define HUEVAL ((double)((double)ntschue/(double)2)+(double)300) +#define TINTVAL ((double)((double)ntsctint/(double)128)) + +static void CalculatePalette(void) +{ + int x,z; + int r,g,b; + double s,y,theta; + static uint8 cols[16]={0,24,21,18,15,12,9,6,3,0,33,30,27,0,0,0}; + static uint8 br1[4]={6,9,12,12}; + static double br2[4]={.29,.45,.73,.9}; + static double br3[4]={0,.24,.47,.77}; + + for(x=0;x<=3;x++) + for(z=0;z<16;z++) + { + s=(double)TINTVAL; + y=(double)br2[x]; + if(z==0) {s=0;y=((double)br1[x])/12;} + + if(z>=13) + { + s=y=0; + if(z==13) + y=(double)br3[x]; + } + + theta=(double)M_PI*(double)(((double)cols[z]*10+HUEVAL)/(double)180); + r=(int)(((double)y+(double)s*(double)sin(theta))*(double)256); + g=(int)(((double)y-(double)((double)27/(double)53)*s*(double)sin(theta)+(double)((double)10/(double)53)*s*cos(theta))*(double)256); + b=(int)(((double)y-(double)s*(double)cos(theta))*(double)256); + + // TODO: Fix RGB to compensate for phosphor changes(add to red??). + + if(r>255) r=255; + if(g>255) g=255; + if(b>255) b=255; + if(r<0) r=0; + if(g<0) g=0; + if(b<0) b=0; + + paletten[(x<<4)+z].r=r; + paletten[(x<<4)+z].g=g; + paletten[(x<<4)+z].b=b; + } + WritePalette(); +} + +#include "drawing.h" +#ifdef FRAMESKIP +void FCEU_PutImageDummy(void) +{ + if(FCEUGameInfo.type!=GIT_NSF) + { + if(controllength) controllength--; + } + if(StateShow) StateShow--; /* DrawState() */ + if(howlong) howlong--; /* DrawMessage() */ + #ifdef FPS + { + extern uint64 frcount; + frcount++; + } + #endif + +} +#endif + +void FCEU_PutImage(void) +{ + if(FCEUGameInfo.type==GIT_NSF) + { + DrawNSF(XBuf); + /* Save snapshot after NSF screen is drawn. Why would we want to + do it before? + */ + if(dosnapsave) + { + ReallySnap(); + dosnapsave=0; + } + } + else + { + /* Save snapshot before overlay stuff is written. */ + if(dosnapsave) + { + ReallySnap(); + dosnapsave=0; + } + if(FCEUGameInfo.type==GIT_VSUNI && DIPS&2) + DrawDips(); + if(StateShow) DrawState(); + if(controllength) {controllength--;DrawBars();} + } + DrawMessage(); + #ifdef FPS + { + extern uint64 frcount; + frcount++; + } + #endif + DrawInput(XBuf+8); +} + +static int ipalette=0; + +void LoadGamePalette(void) +{ + uint8 ptmp[192]; + FILE *fp; + ipalette=0; + if((fp=fopen(FCEU_MakeFName(FCEUMKF_PALETTE,0,0),"rb"))) + { + int x; + fread(ptmp,1,192,fp); + fclose(fp); + for(x=0;x<64;x++) + { + palettei[x].r=ptmp[x+x+x]; + palettei[x].g=ptmp[x+x+x+1]; + palettei[x].b=ptmp[x+x+x+2]; + } + ipalette=1; + } +} + +void FCEU_ResetPalette(void) +{ + ChoosePalette(); + WritePalette(); +} + +static void ChoosePalette(void) +{ + if(FCEUGameInfo.type==GIT_NSF) + palo=NSFPalette; + else if(ipalette) + palo=palettei; + else if(ntsccol && !PAL && FCEUGameInfo.type!=GIT_VSUNI) + { + palo=paletten; + CalculatePalette(); + } + else + palo=palpoint[pale]; +} + +void WritePalette(void) +{ + int x; + + for(x=0;x<6;x++) + FCEUD_SetPalette(x+128,unvpalette[x].r,unvpalette[x].g,unvpalette[x].b); + if(FCEUGameInfo.type==GIT_NSF) + { + for(x=0;x<39;x++) + FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b); + } + else + { + for(x=0;x<64;x++) + FCEUD_SetPalette(x,palo[x].r,palo[x].g,palo[x].b); + SetNESDeemph(lastd,1); + } +} + +void FlushCommandQueue(void) +{ + if(!netplay && CommandQueue) {DoCommand(CommandQueue);CommandQueue=0;} +} + +void DoCommand(uint8 c) +{ + switch(c) + { + case 1:FDSControl(FDS_SELECT);break; + case 2:FDSControl(FDS_IDISK);break; + case 3:FDSControl(FDS_EJECT);break; + + case 10:DIPS^=2;break; + case 11:vsdip^=1;DIPS|=2;break; + case 12:vsdip^=2;DIPS|=2;break; + case 13:vsdip^=4;DIPS|=2;break; + case 14:vsdip^=8;DIPS|=2;break; + case 15:vsdip^=0x10;DIPS|=2;break; + case 16:vsdip^=0x20;DIPS|=2;break; + case 17:vsdip^=0x40;DIPS|=2;break; + case 18:vsdip^=0x80;DIPS|=2;break; + case 19:coinon=6;break; + case 30:ResetNES();break; + case 31:PowerNES();break; + case 40:CheckStates();StateShow=0;SaveState();break; + case 41:CheckStates();StateShow=0;LoadState();break; + case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: + case 50: case 51:StateShow=180;CurrentState=c-42;CheckStates();break; + } +} diff --git a/svga.h b/svga.h new file mode 100644 index 0000000..cebd555 --- /dev/null +++ b/svga.h @@ -0,0 +1,83 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 1998 Bero + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "driver.h" +typedef struct __pal { + uint8 r; + uint8 g; + uint8 b; +} pal; + +typedef struct { + int PAL; + #ifdef NETWORK + int NetworkPlay; + #endif + int SoundVolume; + int GameGenie; + int SUnderBase; + + /* Current first and last rendered scanlines. */ + int FirstSLine; + int LastSLine; + + /* Driver code(user)-specified first and last rendered scanlines. + Usr*SLine[0] is for NTSC, Usr*SLine[1] is for PAL. + */ + int UsrFirstSLine[2]; + int UsrLastSLine[2]; + int SnapName; + unsigned int SndRate; +} FCEUS; + +extern FCEUS FSettings; + +void FCEU_PrintError(char *format, ...); +void FCEU_DispMessage(char *format, ...); + +void SetNESDeemph(uint8 d, int force); +void DrawTextTrans(uint8 *dest, uint32 width, uint8 *textmsg, uint8 fgcolor); +void FCEU_PutImage(void); +#ifdef FRAMESKIP +void FCEU_PutImageDummy(void); +#endif + +extern uint8 Exit; +extern uint8 pale; +extern uint8 vsdip; +void SetNESPalette(void); + +#define JOY_A 1 +#define JOY_B 2 +#define JOY_SELECT 4 +#define JOY_START 8 +#define JOY_UP 0x10 +#define JOY_DOWN 0x20 +#define JOY_LEFT 0x40 +#define JOY_RIGHT 0x80 + +extern pal *palo; + +void DoCommand(uint8 c); +extern uint8 CommandQueue; +void FCEU_ResetPalette(void); +void LoadGamePalette(void); +void FlushCommandQueue(void); diff --git a/types.h b/types.h new file mode 100644 index 0000000..05512f8 --- /dev/null +++ b/types.h @@ -0,0 +1,66 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2001 Aaron Oneal + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __FCEU_TYPES +#define __FCEU_TYPES + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned long uint32; + +#ifdef __GNUC__ + typedef unsigned long long uint64; + typedef long long int64; + #define INLINE inline + #define GINLINE inline +#elif MSVC + typedef __int64 int64; + typedef unsigned __int64 uint64; + #define INLINE __inline + #define GINLINE /* Can't declare a function INLINE + and global in MSVC. Bummer. + */ + #define PSS_STYLE 2 /* Does MSVC compile for anything + other than Windows/DOS targets? + */ +#endif +typedef signed char int8; +typedef signed short int16; +typedef signed long int32; +#define byte uint8 +#define word uint16 + +#ifdef __GNUC__ + #ifdef C80x86 + #define FASTAPASS(x) __attribute__((regparm(x))) + #define FP_FASTAPASS FASTAPASS + #else + #define FASTAPASS(x) + #define FP_FASTAPASS(x) + #endif +#else + #define FP_FASTAPASS(x) + #define FASTAPASS(x) __fastcall +#endif + +typedef void FP_FASTAPASS(2) (*writefunc)(uint32 A, uint8 V); +typedef uint8 FP_FASTAPASS(1) (*readfunc)(uint32 A); +#endif diff --git a/unif.c b/unif.c new file mode 100644 index 0000000..f54ad39 --- /dev/null +++ b/unif.c @@ -0,0 +1,469 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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: Battery backup file saving, mirror force */ +/* **INCOMPLETE** */ +/* Override stuff: CHR RAM instead of CHR ROM, + mirroring. +*/ + +#include +#include +#include + + +#include "types.h" +#include "fce.h" +#include "unif.h" +#include "version.h" +#include "svga.h" +#include "general.h" +#include "state.h" +#include "endian.h" +#include "file.h" +#include "cart.h" +#include "memory.h" +#include "input.h" + +typedef struct { + char ID[4]; + uint32 info; +} UNIF_HEADER; + +typedef struct { + char *name; + void (*init)(void); + int flags; +} BMAPPING; + +typedef struct { + char *name; + int (*init)(int fp); +} BFMAPPING; + +void (*BoardClose)(void); +void (*BoardPower)(void); +void (*BoardReset)(void); + +static int vramo; +static int mirrortodo; +int UNIFbattery; +static char *boardname; +static char *sboardname; +char *UNIFchrrama; + +static UNIF_HEADER unhead; +static UNIF_HEADER uchead; + + +static uint8 *malloced[32]; + +static int FixRomSize(uint32 size, uint32 minimum) +{ + int x=1; + + if(size15) + return(0); + printf(" PRG ROM %d size: %d",z,(int) uchead.info); + if(malloced[z]) + free(malloced[z]); + t=FixRomSize(uchead.info,2048); + if(!(malloced[z]=FCEU_malloc(t))) + return(0); + memset(malloced[z]+uchead.info,0xFF,t-uchead.info); + if(FCEU_fread(malloced[z],1,uchead.info,fp)!=uchead.info) + { + puts("Read Error!"); + return(0); + } + else + puts(""); + + SetupCartPRGMapping(z,malloced[z],t,0); + return(1); +} + +static int SetBoardName(int fp) +{ + if(!(boardname=FCEU_malloc(uchead.info+1))) + return(0); + FCEU_fread(boardname,1,uchead.info,fp); + boardname[uchead.info]=0; + printf(" Board name: %s\n",boardname); + sboardname=boardname; + if(!memcmp(boardname,"NES-",4) || !memcmp(boardname,"UNL-",4) || !memcmp(boardname,"HVC-",4) || !memcmp(boardname,"BTL-",4) || !memcmp(boardname,"BMC-",4)) + sboardname+=4; + return(1); +} + +static int LoadCHR(int fp) +{ + int z,t; + z=uchead.ID[3]-'0'; + if(z<0 || z>15) + return(0); + printf(" CHR ROM %d size: %d",z,(int) uchead.info); + if(malloced[16+z]) + free(malloced[16+z]); + t=FixRomSize(uchead.info,8192); + if(!(malloced[16+z]=FCEU_malloc(t))) + return(0); + memset(malloced[16+z]+uchead.info,0xFF,t-uchead.info); + if(FCEU_fread(malloced[16+z],1,uchead.info,fp)!=uchead.info) + { + puts("Read Error!"); + return(0); + } + else + puts(""); + + SetupCartCHRMapping(z,malloced[16+z],t,0); + return(1); +} + + +#define BMCFLAG_FORCE4 1 +#define BMCFLAG_CHRROK 2 // Ok for generic UNIF code to make available + // 8KB of CHR RAM if no CHR ROM is present. +#define BMC 48 + +BMAPPING bmap[BMC] = { + +/* Sachen Carts */ + { "TC-U01-1.5M", TCU01_Init,0}, + { "Sachen-8259B", S8259B_Init,BMCFLAG_CHRROK}, + { "Sachen-8259A", S8259A_Init,BMCFLAG_CHRROK}, + { "Sachen-74LS374N", S74LS374N_Init,0}, + { "SA-016-1M", SA0161M_Init,0}, + { "SA-72007", SA72007_Init,0}, + { "SA-72008", SA72008_Init,0}, + { "SA-0036", SA0036_Init,0}, + { "SA-0037", SA0037_Init,0}, + + { "H2288", H2288_Init,0}, +// /* AVE carts. */ +// { "MB-91", MB91_Init,0}, // DeathBots +// { "NINA-06", NINA06_Init,0}, // F-15 City War +// { "NINA-03", NINA03_Init,0}, // Tiles of Fate +// { "NINA-001", NINA001_Init,0}, // Impossible Mission 2 + + { "HKROM", HKROM_Init,0}, + + { "EWROM", EWROM_Init,0}, + { "EKROM", EKROM_Init,0}, + { "ELROM", ELROM_Init,0}, + { "ETROM", ETROM_Init,0}, + + { "SAROM", SAROM_Init,0}, + { "SBROM", SBROM_Init,0}, + { "SCROM", SCROM_Init,0}, + { "SEROM", SEROM_Init,0}, + { "SGROM", SGROM_Init,0}, + { "SKROM", SKROM_Init,0}, + { "SLROM", SLROM_Init,0}, + { "SL1ROM", SL1ROM_Init,0}, + { "SNROM", SNROM_Init,0}, + { "SOROM", SOROM_Init,0}, + + { "TGROM", TGROM_Init,0}, + { "TR1ROM", TFROM_Init,BMCFLAG_FORCE4}, + { "TFROM", TFROM_Init,0}, + { "TLROM", TLROM_Init,0}, + { "TKROM", TKROM_Init,0}, + { "TSROM", TSROM_Init,0}, + + { "TLSROM", TLSROM_Init,0}, + { "TKSROM", TKSROM_Init,0}, + { "TQROM", TQROM_Init,0}, + { "TVROM", TLROM_Init,BMCFLAG_FORCE4}, + + { "CPROM", CPROM_Init,0}, + { "CNROM", CNROM_Init,0}, + { "NROM", NROM256_Init,0 }, + { "RROM", NROM128_Init,0 }, + { "RROM-128", NROM128_Init,0 }, + { "NROM-128", NROM128_Init,0 }, + { "NROM-256", NROM256_Init,0 }, + { "MHROM", MHROM_Init,0}, + { "UNROM", UNROM_Init,0}, + { "MARIO1-MALEE2", MALEE_Init,0}, + { "Supervision16in1", Supervision16_Init,0}, + { "NovelDiamond9999999in1", Novel_Init,0}, + { "Super24in1SC03", Super24_Init,0} +}; + +#define BMF 7 +BFMAPPING bfunc[BMF] = { + { "CTRL", CTRL }, + { "TVCI", TVCI }, + { "BATR", EnableBattery }, + { "MIRR", DoMirroring }, + { "PRG", LoadPRG }, + { "CHR", LoadCHR }, + { "MAPR", SetBoardName } +}; + +int LoadUNIFChunks(int fp) +{ + int x; + int t; + for(;;) + { + t=FCEU_fread(&uchead,1,4,fp); + if(t<4) + { + if(t>0) + return 0; + return 1; + } + if(!(FCEU_read32(&uchead.info,fp))) + return 0; + t=0; + for(x=0;x +#include +#include + +#include "types.h" +#include "video.h" +#include "fce.h" +#include "svga.h" +#include "version.h" +#include "general.h" +#include "memory.h" + +uint8 *XBuf=NULL; + +int InitVirtualVideo(void) +{ + uint32 m; + + if(!XBuf) /* Some driver code may allocate XBuf externally. */ + if(!(XBuf = (uint8*) (FCEU_malloc((256+16) * 240 + 8)))) + return 0; + + if(sizeof(uint8*)==4) + { + m=(uint32) XBuf; + m+=8;m&=0xFFFFFFF8; + (uint32)XBuf=m; + } + + memset(XBuf,128,272*240); + return 1; +} + +#ifndef ZLIB +static uint8 pcxheader[128] = +{ + 10,5,1,8,1,0,1,0,0,1,240,0,2,1,234,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +int SaveSnapshot(void) +{ + char *fn=0; + uint8 *tmp; + int x,u,y; + FILE *pp=NULL; + + for(u=0;u<999;u++) + { + pp=fopen((fn=FCEU_MakeFName(FCEUMKF_SNAP,u,"pcx")),"rb"); + if(pp==NULL) break; + fclose(pp); + } + + if(!(pp=fopen(fn,"wb"))) + return 0; + + { + int totallines=FSettings.LastSLine-FSettings.FirstSLine+1; + + tmp=XBuf+8+FSettings.FirstSLine*272; + + pcxheader[10]=totallines; + fwrite(pcxheader,1,128,pp); + for(y=0;y=0xc0) fputc(0xC1,pp); + fputc(*tmp,pp); + tmp++; + } + tmp+=16; + } + } + + fputc(0xC,pp); + for(x=0;x<256;x++) + { + uint8 r,g,b; + + FCEUD_GetPalette(x,&r,&g,&b); + fputc(r,pp); + fputc(g,pp); + fputc(b,pp); + } + fclose(pp); + + return u+1; +} + +#else + +#include +#include "crc32.h" + +static int WritePNGChunk(FILE *fp, uint32 size, char *type, uint8 *data) +{ + uint32 crc; + + uint8 tempo[4]; + + tempo[0]=size>>24; + tempo[1]=size>>16; + tempo[2]=size>>8; + tempo[3]=size; + + if(fwrite(tempo,4,1,fp)!=1) + return 0; + if(fwrite(type,4,1,fp)!=1) + return 0; + + if(size) + if(fwrite(data,1,size,fp)!=size) + return 0; + + crc=CalcCRC32(0,type,4); + if(size) + crc=CalcCRC32(crc,data,size); + + tempo[0]=crc>>24; + tempo[1]=crc>>16; + tempo[2]=crc>>8; + tempo[3]=crc; + + if(fwrite(tempo,4,1,fp)!=1) + return 0; + return 1; +} + +int SaveSnapshot(void) +{ + char *fn=0; + int totallines=FSettings.LastSLine-FSettings.FirstSLine+1; + int x,u,y; + FILE *pp=NULL; + uint8 *compmem=NULL; + uint32 compmemsize=totallines*263+12; + + if(!(compmem=FCEU_malloc(compmemsize))) + return 0; + + for(u=0;u<999;u++) + { + pp=fopen((fn=FCEU_MakeFName(FCEUMKF_SNAP,u,"png")),"rb"); + if(pp==NULL) break; + fclose(pp); + } + + if(!(pp=fopen(fn,"wb"))) + return 0; + { + static uint8 header[8]={137,80,78,71,13,10,26,10}; + if(fwrite(header,8,1,pp)!=1) + goto PNGerr; + } + + { + uint8 chunko[13]; + + chunko[0]=chunko[1]=chunko[3]=0; + chunko[2]=0x1; // Width of 256 + + chunko[4]=chunko[5]=chunko[6]=0; + chunko[7]=totallines; // Height + + chunko[8]=8; // bit depth + chunko[9]=3; // Color type; indexed 8-bit + chunko[10]=0; // compression: deflate + chunko[11]=0; // Basic adapative filter set(though none are used). + chunko[12]=0; // No interlace. + + if(!WritePNGChunk(pp,13,"IHDR",chunko)) + goto PNGerr; + } + + { + char pdata[256*3]; + for(x=0;x<256;x++) + FCEUD_GetPalette(x,pdata+x*3,pdata+x*3+1,pdata+x*3+2); + if(!WritePNGChunk(pp,256*3,"PLTE",pdata)) + goto PNGerr; + } + + { + uint8 *tmp=XBuf+FSettings.FirstSLine*272+8; + uint8 *dest,*mal,*mork; + + /* If memory couldn't be allocated, just use XBuf(screen contents + will be corrupted for one frame, though. + */ + if(!(mal=mork=dest=malloc((totallines<<8)+totallines))) + mork=dest=XBuf; + + for(y=0;y + +#include "types.h" +#include "x6502.h" +#include "fce.h" +#include "sound.h" + +X6502 X; +uint32 timestamp; +void FP_FASTAPASS(1) (*MapIRQHook)(int a); + +#define _PC X.PC +#define _A X.A +#define _X X.X +#define _Y X.Y +#define _S X.S +#define _P X.P +#define _PI X.mooPI +#define _PZ X.PZ +#define _DB X.DB +#define _count X.count +#define _tcount X.tcount +#define _IRQlow X.IRQlow +#define _jammed X.jammed + + +static INLINE uint8 RdMem(unsigned int A) +{ + return((_DB=ARead[A](A))); +} + +static INLINE void WrMem(unsigned int A, uint8 V) +{ + BWrite[A](A,V); +} + +static INLINE uint8 RdRAM(unsigned int A) +{ + return((_DB=RAM[A])); +} + +static INLINE void WrRAM(unsigned int A, uint8 V) +{ + RAM[A]=V; +} + +static INLINE void ADDCYC(int x) +{ + _tcount+=x; + _count-=x*48; + timestamp+=x; +} + +void FASTAPASS(1) X6502_AddCycles(int x) +{ + ADDCYC(x); +} + +static INLINE void PUSH(uint8 V) +{ + WrRAM(0x100+_S,V); + _S--; +} + +static INLINE uint8 POP(void) +{ + _S++; + return(RdRAM(0x100+_S)); +} + +static uint8 ZNTable[256] = { + Z_FLAG,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG, + N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG,N_FLAG +}; +/* Some of these operations will only make sense if you know what the flag + constants are. */ +#define X_ZN(zort) _P&=~(Z_FLAG|N_FLAG);_P|=ZNTable[zort] +#define X_ZNT(zort) _P|=ZNTable[zort] + +/* Care must be taken if you want to turn this into a macro. Use { and }. */ +#define JR(); \ +{ \ + uint32 tmp; \ + int8 disp; \ + disp=RdMem(_PC++); \ + ADDCYC(1); \ + tmp=_PC; \ + _PC+=disp; \ + if((tmp^_PC)&0x100) \ + ADDCYC(1); \ +} + +#define LDA _A=x;X_ZN(_A) +#define LDX _X=x;X_ZN(_X) +#define LDY _Y=x;X_ZN(_Y) + +/* All of the freaky arithmetic operations. */ +#define AND _A&=x;X_ZN(_A) +#define BIT _P&=~(Z_FLAG|V_FLAG|N_FLAG);_P|=ZNTable[x&_A]&Z_FLAG;_P|=x&(V_FLAG|N_FLAG) +#define EOR _A^=x;X_ZN(_A) +#define ORA _A|=x;X_ZN(_A) + +#define ADC { \ + uint32 l=_A+x+(_P&1); \ + _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \ + _P|=((((_A^x)&0x80)^0x80) & ((_A^l)&0x80))>>1; \ + _P|=(l>>8)&C_FLAG; \ + _A=l; \ + X_ZNT(_A); \ + } +#define SBC { \ + uint32 l=_A-x-((_P&1)^1); \ + _P&=~(Z_FLAG|C_FLAG|N_FLAG|V_FLAG); \ + _P|=((_A^l)&(_A^x)&0x80)>>1; \ + _P|=((l>>8)&C_FLAG)^C_FLAG; \ + _A=l; \ + X_ZNT(_A); \ + } + +#define CMPL(a1,a2) { \ + uint32 t=a1-a2; \ + X_ZN(t&0xFF); \ + _P&=~C_FLAG; \ + _P|=((t>>8)&C_FLAG)^C_FLAG; \ + } + +/* Special undocumented operation. Very similar to CMP. */ +#define AXS { \ + uint32 t=(_A&_X)-x; \ + X_ZN(t&0xFF); \ + _P&=~C_FLAG; \ + _P|=((t>>8)&C_FLAG)^C_FLAG; \ + _X=t; \ + } + +#define CMP CMPL(_A,x) +#define CPX CMPL(_X,x) +#define CPY CMPL(_Y,x) + +/* The following operations modify the byte being worked on. */ +#define DEC x--;X_ZN(x) +#define INC x++;X_ZN(x) + +#define ASL _P&=~C_FLAG;_P|=x>>7;x<<=1;X_ZN(x) +#define LSR _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=x&1;x>>=1;X_ZNT(x) + +/* For undocumented instructions, maybe for other things later... */ +#define LSRA _P&=~(C_FLAG|N_FLAG|Z_FLAG);_P|=_A&1;_A>>=1;X_ZNT(_A) + +#define ROL { \ + uint8 l=x>>7; \ + x<<=1; \ + x|=_P&C_FLAG; \ + _P&=~(Z_FLAG|N_FLAG|C_FLAG); \ + _P|=l; \ + X_ZNT(x); \ + } +#define ROR { \ + uint8 l=x&1; \ + x>>=1; \ + x|=(_P&C_FLAG)<<7; \ + _P&=~(Z_FLAG|N_FLAG|C_FLAG); \ + _P|=l; \ + X_ZNT(x); \ + } + +/* Icky icky thing for some undocumented instructions. Can easily be + broken if names of local variables are changed. +*/ + +/* Absolute */ +#define GetAB(target) \ +{ \ + target=RdMem(_PC++); \ + target|=RdMem(_PC++)<<8; \ +} + +/* Absolute Indexed(for reads) */ +#define GetABIRD(target, i) \ +{ \ + unsigned int tmp; \ + GetAB(tmp); \ + target=tmp; \ + target+=i; \ + if((target^tmp)&0x100) \ + { \ + target&=0xFFFF; \ + RdMem(target^0x100); \ + ADDCYC(1); \ + } \ +} + +/* Absolute Indexed(for writes and rmws) */ +#define GetABIWR(target, i) \ +{ \ + unsigned int rt; \ + GetAB(rt); \ + target=rt; \ + target+=i; \ + target&=0xFFFF; \ + RdMem((target&0x00FF)|(rt&0xFF00)); \ +} + +/* Zero Page */ +#define GetZP(target) \ +{ \ + target=RdMem(_PC++); \ +} + +/* Zero Page Indexed */ +#define GetZPI(target,i) \ +{ \ + target=i+RdMem(_PC++); \ +} + +/* Indexed Indirect */ +#define GetIX(target) \ +{ \ + uint8 tmp; \ + tmp=RdMem(_PC++); \ + tmp+=_X; \ + target=RdRAM(tmp++); \ + target|=RdRAM(tmp)<<8; \ +} + +/* Indirect Indexed(for reads) */ +#define GetIYRD(target) \ +{ \ + unsigned int rt; \ + uint8 tmp; \ + tmp=RdMem(_PC++); \ + rt=RdRAM(tmp++); \ + rt|=RdRAM(tmp)<<8; \ + target=rt; \ + target+=_Y; \ + if((target^rt)&0x100) \ + { \ + target&=0xFFFF; \ + RdMem(target^0x100); \ + ADDCYC(1); \ + } \ +} + +/* Indirect Indexed(for writes and rmws) */ +#define GetIYWR(target) \ +{ \ + unsigned int rt; \ + uint8 tmp; \ + tmp=RdMem(_PC++); \ + rt=RdRAM(tmp++); \ + rt|=RdRAM(tmp)<<8; \ + target=rt; \ + target+=_Y; \ + RdMem((target&0x00FF)|(rt&0xFF00)); \ +} + +/* Now come the macros to wrap up all of the above stuff addressing mode functions + and operation macros. Note that operation macros will always operate(redundant + redundant) on the variable "x". +*/ + +#define RMW_A(op) {uint8 x=_A; op; _A=x; break; } /* Meh... */ +#define RMW_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_ABI(reg,op) {unsigned int A; uint8 x; GetABIWR(A,reg); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_ABX(op) RMW_ABI(_X,op) +#define RMW_ABY(op) RMW_ABI(_Y,op) +#define RMW_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_IY(op) {unsigned int A; uint8 x; GetIYWR(A); x=RdMem(A); WrMem(A,x); op; WrMem(A,x); break; } +#define RMW_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; WrRAM(A,x); break; } +#define RMW_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; WrRAM(A,x); break;} + +#define LD_IM(op) {uint8 x; x=RdMem(_PC++); op; break;} +#define LD_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; break;} +#define LD_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; break;} +#define LD_ZPY(op) {uint8 A; uint8 x; GetZPI(A,_Y); x=RdRAM(A); op; break;} +#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; } +#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;} +#define LD_ABX(op) LD_ABI(_X,op) +#define LD_ABY(op) LD_ABI(_Y,op) +#define LD_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); op; break;} +#define LD_IY(op) {unsigned int A; uint8 x; GetIYRD(A); x=RdMem(A); op; break;} + +#define ST_ZP(r) {uint8 A; GetZP(A); WrRAM(A,r); break;} +#define ST_ZPX(r) {uint8 A; GetZPI(A,_X); WrRAM(A,r); break;} +#define ST_ZPY(r) {uint8 A; GetZPI(A,_Y); WrRAM(A,r); break;} +#define ST_AB(r) {unsigned int A; GetAB(A); WrMem(A,r); break;} +#define ST_ABI(reg,r) {unsigned int A; GetABIWR(A,reg); WrMem(A,r); break; } +#define ST_ABX(r) ST_ABI(_X,r) +#define ST_ABY(r) ST_ABI(_Y,r) +#define ST_IX(r) {unsigned int A; GetIX(A); WrMem(A,r); break; } +#define ST_IY(r) {unsigned int A; GetIYWR(A); WrMem(A,r); break; } + +static uint8 CycTable[256] = +{ +/*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6, +/*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6, +/*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6, +/*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6, +/*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, +/*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5, +/*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, +/*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4, +/*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6, +/*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +/*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6, +/*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, +}; + +void FASTAPASS(1) X6502_IRQBegin(int w) +{ + _IRQlow|=w; +} + +void FASTAPASS(1) X6502_IRQEnd(int w) +{ + _IRQlow&=~w; +} + +void TriggerIRQ(void) /* This function should probably be phased out. */ +{ + _IRQlow|=FCEU_IQTEMP; +} + +void TriggerNMINSF(void) +{ + ADDCYC(7); + PUSH(_PC>>8); + PUSH(_PC); + PUSH((_P&~B_FLAG)|(U_FLAG)); + _PC=0x3800; +} + +void TriggerNMI(void) +{ + _IRQlow|=FCEU_IQNMI; +} + +static void TriggerNMIReal(void) +{ + if(!_jammed) + { + ADDCYC(7); + PUSH(_PC>>8); + PUSH(_PC); + PUSH((_P&~B_FLAG)|(U_FLAG)); + _PC=RdMem(0xFFFA); + _PC|=RdMem(0xFFFB)<<8; + } +} + +void TriggerIRQReal(void) +{ + if(!(_PI&I_FLAG) && !_jammed) + { + ADDCYC(7); + PUSH(_PC>>8); + PUSH(_PC); + PUSH((_P&~B_FLAG)|(U_FLAG)); + _P|=I_FLAG; + _PC=RdMem(0xFFFE); + _PC|=RdMem(0xFFFF)<<8; + } +} + +void X6502_Reset(void) +{ + _PC=RdMem(0xFFFC); + _PC|=RdMem(0xFFFD)<<8; + if(FCEUGameInfo.type==GIT_NSF) _PC=0x3830; + _jammed=0; + _PI=_P=I_FLAG; +} + +void X6502_Power(void) +{ + memset((void *)&X,0,sizeof(X)); + timestamp=0; + X6502_Reset(); +} + +void X6502_Run(int32 cycles) +{ + if(PAL) + cycles*=15; // 15*4=60 + else + cycles*=16; // 16*4=64 + + _count+=cycles; + + while(_count>0) + { + int32 temp; + uint8 b1; + + if(_IRQlow) + { + if(_IRQlow&FCEU_IQNMI) + TriggerNMIReal(); + else + TriggerIRQReal(); + + _IRQlow&=~(FCEU_IQTEMP|FCEU_IQNMI); + if(_count<=0) {_PI=_P;return;} /* Should increase accuracy without a */ + /* major speed hit. */ + } + _PI=_P; + b1=RdMem(_PC); + ADDCYC(CycTable[b1]); + temp=_tcount; + _tcount=0; + if(MapIRQHook) MapIRQHook(temp); + + temp*=48; + + fhcnt-=temp; + if(fhcnt<=0) + { + FrameSoundUpdate(); + fhcnt+=fhinc; + } + + + if(PCMIRQCount>0) + { + PCMIRQCount-=temp; + if(PCMIRQCount<=0) + { + vdis=1; + if((PSG[0x10]&0x80) && !(PSG[0x10]&0x40)) + { + extern uint8 SIRQStat; + SIRQStat|=0x80; + X6502_IRQBegin(FCEU_IQDPCM); + } + } + } + //printf("$%04x:$%02x\n",_PC,b1); + //_PC++; + //printf("$%02x\n",b1); + _PC++; + switch(b1) + { + #include "ops.h" + } + } +} diff --git a/x6502.h b/x6502.h new file mode 100644 index 0000000..414533b --- /dev/null +++ b/x6502.h @@ -0,0 +1,66 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef struct { + int32 count; /* Cycle counter */ + int32 tcount; /* Temporary cycle counter */ + uint16 PC; /* I'll change this to uint32 later... */ + /* I'll need to AND PC after increments to 0xFFFF */ + /* when I do, though. Perhaps an IPC() macro? */ + uint8 A,X,Y,S,P,mooPI,PZ; + uint8 DB; /* Data bus "cache" for reads from certain areas */ + uint8 IRQlow; /* Simulated IRQ pin held low(or is it high?). */ + uint8 jammed; +} X6502; + +extern X6502 X; + +#define N_FLAG 0x80 +#define V_FLAG 0x40 +#define U_FLAG 0x20 +#define B_FLAG 0x10 +#define D_FLAG 0x08 +#define I_FLAG 0x04 +#define Z_FLAG 0x02 +#define C_FLAG 0x01 + +extern uint32 timestamp; +extern void FP_FASTAPASS(1) (*MapIRQHook)(int a); + +#define NTSC_CPU 1789772.7272727272727272 +#define PAL_CPU 1662607.125 + +#define FCEU_IQEXT 0x01 +#define FCEU_IQNMI 0x08 +#define FCEU_IQDPCM 0x10 +#define FCEU_IQFCOUNT 0x20 +#define FCEU_IQTEMP 0x80 + +void X6502_Reset(void); +void X6502_Power(void); +void X6502_Run(int32 cycles); + +void TriggerIRQ(void); +void TriggerNMI(void); +void TriggerNMINSF(void); + +void FASTAPASS(1) X6502_AddCycles(int x); +void FASTAPASS(1) X6502_IRQBegin(int w); +void FASTAPASS(1) X6502_IRQEnd(int w); diff --git a/zlib/ChangeLog b/zlib/ChangeLog new file mode 100644 index 0000000..58d58af --- /dev/null +++ b/zlib/ChangeLog @@ -0,0 +1,481 @@ + + ChangeLog file for zlib + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occuring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id: ChangeLog,v 1.2 2002/03/13 17:45:55 xodnizel Exp $. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generated bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/zlib/Makefile b/zlib/Makefile new file mode 100644 index 0000000..ac87b2f --- /dev/null +++ b/zlib/Makefile @@ -0,0 +1,25 @@ +UNZIPOBJS = zlib/unzip.o +ZLIBOBJS = zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/gzio.o zlib/uncompr.o zlib/deflate.o zlib/trees.o \ + zlib/zutil.o zlib/inflate.o zlib/infblock.o zlib/inftrees.o zlib/infcodes.o zlib/infutil.o zlib/inffast.o + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +zlib/adler32.o: zlib/zlib.h zlib/zconf.h +zlib/compress.o: zlib/zlib.h zlib/zconf.h +zlib/crc32.o: zlib/zlib.h zlib/zconf.h +zlib/deflate.o: zlib/deflate.h zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/example.o: zlib/zlib.h zlib/zconf.h +zlib/gzio.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/infblock.o: zlib/infblock.h zlib/inftrees.h zlib/infcodes.h zlib/infutil.h zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/infcodes.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h +zlib/infcodes.o: zlib/inftrees.h zlib/infblock.h zlib/infcodes.h zlib/infutil.h zlib/inffast.h +zlib/inffast.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/inftrees.h +zlib/inffast.o: zlib/infblock.h zlib/infcodes.h zlib/infutil.h zlib/inffast.h +zlib/inflate.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/infblock.h +zlib/inftrees.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/inftrees.h +zlib/infutil.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/infblock.h zlib/inftrees.h zlib/infcodes.h zlib/infutil.h +zlib/trees.o: zlib/deflate.h zlib/zutil.h zlib/zlib.h zlib/zconf.h zlib/trees.h +zlib/uncompr.o: zlib/zlib.h zlib/zconf.h +zlib/zutil.o: zlib/zutil.h zlib/zlib.h zlib/zconf.h + +zlib/unzip.o: zlib/unzip.h diff --git a/zlib/adler32.c b/zlib/adler32.c new file mode 100644 index 0000000..a250f3f --- /dev/null +++ b/zlib/adler32.c @@ -0,0 +1,48 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff --git a/zlib/algorithm.txt b/zlib/algorithm.txt new file mode 100644 index 0000000..cdc830b --- /dev/null +++ b/zlib/algorithm.txt @@ -0,0 +1,213 @@ +1. Compression algorithm (deflate) + +The deflation algorithm used by gzip (also zip and zlib) is a variation of +LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in +the input data. The second occurrence of a string is replaced by a +pointer to the previous string, in the form of a pair (distance, +length). Distances are limited to 32K bytes, and lengths are limited +to 258 bytes. When a string does not occur anywhere in the previous +32K bytes, it is emitted as a sequence of literal bytes. (In this +description, `string' must be taken as an arbitrary sequence of bytes, +and is not restricted to printable characters.) + +Literals or match lengths are compressed with one Huffman tree, and +match distances are compressed with another tree. The trees are stored +in a compact form at the start of each block. The blocks can have any +size (except that the compressed data for one block must fit in +available memory). A block is terminated when deflate() determines that +it would be useful to start another block with fresh trees. (This is +somewhat similar to the behavior of LZW-based _compress_.) + +Duplicated strings are found using a hash table. All input strings of +length 3 are inserted in the hash table. A hash index is computed for +the next 3 bytes. If the hash chain for this index is not empty, all +strings in the chain are compared with the current input string, and +the longest match is selected. + +The hash chains are searched starting with the most recent strings, to +favor small distances and thus take advantage of the Huffman encoding. +The hash chains are singly linked. There are no deletions from the +hash chains, the algorithm simply discards matches that are too old. + +To avoid a worst-case situation, very long hash chains are arbitrarily +truncated at a certain length, determined by a runtime option (level +parameter of deflateInit). So deflate() does not always find the longest +possible match but generally finds a match which is long enough. + +deflate() also defers the selection of matches with a lazy evaluation +mechanism. After a match of length N has been found, deflate() searches for +a longer match at the next input byte. If a longer match is found, the +previous match is truncated to a length of one (thus producing a single +literal byte) and the process of lazy evaluation begins again. Otherwise, +the original match is kept, and the next match search is attempted only N +steps later. + +The lazy match evaluation is also subject to a runtime parameter. If +the current match is long enough, deflate() reduces the search for a longer +match, thus speeding up the whole process. If compression ratio is more +important than speed, deflate() attempts a complete second search even if +the first match is already long enough. + +The lazy match evaluation is not performed for the fastest compression +modes (level parameter 1 to 3). For these fast modes, new strings +are inserted in the hash table only when no match was found, or +when the match is not too long. This degrades the compression ratio +but saves time since there are both fewer insertions and fewer searches. + + +2. Decompression algorithm (inflate) + +2.1 Introduction + +The real question is, given a Huffman tree, how to decode fast. The most +important realization is that shorter codes are much more common than +longer codes, so pay attention to decoding the short codes fast, and let +the long codes take longer to decode. + +inflate() sets up a first level table that covers some number of bits of +input less than the length of longest code. It gets that many bits from the +stream, and looks it up in the table. The table will tell if the next +code is that many bits or less and how many, and if it is, it will tell +the value, else it will point to the next level table for which inflate() +grabs more bits and tries to decode a longer code. + +How many bits to make the first lookup is a tradeoff between the time it +takes to decode and the time it takes to build the table. If building the +table took no time (and if you had infinite memory), then there would only +be a first level table to cover all the way to the longest code. However, +building the table ends up taking a lot longer for more bits since short +codes are replicated many times in such a table. What inflate() does is +simply to make the number of bits in the first table a variable, and set it +for the maximum speed. + +inflate() sends new trees relatively often, so it is possibly set for a +smaller first level table than an application that has only one tree for +all the data. For inflate, which has 286 possible codes for the +literal/length tree, the size of the first table is nine bits. Also the +distance trees have 30 possible values, and the size of the first table is +six bits. Note that for each of those cases, the table ended up one bit +longer than the ``average'' code length, i.e. the code length of an +approximately flat code which would be a little more than eight bits for +286 symbols and a little less than five bits for 30 symbols. It would be +interesting to see if optimizing the first level table for other +applications gave values within a bit or two of the flat code size. + + +2.2 More details on the inflate table lookup + +Ok, you want to know what this cleverly obfuscated inflate tree actually +looks like. You are correct that it's not a Huffman tree. It is simply a +lookup table for the first, let's say, nine bits of a Huffman symbol. The +symbol could be as short as one bit or as long as 15 bits. If a particular +symbol is shorter than nine bits, then that symbol's translation is duplicated +in all those entries that start with that symbol's bits. For example, if the +symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a +symbol is nine bits long, it appears in the table once. + +If the symbol is longer than nine bits, then that entry in the table points +to another similar table for the remaining bits. Again, there are duplicated +entries as needed. The idea is that most of the time the symbol will be short +and there will only be one table look up. (That's whole idea behind data +compression in the first place.) For the less frequent long symbols, there +will be two lookups. If you had a compression method with really long +symbols, you could have as many levels of lookups as is efficient. For +inflate, two is enough. + +So a table entry either points to another table (in which case nine bits in +the above example are gobbled), or it contains the translation for the symbol +and the number of bits to gobble. Then you start again with the next +ungobbled bit. + +You may wonder: why not just have one lookup table for how ever many bits the +longest symbol is? The reason is that if you do that, you end up spending +more time filling in duplicate symbol entries than you do actually decoding. +At least for deflate's output that generates new trees every several 10's of +kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code +would take too long if you're only decoding several thousand symbols. At the +other extreme, you could make a new table for every bit in the code. In fact, +that's essentially a Huffman tree. But then you spend two much time +traversing the tree while decoding, even for short symbols. + +So the number of bits for the first lookup table is a trade of the time to +fill out the table vs. the time spent looking at the second level and above of +the table. + +Here is an example, scaled down: + +The code being decoded, with 10 symbols, from 1 to 6 bits long: + +A: 0 +B: 10 +C: 1100 +D: 11010 +E: 11011 +F: 11100 +G: 11101 +H: 11110 +I: 111110 +J: 111111 + +Let's make the first table three bits long (eight entries): + +000: A,1 +001: A,1 +010: A,1 +011: A,1 +100: B,2 +101: B,2 +110: -> table X (gobble 3 bits) +111: -> table Y (gobble 3 bits) + +Each entry is what the bits decode to and how many bits that is, i.e. how +many bits to gobble. Or the entry points to another table, with the number of +bits to gobble implicit in the size of the table. + +Table X is two bits long since the longest code starting with 110 is five bits +long: + +00: C,1 +01: C,1 +10: D,2 +11: E,2 + +Table Y is three bits long since the longest code starting with 111 is six +bits long: + +000: F,2 +001: F,2 +010: G,2 +011: G,2 +100: H,2 +101: H,2 +110: I,3 +111: J,3 + +So what we have here are three tables with a total of 20 entries that had to +be constructed. That's compared to 64 entries for a single table. Or +compared to 16 entries for a Huffman tree (six two entry tables and one four +entry table). Assuming that the code ideally represents the probability of +the symbols, it takes on the average 1.25 lookups per symbol. That's compared +to one lookup for the single table, or 1.66 lookups per symbol for the +Huffman tree. + +There, I think that gives you a picture of what's going on. For inflate, the +meaning of a particular symbol is often more than just a letter. It can be a +byte (a "literal"), or it can be either a length or a distance which +indicates a base value and a number of bits to fetch after the code that is +added to the base value. Or it might be the special end-of-block code. The +data structures created in inftrees.c try to encode all that information +compactly in the tables. + + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + + +References: + +[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data +Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3, +pp. 337-343. + +``DEFLATE Compressed Data Format Specification'' available in +ftp://ds.internic.net/rfc/rfc1951.txt diff --git a/zlib/compress.c b/zlib/compress.c new file mode 100644 index 0000000..c0eecda --- /dev/null +++ b/zlib/compress.c @@ -0,0 +1,68 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: compress.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} diff --git a/zlib/crc32.c b/zlib/crc32.c new file mode 100644 index 0000000..558f6f6 --- /dev/null +++ b/zlib/crc32.c @@ -0,0 +1,162 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: crc32.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +#define local static + +#ifdef DYNAMIC_CRC_TABLE + +local int crc_table_empty = 1; +local uLongf crc_table[256]; +local void make_crc_table OF((void)); + +/* + Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The table is simply the CRC of all possible eight bit values. This is all + the information needed to generate CRC's on data a byte at a time for all + combinations of CRC register values and incoming bytes. +*/ +local void make_crc_table() +{ + uLong c; + int n, k; + uLong poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* make exclusive-or pattern from polynomial (0xedb88320L) */ + poly = 0L; + for (n = 0; n < sizeof(p)/sizeof(Byte); n++) + poly |= 1L << (31 - p[n]); + + for (n = 0; n < 256; n++) + { + c = (uLong)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[n] = c; + } + crc_table_empty = 0; +} +#else +/* ======================================================================== + * Table of CRC-32's of all single-byte values (made by make_crc_table) + */ +local const uLongf crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; +#endif + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const uLongf * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) make_crc_table(); +#endif + return (const uLongf *)crc_table; +} + +/* ========================================================================= */ +#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); + +/* ========================================================================= */ +uLong ZEXPORT crc32(crc, buf, len) + uLong crc; + const Bytef *buf; + uInt len; +{ + if (buf == Z_NULL) return 0L; +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif + crc = crc ^ 0xffffffffL; + while (len >= 8) + { + DO8(buf); + len -= 8; + } + if (len) do { + DO1(buf); + } while (--len); + return crc ^ 0xffffffffL; +} diff --git a/zlib/deflate.c b/zlib/deflate.c new file mode 100644 index 0000000..0b79a14 --- /dev/null +++ b/zlib/deflate.c @@ -0,0 +1,1350 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in ftp://ds.internic.net/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_slow OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int noheader = 0; + static const char* my_version = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == Z_NULL) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == Z_NULL) strm->zfree = zcfree; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#ifdef FASTEST + level = 1; +#endif + + if (windowBits < 0) { /* undocumented feature: suppress zlib header */ + noheader = 1; + windowBits = -windowBits; + } + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->noheader = noheader; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->status != INIT_STATE) return Z_STREAM_ERROR; + + s = strm->state; + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); +#ifndef USE_DICT_HEAD + dictionary += dictLength - length; /* use the tail of the dictionary */ +#endif + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->noheader < 0) { + s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ + } + s->status = s->noheader ? BUSY_STATE : INIT_STATE; + strm->adler = 1; + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + + if (level == Z_DEFAULT_COMPRESSION) { + level = 6; + } + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the zlib header */ + if (s->status == INIT_STATE) { + + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags = (s->level-1) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = 1L; + } + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUFF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->noheader) return Z_STREAM_END; + + /* Write the zlib trailer (adler32) */ + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + s->noheader = -1; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + *dest = *source; + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + *ds = *ss; + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (!strm->state->noheader) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +} + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +#ifndef FASTEST +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} + +#else /* FASTEST */ +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return len <= s->lookahead ? len : s->lookahead; +} +#endif /* FASTEST */ +#endif /* ASMV */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + } else if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in hash table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED || + (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} diff --git a/zlib/deflate.h b/zlib/deflate.h new file mode 100644 index 0000000..9bd8cb0 --- /dev/null +++ b/zlib/deflate.h @@ -0,0 +1,318 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2002 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#ifndef _DEFLATE_H +#define _DEFLATE_H + +#include "zutil.h" + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + int noheader; /* suppress zlib header and adler32 */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif diff --git a/zlib/descrip.mms b/zlib/descrip.mms new file mode 100644 index 0000000..9d36459 --- /dev/null +++ b/zlib/descrip.mms @@ -0,0 +1,48 @@ +# descrip.mms: MMS description file for building zlib on VMS +# written by Martin P.J. Zinser + +cc_defs = +c_deb = + +.ifdef __DECC__ +pref = /prefix=all +.endif + +OBJS = adler32.obj, compress.obj, crc32.obj, gzio.obj, uncompr.obj,\ + deflate.obj, trees.obj, zutil.obj, inflate.obj, infblock.obj,\ + inftrees.obj, infcodes.obj, infutil.obj, inffast.obj + +CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF) + +all : example.exe minigzip.exe + @ write sys$output " Example applications available" +libz.olb : libz.olb($(OBJS)) + @ write sys$output " libz available" + +example.exe : example.obj libz.olb + link example,libz.olb/lib + +minigzip.exe : minigzip.obj libz.olb + link minigzip,libz.olb/lib,x11vms:xvmsutils.olb/lib + +clean : + delete *.obj;*,libz.olb;* + + +# Other dependencies. +adler32.obj : zutil.h zlib.h zconf.h +compress.obj : zlib.h zconf.h +crc32.obj : zutil.h zlib.h zconf.h +deflate.obj : deflate.h zutil.h zlib.h zconf.h +example.obj : zlib.h zconf.h +gzio.obj : zutil.h zlib.h zconf.h +infblock.obj : zutil.h zlib.h zconf.h infblock.h inftrees.h infcodes.h infutil.h +infcodes.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h infcodes.h inffast.h +inffast.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h inffast.h +inflate.obj : zutil.h zlib.h zconf.h infblock.h +inftrees.obj : zutil.h zlib.h zconf.h inftrees.h +infutil.obj : zutil.h zlib.h zconf.h inftrees.h infutil.h +minigzip.obj : zlib.h zconf.h +trees.obj : deflate.h zutil.h zlib.h zconf.h +uncompr.obj : zlib.h zconf.h +zutil.obj : zutil.h zlib.h zconf.h diff --git a/zlib/example.c b/zlib/example.c new file mode 100644 index 0000000..af7f43e --- /dev/null +++ b/zlib/example.c @@ -0,0 +1,556 @@ +/* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: example.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include +#include "zlib.h" + +#ifdef STDC +# include +# include +#else + extern void exit OF((int)); +#endif + +#if defined(VMS) || defined(RISCOS) +# define TESTFILE "foo-gz" +#else +# define TESTFILE "foo.gz" +#endif + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char hello[] = "hello, hello!"; +/* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + */ + +const char dictionary[] = "hello"; +uLong dictId; /* Adler32 value of the dictionary */ + +void test_compress OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_gzio OF((const char *out, const char *in, + Byte *uncompr, int uncomprLen)); +void test_deflate OF((Byte *compr, uLong comprLen)); +void test_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_deflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_flush OF((Byte *compr, uLong *comprLen)); +void test_sync OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_dict_deflate OF((Byte *compr, uLong comprLen)); +void test_dict_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Test compress() and uncompress() + */ +void test_compress(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + uLong len = strlen(hello)+1; + + err = compress(compr, &comprLen, (const Bytef*)hello, len); + CHECK_ERR(err, "compress"); + + strcpy((char*)uncompr, "garbage"); + + err = uncompress(uncompr, &uncomprLen, compr, comprLen); + CHECK_ERR(err, "uncompress"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad uncompress\n"); + exit(1); + } else { + printf("uncompress(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test read/write of .gz files + */ +void test_gzio(out, in, uncompr, uncomprLen) + const char *out; /* compressed output file */ + const char *in; /* compressed input file */ + Byte *uncompr; + int uncomprLen; +{ + int err; + int len = strlen(hello)+1; + gzFile file; + z_off_t pos; + + file = gzopen(out, "wb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + gzputc(file, 'h'); + if (gzputs(file, "ello") != 4) { + fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); + exit(1); + } + if (gzprintf(file, ", %s!", "hello") != 8) { + fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); + exit(1); + } + gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ + gzclose(file); + + file = gzopen(in, "rb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + } + strcpy((char*)uncompr, "garbage"); + + uncomprLen = gzread(file, uncompr, (unsigned)uncomprLen); + if (uncomprLen != len) { + fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); + exit(1); + } else { + printf("gzread(): %s\n", (char *)uncompr); + } + + pos = gzseek(file, -8L, SEEK_CUR); + if (pos != 6 || gztell(file) != pos) { + fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", + (long)pos, (long)gztell(file)); + exit(1); + } + + if (gzgetc(file) != ' ') { + fprintf(stderr, "gzgetc error\n"); + exit(1); + } + + gzgets(file, (char*)uncompr, uncomprLen); + uncomprLen = strlen((char*)uncompr); + if (uncomprLen != 6) { /* "hello!" */ + fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello+7)) { + fprintf(stderr, "bad gzgets after gzseek\n"); + exit(1); + } else { + printf("gzgets() after gzseek: %s\n", (char *)uncompr); + } + + gzclose(file); +} + +/* =========================================================================== + * Test deflate() with small buffers + */ +void test_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + int len = strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + + while (c_stream.total_in != (uLong)len && c_stream.total_out < comprLen) { + c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + } + /* Finish the stream, still forcing small buffers: */ + for (;;) { + c_stream.avail_out = 1; + err = deflate(&c_stream, Z_FINISH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "deflate"); + } + + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with small buffers + */ +void test_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 0; + d_stream.next_out = uncompr; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate\n"); + exit(1); + } else { + printf("inflate(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test deflate() with large buffers and dynamic change of compression level + */ +void test_large_deflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_SPEED); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + /* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + */ + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + if (c_stream.avail_in != 0) { + fprintf(stderr, "deflate not greedy\n"); + exit(1); + } + + /* Feed in already compressed data and switch to no compression: */ + deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in = compr; + c_stream.avail_in = (uInt)comprLen/2; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + /* Switch back to compressing mode: */ + deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with large buffers + */ +void test_large_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + for (;;) { + d_stream.next_out = uncompr; /* discard the output */ + d_stream.avail_out = (uInt)uncomprLen; + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "large inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (d_stream.total_out != 2*uncomprLen + comprLen/2) { + fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); + exit(1); + } else { + printf("large_inflate(): OK\n"); + } +} + +/* =========================================================================== + * Test deflate() with full flush + */ +void test_flush(compr, comprLen) + Byte *compr; + uLong *comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + int len = strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + c_stream.avail_in = 3; + c_stream.avail_out = (uInt)*comprLen; + err = deflate(&c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, "deflate"); + + compr[3]++; /* force an error in first compressed block */ + c_stream.avail_in = len - 3; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + CHECK_ERR(err, "deflate"); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); + + *comprLen = c_stream.total_out; +} + +/* =========================================================================== + * Test inflateSync() + */ +void test_sync(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 2; /* just read the zlib header */ + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + inflate(&d_stream, Z_NO_FLUSH); + CHECK_ERR(err, "inflate"); + + d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ + err = inflateSync(&d_stream); /* but skip the damaged part */ + CHECK_ERR(err, "inflateSync"); + + err = inflate(&d_stream, Z_FINISH); + if (err != Z_DATA_ERROR) { + fprintf(stderr, "inflate should report DATA_ERROR\n"); + /* Because of incorrect adler32 */ + exit(1); + } + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + printf("after inflateSync(): hel%s\n", (char *)uncompr); +} + +/* =========================================================================== + * Test deflate() with preset dictionary + */ +void test_dict_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + err = deflateSetDictionary(&c_stream, + (const Bytef*)dictionary, sizeof(dictionary)); + CHECK_ERR(err, "deflateSetDictionary"); + + dictId = c_stream.adler; + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + c_stream.next_in = (Bytef*)hello; + c_stream.avail_in = (uInt)strlen(hello)+1; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with a preset dictionary + */ +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + if (err == Z_NEED_DICT) { + if (d_stream.adler != dictId) { + fprintf(stderr, "unexpected dictionary"); + exit(1); + } + err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, + sizeof(dictionary)); + } + CHECK_ERR(err, "inflate with dict"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate with dict\n"); + exit(1); + } else { + printf("inflate with dictionary: %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Usage: example [output.gz [input.gz]] + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + Byte *compr, *uncompr; + uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ + uLong uncomprLen = comprLen; + static const char* myVersion = ZLIB_VERSION; + + if (zlibVersion()[0] != myVersion[0]) { + fprintf(stderr, "incompatible zlib version\n"); + exit(1); + + } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { + fprintf(stderr, "warning: different zlib version\n"); + } + + compr = (Byte*)calloc((uInt)comprLen, 1); + uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + /* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + */ + if (compr == Z_NULL || uncompr == Z_NULL) { + printf("out of memory\n"); + exit(1); + } + test_compress(compr, comprLen, uncompr, uncomprLen); + + test_gzio((argc > 1 ? argv[1] : TESTFILE), + (argc > 2 ? argv[2] : TESTFILE), + uncompr, (int)uncomprLen); + + test_deflate(compr, comprLen); + test_inflate(compr, comprLen, uncompr, uncomprLen); + + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + + test_flush(compr, &comprLen); + test_sync(compr, comprLen, uncompr, uncomprLen); + comprLen = uncomprLen; + + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + + exit(0); + return 0; /* to avoid warning */ +} diff --git a/zlib/faq b/zlib/faq new file mode 100644 index 0000000..0feb6d3 --- /dev/null +++ b/zlib/faq @@ -0,0 +1,72 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://www.cdrom.com/pub/infozip/zlib/ which may have more recent information. + + +1) I need a Windows DLL +2) I need a Visual Basic interface to zlib +3) compress() returns Z_BUF_ERROR +4) deflate or inflate returns Z_BUF_ERROR +5) Where is the zlib documentation (man pages, etc...)? +6) Why don't you use GNU autoconf, libtool, etc...? +7) There is a bug in zlib. +8) I get "undefined reference to gzputc" + + + +1) I need a Windows DLL + + The zlib sources can be compiled without change to produce a DLL. + If you want a precompiled DLL, see http://www.winimage.com/zLibDll + + +2) I need a Visual Basic interface to zlib + + See http://www.tcfb.com/dowseware/cmp-z-it.zip + http://web2.airmail.net/markn/articles/zlibtool/zlibtool.htm + and contrib/visual-basic.txt + +3) compress() returns Z_BUF_ERROR + + Make sure that before the call of compress, the length of the + compressed buffer is equal to the total size of the compressed buffer + and not zero. For Visual Basic, check that this parameter is passed + by reference ("as any"), not by value ("as long"). + + +4) deflate or inflate returns Z_BUF_ERROR + + Make sure that before the call avail_in and avail_out are not zero. + + +5) Where is the zlib documentation (man pages, etc...)? + + It's in zlib.h for the moment. Volunteers to transform this + to man pages, please contact jloup@gzip.org. Examples of zlib usage + are in the files example.c and minigzip.c. + + +6) Why don't you use GNU autoconf, libtool, etc...? + + Because we would like to keep zlib as a very small and simple package. + zlib is rather portable and doesn't need much configuration. + + +7) There is a bug in zlib. + + Most of the time, such problems are due to an incorrect usage + of zlib. Please try to reproduce the problem with a small + program and send us the corresponding source at zlib@quest.jpl.nasa.gov + Do not send multi-megabyte data files without prior agreement. + + +8) I get "undefined reference to gzputc" + + If "make test" produces something like + example.o(.text+0x174): + check that you don't have old files libz.* in /usr/lib, /usr/local/lib + or /usr/X11R6/lib. Remove old versions then do "make install". + diff --git a/zlib/gzio.c b/zlib/gzio.c new file mode 100644 index 0000000..6142b0a --- /dev/null +++ b/zlib/gzio.c @@ -0,0 +1,875 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* @(#) $Id: gzio.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include + +#include "zutil.h" + +struct internal_state {int dummy;}; /* for buggy compilers */ + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open return NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->startpos = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * startpos anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->startpos = (ftell(s->file) - s->stream.avail_in); + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[20]; + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Check the gzip magic header */ + for (len = 0; len < 2; len++) { + c = get_byte(s); + if (c != gz_magic[len]) { + if (len != 0) s->stream.avail_in++, s->stream.next_in--; + if (c != EOF) { + s->stream.avail_in++, s->stream.next_in--; + s->transparent = 1; + } + s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; + return; + } + } + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, + s->file); + } + len -= s->stream.avail_out; + s->stream.total_in += (uLong)len; + s->stream.total_out += (uLong)len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may + * be different from s->stream.total_out) in case of + * concatenated .gz files. Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + uLong total_in = s->stream.total_in; + uLong total_out = s->stream.total_out; + + inflateReset(&(s->stream)); + s->stream.total_in = total_in; + s->stream.total_out = total_out; + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_DEFLATE +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + const voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + va_start(va, format); +#ifdef HAS_vsnprintf + (void)vsnprintf(buf, sizeof(buf), format, va); +#else + (void)vsprintf(buf, format, va); +#endif + va_end(va); + len = strlen(buf); /* some *sprintf don't return the nb of bytes written */ + if (len <= 0) return 0; + + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + +#ifdef HAS_snprintf + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +#else + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +#endif + len = strlen(buf); /* old sprintf doesn't return the nb of bytes written */ + if (len <= 0) return 0; + + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->z_err = deflate(&(s->stream), flush); + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_DEFLATE */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->stream.total_in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return (z_off_t)s->stream.total_in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->stream.total_out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->stream.total_in = s->stream.total_out = (uLong)offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if ((uLong)offset >= s->stream.total_out) { + offset -= s->stream.total_out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return (z_off_t)s->stream.total_out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + + if (s->startpos == 0) { /* not a compressed file */ + rewind(s->file); + return 0; + } + + (void) inflateReset(&s->stream); + return fseek(s->file, s->startpos, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + return (s == NULL || s->mode != 'r') ? 0 : s->z_eof; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + int err; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_DEFLATE + return Z_STREAM_ERROR; +#else + err = do_flush (file, Z_FINISH); + if (err != Z_OK) return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, s->stream.total_in); +#endif + } + return destroy((gz_stream*)file); +} + +/* =========================================================================== + Returns the error message for the last error which occured on the + given compressed file. errnum is set to zlib error number. If an + error occured in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char* ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} diff --git a/zlib/infblock.c b/zlib/infblock.c new file mode 100644 index 0000000..dd7a6d4 --- /dev/null +++ b/zlib/infblock.c @@ -0,0 +1,403 @@ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" +#include "inftrees.h" +#include "infcodes.h" +#include "infutil.h" + +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* Table for deflate from PKZIP's appnote.txt. */ +local const uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_streamp z; +uLongf *c; +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_streamp z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + } + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + { + ZFREE(z, s->sub.trees.blens); + s->mode = BAD; + } + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + ZFREE(z, s->sub.trees.blens); + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONE; + case DONE: + r = Z_STREAM_END; + LEAVE + case BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(s, z) +inflate_blocks_statef *s; +z_streamp z; +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + +void inflate_set_dictionary(s, d, n) +inflate_blocks_statef *s; +const Bytef *d; +uInt n; +{ + zmemcpy(s->window, d, n); + s->read = s->write = s->window + n; +} + + +/* Returns true if inflate is currently at the end of a block generated + * by Z_SYNC_FLUSH or Z_FULL_FLUSH. + * IN assertion: s != Z_NULL + */ +int inflate_blocks_sync_point(s) +inflate_blocks_statef *s; +{ + return s->mode == LENS; +} diff --git a/zlib/infblock.h b/zlib/infblock.h new file mode 100644 index 0000000..173b226 --- /dev/null +++ b/zlib/infblock.h @@ -0,0 +1,39 @@ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +extern inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +extern int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +extern void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +extern int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp)); + +extern void inflate_set_dictionary OF(( + inflate_blocks_statef *s, + const Bytef *d, /* dictionary */ + uInt n)); /* dictionary length */ + +extern int inflate_blocks_sync_point OF(( + inflate_blocks_statef *s)); diff --git a/zlib/infcodes.c b/zlib/infcodes.c new file mode 100644 index 0000000..9abe541 --- /dev/null +++ b/zlib/infcodes.c @@ -0,0 +1,251 @@ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "infblock.h" +#include "infcodes.h" +#include "infutil.h" +#include "inffast.h" + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ +inflate_codes_mode; + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + inflate_codes_mode mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +z_streamp z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ + f = q - c->sub.copy.dist; + while (f < s->window) /* modulo window size-"while" instead */ + f += s->end - s->window; /* of "if" handles invalid distances */ + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_streamp z; +{ + ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} diff --git a/zlib/infcodes.h b/zlib/infcodes.h new file mode 100644 index 0000000..46821a0 --- /dev/null +++ b/zlib/infcodes.h @@ -0,0 +1,27 @@ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +extern inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_streamp )); + +extern int inflate_codes OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +extern void inflate_codes_free OF(( + inflate_codes_statef *, + z_streamp )); + diff --git a/zlib/inffast.c b/zlib/inffast.c new file mode 100644 index 0000000..aa7f1d4 --- /dev/null +++ b/zlib/inffast.c @@ -0,0 +1,183 @@ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "infblock.h" +#include "infcodes.h" +#include "infutil.h" +#include "inffast.h" + +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +inflate_blocks_statef *s; +z_streamp z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + r = q - d; + if (r < s->window) /* wrap if needed */ + { + do { + r += s->end - s->window; /* force pointer in window */ + } while (r < s->window); /* covers invalid distances */ + e = s->end - r; + if (c > e) + { + c -= e; /* wrapped copy */ + do { + *q++ = *r++; + } while (--e); + r = s->window; + do { + *q++ = *r++; + } while (--c); + } + else /* normal copy */ + { + *q++ = *r++; c--; + *q++ = *r++; c--; + do { + *q++ = *r++; + } while (--c); + } + } + else /* normal copy */ + { + *q++ = *r++; c--; + *q++ = *r++; c--; + do { + *q++ = *r++; + } while (--c); + } + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} diff --git a/zlib/inffast.h b/zlib/inffast.h new file mode 100644 index 0000000..a31a4bb --- /dev/null +++ b/zlib/inffast.h @@ -0,0 +1,17 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +extern int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_streamp )); diff --git a/zlib/inffixed.h b/zlib/inffixed.h new file mode 100644 index 0000000..77f7e76 --- /dev/null +++ b/zlib/inffixed.h @@ -0,0 +1,151 @@ +/* inffixed.h -- table for decoding fixed codes + * Generated automatically by the maketree.c program + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local uInt fixed_bl = 9; +local uInt fixed_bd = 5; +local inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +local inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; diff --git a/zlib/inflate.c b/zlib/inflate.c new file mode 100644 index 0000000..dfb2e86 --- /dev/null +++ b/zlib/inflate.c @@ -0,0 +1,366 @@ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" + +struct inflate_blocks_state {int dummy;}; /* for buggy compilers */ + +typedef enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + DICT4, /* four dictionary check bytes to go */ + DICT3, /* three dictionary check bytes to go */ + DICT2, /* two dictionary check bytes to go */ + DICT1, /* one dictionary check byte to go */ + DICT0, /* waiting for inflateSetDictionary */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ +inflate_mode; + +/* inflate private state */ +struct internal_state { + + /* mode */ + inflate_mode mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int ZEXPORT inflateReset(z) +z_streamp z; +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int ZEXPORT inflateEnd(z) +z_streamp z; +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int ZEXPORT inflateInit2_(z, w, version, stream_size) +z_streamp z; +int w; +const char *version; +int stream_size; +{ + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != sizeof(z_stream)) + return Z_VERSION_ERROR; + + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int ZEXPORT inflateInit_(z, version, stream_size) +z_streamp z; +const char *version; +int stream_size; +{ + return inflateInit2_(z, DEF_WBITS, version, stream_size); +} + + +#define NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int ZEXPORT inflate(z, f) +z_streamp z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + b = NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = BLOCKS; + break; + } + z->state->mode = DICT4; + case DICT4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = DICT3; + case DICT3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = DICT2; + case DICT2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = DICT1; + case DICT1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z->state->mode = BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_STREAM_ERROR; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +#ifdef NEED_DUMMY_RETURN + return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ +#endif +} + + +int ZEXPORT inflateSetDictionary(z, dictionary, dictLength) +z_streamp z; +const Bytef *dictionary; +uInt dictLength; +{ + uInt length = dictLength; + + if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0) + return Z_STREAM_ERROR; + + if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; + z->adler = 1L; + + if (length >= ((uInt)1<state->wbits)) + { + length = (1<state->wbits)-1; + dictionary += dictLength - length; + } + inflate_set_dictionary(z->state->blocks, dictionary, length); + z->state->mode = BLOCKS; + return Z_OK; +} + + +int ZEXPORT inflateSync(z) +z_streamp z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + static const Byte mark[4] = {0, 0, 0xff, 0xff}; + if (*p == mark[m]) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + + +/* Returns true if inflate is currently at the end of a block generated + * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + * but removes the length bytes of the resulting empty stored block. When + * decompressing, PPP checks that at the end of input packet, inflate is + * waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(z) +z_streamp z; +{ + if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL) + return Z_STREAM_ERROR; + return inflate_blocks_sync_point(z->state->blocks); +} diff --git a/zlib/inftrees.c b/zlib/inftrees.c new file mode 100644 index 0000000..4c32ca3 --- /dev/null +++ b/zlib/inftrees.c @@ -0,0 +1,454 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#if !defined(BUILDFIXED) && !defined(STDC) +# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */ +#endif + +const char inflate_copyright[] = + " inflate 1.1.4 Copyright 1995-2002 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ +struct internal_state {int dummy;}; /* for buggy compilers */ + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + const uIntf *, /* list of base values for non-simple codes */ + const uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + inflate_huft *, /* space for trees */ + uInt *, /* hufts used in space */ + uIntf * )); /* space for values */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* see note #13 above about 258 */ +local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ +local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local const uInt cpdext[30] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ + +local int huft_build(b, n, s, d, e, t, m, hp, hn, v) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= 288) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +const uIntf *d; /* list of base values for non-simple codes */ +const uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +inflate_huft *hp; /* space for trees */ +uInt *hn; /* hufts used in space */ +uIntf *v; /* working area: values in order of bit length */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), or Z_DATA_ERROR if the input is invalid. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; /* set n to length of v */ + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = g - w; + z = z > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate new table */ + if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ + return Z_DATA_ERROR; /* overflow of MANY */ + u[h] = q = hp + *hn; + *hn += z; + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ + u[h-1][j] = r; /* connect to last table */ + } + else + *t = q; /* first table is returned result */ + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + mask = (1 << w) - 1; /* needed on HP, cc -O bug */ + while ((i & mask) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + mask = (1 << w) - 1; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits(c, bb, tb, hp, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +inflate_huft *hp; /* space for trees */ +z_streamp z; /* for messages */ +{ + int r; + uInt hn = 0; /* hufts used in space */ + uIntf *v; /* work area for huft_build */ + + if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +inflate_huft *hp; /* space for trees */ +z_streamp z; /* for messages */ +{ + int r; + uInt hn = 0; /* hufts used in space */ + uIntf *v; /* work area for huft_build */ + + /* allocate work area */ + if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + /* build literal/length tree */ + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + /* build distance tree */ + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +#endif + } + + /* done */ + ZFREE(z, v); + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +#ifdef BUILDFIXED +local int fixed_built = 0; +#define FIXEDH 544 /* number of hufts used by fixed tables */ +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; +#else +#include "inffixed.h" +#endif + + +int inflate_trees_fixed(bl, bd, tl, td, z) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_streamp z; /* for memory allocation */ +{ +#ifdef BUILDFIXED + /* build fixed tables if not already */ + if (!fixed_built) + { + int k; /* temporary variable */ + uInt f = 0; /* number of hufts used in fixed_mem */ + uIntf *c; /* length list for huft_build */ + uIntf *v; /* work area for huft_build */ + + /* allocate memory */ + if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + { + ZFREE(z, c); + return Z_MEM_ERROR; + } + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 9; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, + fixed_mem, &f, v); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, + fixed_mem, &f, v); + + /* done */ + ZFREE(z, v); + ZFREE(z, c); + fixed_built = 1; + } +#endif + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} diff --git a/zlib/inftrees.h b/zlib/inftrees.h new file mode 100644 index 0000000..04b73b7 --- /dev/null +++ b/zlib/inftrees.h @@ -0,0 +1,58 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit int's) */ + uInt base; /* literal, length base, distance base, + or table offset */ +}; + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1004 huft structures (850 for length/literals + and 154 for distances, the latter actually the result of an + exhaustive search). The actual maximum is not known, but the + value below is more than safe. */ +#define MANY 1440 + +extern int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +extern int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + inflate_huft *, /* space for trees */ + z_streamp)); /* for messages */ + +extern int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_streamp)); /* for memory allocation */ diff --git a/zlib/infutil.c b/zlib/infutil.c new file mode 100644 index 0000000..9a07622 --- /dev/null +++ b/zlib/infutil.c @@ -0,0 +1,87 @@ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "infblock.h" +#include "inftrees.h" +#include "infcodes.h" +#include "infutil.h" + +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ + +/* And'ing with mask[n] masks the lower n bits */ +uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + + +/* copy as much as possible from the sliding window to the output area */ +int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt n; + Bytef *p; + Bytef *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} diff --git a/zlib/infutil.h b/zlib/infutil.h new file mode 100644 index 0000000..4401df8 --- /dev/null +++ b/zlib/infutil.h @@ -0,0 +1,98 @@ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +typedef enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONE, /* finished last block, done */ + BAD} /* got a data error--stuck here */ +inflate_block_mode; + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + inflate_block_mode mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + inflate_huft *hufts; /* single malloc for tree space */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ +extern uInt inflate_mask[17]; + +/* copy as much as possible from the sliding window to the output area */ +extern int inflate_flush OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +struct internal_state {int dummy;}; /* for buggy compilers */ + +#endif diff --git a/zlib/maketree.c b/zlib/maketree.c new file mode 100644 index 0000000..a16d4b1 --- /dev/null +++ b/zlib/maketree.c @@ -0,0 +1,85 @@ +/* maketree.c -- make inffixed.h table for decoding fixed codes + * Copyright (C) 1995-2002 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* This program is included in the distribution for completeness. + You do not need to compile or run this program since inffixed.h + is already included in the distribution. To use this program + you need to compile zlib with BUILDFIXED defined and then compile + and link this program with the zlib library. Then the output of + this program can be piped to inffixed.h. */ + +#include +#include +#include "zutil.h" +#include "inftrees.h" + +/* simplify the use of the inflate_huft type with some defines */ +#define exop word.what.Exop +#define bits word.what.Bits + +/* generate initialization table for an inflate_huft structure array */ +void maketree(uInt b, inflate_huft *t) +{ + int i, e; + + i = 0; + while (1) + { + e = t[i].exop; + if (e && (e & (16+64)) == 0) /* table pointer */ + { + fprintf(stderr, "maketree: cannot initialize sub-tables!\n"); + exit(1); + } + if (i % 4 == 0) + printf("\n "); + printf(" {{{%u,%u}},%u}", t[i].exop, t[i].bits, t[i].base); + if (++i == (1<, or to +Gilles Vollant for the Windows DLL version. +The zlib home page is http://www.cdrom.com/pub/infozip/zlib/ +The official zlib ftp site is ftp://ftp.cdrom.com/pub/infozip/zlib/ +Before reporting a problem, please check those sites to verify that +you have the latest version of zlib; otherwise get the latest version and +check whether the problem still exists or not. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://web2.airmail.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.1.3 are documented in the file ChangeLog. +The main changes since 1.1.2 are: + +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +plus many changes for portability. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit 1.1 +http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html +See the zlib home page http://www.cdrom.com/pub/infozip/zlib/ for details. + +A Perl interface to zlib written by Paul Marquess +is in the CPAN (Comprehensive Perl Archive Network) sites, such as: +ftp://ftp.cis.ufl.edu/pub/perl/CPAN/modules/by-module/Compress/Compress-Zlib* + +A Python interface to zlib written by A.M. Kuchling +is available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries +is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html + +An experimental package to read and write files in .zip format, +written on top of zlib by Gilles Vollant , is +available at http://www.winimage.com/zLibDll/unzip.html +and also in the contrib/minizip directory of zlib. + + +Notes for some targets: + +- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc + and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL + The zlib DLL support was initially done by Alessandro Iacopetti and is + now maintained by Gilles Vollant . Check the zlib DLL + home page at http://www.winimage.com/zLibDll + + From Visual Basic, you can call the DLL functions which do not take + a structure as argument: compress, uncompress and all gz* functions. + See contrib/visual-basic.txt for more information, or get + http://www.tcfb.com/dowseware/cmp-z-it.zip + +- For 64-bit Irix, deflate.c must be compiled without any optimization. + With -O, one libpng test fails. The test works in 32 bit mode (with + the -n32 compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 + it works when compiled with cc. + +- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 + is necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works + with other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For Turbo C the small model is supported only with reduced performance to + avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 + +- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html + Per Harald Myrvang + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. diff --git a/zlib/trees.c b/zlib/trees.c new file mode 100644 index 0000000..c98b000 --- /dev/null +++ b/zlib/trees.c @@ -0,0 +1,1214 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2002 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is ascii or binary */ + if (s->data_type == Z_UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute first the block length in bytes*/ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/zlib/trees.h b/zlib/trees.h new file mode 100644 index 0000000..72facf9 --- /dev/null +++ b/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/zlib/uncompr.c b/zlib/uncompr.c new file mode 100644 index 0000000..3a71259 --- /dev/null +++ b/zlib/uncompr.c @@ -0,0 +1,58 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: uncompr.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/zlib/unzip.c b/zlib/unzip.c new file mode 100644 index 0000000..d156fae --- /dev/null +++ b/zlib/unzip.c @@ -0,0 +1,1301 @@ +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info +*/ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + FILE* file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte(fin,pi) + FILE *fin; + int *pi; +{ + unsigned char c; + int err = fread(&c, 1, 1, fin); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort (fin,pX) + FILE* fin; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong (fin,pX) + FILE* fin; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir(fin) + FILE *fin; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (fseek(fin,0,SEEK_END) != 0) + return 0; + + + uSizeFile = ftell( fin ); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (fseek(fin,uReadPos,SEEK_SET)!=0) + break; + + if (fread(buf,(uInt)uReadSize,1,fin)!=1) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen (path) + const char *path; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + FILE * fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + fin=fopen(path,"rb"); + if (fin==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) + err=UNZ_ERRNO; + + if (fseek(fin,central_pos,SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + fclose(s->file); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (fseek(s->file,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1, + pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { + uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (fread(szComment,(uInt)uReadThis,1,s->file)!=1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} diff --git a/zlib/unzip.h b/zlib/unzip.h new file mode 100644 index 0000000..76692cb --- /dev/null +++ b/zlib/unzip.h @@ -0,0 +1,275 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/zlib/zconf.h b/zlib/zconf.h new file mode 100644 index 0000000..68a9f9c --- /dev/null +++ b/zlib/zconf.h @@ -0,0 +1,279 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +# ifndef __32BIT__ +# define __32BIT__ +# endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) +# define STDC +#endif +#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__) +# ifndef STDC +# define STDC +# endif +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Old Borland C incorrectly complains about missing returns: */ +#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500) +# define NEED_DUMMY_RETURN +#endif + + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +# ifndef __32BIT__ +# define SMALL_MEDIUM +# define FAR _far +# endif +#endif + +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if defined(ZLIB_DLL) +# if defined(_WINDOWS) || defined(WINDOWS) +# ifdef FAR +# undef FAR +# endif +# include +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR _cdecl _export +# endif +# endif +# if defined (__BORLANDC__) +# if (__BORLANDC__ >= 0x0500) && defined (WIN32) +# include +# define ZEXPORT __declspec(dllexport) WINAPI +# define ZEXPORTRVA __declspec(dllexport) WINAPIV +# else +# if defined (_Windows) && defined (__DLL__) +# define ZEXPORT _export +# define ZEXPORTVA _export +# endif +# endif +# endif +#endif + +#if defined (__BEOS__) +# if defined (ZLIB_DLL) +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +#endif + +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif +#ifndef ZEXTERN +# define ZEXTERN extern +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(MACOS) && !defined(TARGET_OS_MAC) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(inflate_blocks,"INBL") +# pragma map(inflate_blocks_new,"INBLNE") +# pragma map(inflate_blocks_free,"INBLFR") +# pragma map(inflate_blocks_reset,"INBLRE") +# pragma map(inflate_codes_free,"INCOFR") +# pragma map(inflate_codes,"INCO") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_flush,"INFLU") +# pragma map(inflate_mask,"INMA") +# pragma map(inflate_set_dictionary,"INSEDI2") +# pragma map(inflate_copyright,"INCOPY") +# pragma map(inflate_trees_bits,"INTRBI") +# pragma map(inflate_trees_dynamic,"INTRDY") +# pragma map(inflate_trees_fixed,"INTRFI") +# pragma map(inflate_trees_free,"INTRFR") +#endif + +#endif /* _ZCONF_H */ diff --git a/zlib/zlib.h b/zlib/zlib.h new file mode 100644 index 0000000..52cb529 --- /dev/null +++ b/zlib/zlib.h @@ -0,0 +1,893 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.1.4, March 11th, 2002 + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef _ZLIB_H +#define _ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.1.4" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may some + introduce some output latency (reading input without producing any output) + except when forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much + output as possible to the output buffer. The flushing behavior of inflate is + not specified for values of the flush parameter other than Z_SYNC_FLUSH + and Z_FINISH, but the current implementation actually flushes as much output + as possible anyway. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + If a preset dictionary is needed at this point (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the + dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise + it sets strm->adler to the adler32 checksum of all output produced + so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or + an error code as described below. At the end of the stream, inflate() + checks that its computed adler32 checksum is equal to that saved by the + compressor and returns Z_STREAM_END only if the checksum is correct. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect + adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent + (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if no progress is possible or if there was not + enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR + case, the application may then call inflateSync to look for a good + compression block. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. If a compressed stream with a larger window size is given as + input, inflate() will return with the error code Z_DATA_ERROR instead of + trying to allocate a larger window. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least 0.1% larger than + sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h". (See the description + of deflateInit2 for more information about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + const voidp buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int err)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZLIB_H */ diff --git a/zlib/zutil.c b/zlib/zutil.c new file mode 100644 index 0000000..08166c0 --- /dev/null +++ b/zlib/zutil.c @@ -0,0 +1,225 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#include "zutil.h" + +struct internal_state {int dummy;}; /* for buggy compilers */ + +#ifndef STDC +extern void exit OF((int)); +#endif + +const char *z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifdef __TURBOC__ +#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__) +/* Small and medium model in Turbo C are for now limited to near allocation + * with reduced MAX_WBITS and MAX_MEM_LEVEL + */ +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} +#endif +#endif /* __TURBOC__ */ + + +#if defined(M_I86) && !defined(__32BIT__) +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* MSC */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/zlib/zutil.h b/zlib/zutil.h new file mode 100644 index 0000000..507cab9 --- /dev/null +++ b/zlib/zutil.h @@ -0,0 +1,220 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.2 2002/03/13 17:45:56 xodnizel Exp $ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + +#include "zlib.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#ifdef MSDOS +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +# define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# define fdopen(fd,type) _fdopen(fd,type) +#endif + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf, + uInt len)); +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* _Z_UTIL_H */ -- 2.39.2