Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fe5b1a41d | |||
| da07e7e466 | |||
| 0f0245743f | |||
| 137232a50d | |||
| d891dc75e4 | |||
| 89ac34677d | |||
| a25c7277cd | |||
| 78cdb9a493 | |||
| d47ffbc5ec | |||
| 85a75b3ec7 | |||
| 97f3d66a9d | |||
| dab27bf1d2 | |||
| 955658e547 | |||
| c614a91a1f | |||
| f6f74109be | |||
| a7ad4d21cf | |||
| 944d9954bb | |||
| 4274fa8510 | |||
| f8c7919cdf | |||
| c3db984ef7 | |||
| f0ebea6655 | |||
| d1b1e673c0 | |||
| 83c3edc625 | |||
| 912191aa94 | |||
| e7c8745b4e | |||
| 9ce27a4cd6 | |||
| bea03d023c | |||
| 731eed88d8 | |||
| e6ba3a7e80 | |||
| 49d6e9721e | |||
| 66823f0f5a | |||
| a40d4f03d0 | |||
| 114fcdd3fa | |||
| c67d23d158 | |||
| d8ed15b6d0 | |||
| 80f06c9e18 | |||
| 674e6454bf | |||
| aa2bc8680f | |||
| 062e83f7d7 | |||
| a9e16763d2 | |||
| 3fc41431fb | |||
| 718b2932a7 | |||
| f77d286df6 | |||
| c45912f8b4 | |||
| cd18b5e165 |
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2009, 2012 Lawrence Sebald
|
||||
Copyright (C) 2009, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef CRABEMU_H
|
||||
#define CRABEMU_H
|
||||
|
||||
#define VERSION "0.2.0"
|
||||
#define VERSION "0.2.1"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define CLINKAGE extern "C" {
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
#include <arch/types.h>
|
||||
#include <stdint.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
typedef uint8_t uint8;
|
||||
@@ -127,6 +128,11 @@ typedef uint16 pixel_t;
|
||||
CLINKAGE
|
||||
extern void gui_set_aspect(float x, float y);
|
||||
extern void gui_set_title(const char *str);
|
||||
|
||||
/* Forward declaration... */
|
||||
struct crabemu_console;
|
||||
|
||||
extern void gui_set_console(struct crabemu_console *c);
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !CRABEMU_H */
|
||||
|
||||
@@ -35,10 +35,15 @@
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1B46D09D14293CAD0025EF88 /* libz.1.2.5.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B46D09C14293CAD0025EF88 /* libz.1.2.5.dylib */; };
|
||||
1B46D09D14293CAD0025EF88 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B46D09C14293CAD0025EF88 /* libz.tbd */; };
|
||||
27E3D4E41B6543BC00D8D8B5 /* fmemopen.c in Sources */ = {isa = PBXBuildFile; fileRef = 27E3D4E21B6543BC00D8D8B5 /* fmemopen.c */; };
|
||||
27E3D4E71B6543D500D8D8B5 /* open_memstream.c in Sources */ = {isa = PBXBuildFile; fileRef = 27E3D4E51B6543D500D8D8B5 /* open_memstream.c */; };
|
||||
3720DB4D0F19510D00744A9A /* CrabEmu.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3720DB4C0F19510D00744A9A /* CrabEmu.icns */; };
|
||||
82CAFADA0FEDA5CA00CCDC7E /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 82CAFAD90FEDA5CA00CCDC7E /* dsa_pub.pem */; };
|
||||
82CAFDB00FEDD88100CCDC7E /* config.yaml in CopyFiles */ = {isa = PBXBuildFile; fileRef = 82CAFADD0FEDA5EA00CCDC7E /* config.yaml */; };
|
||||
834A0CAC1B79371A0073803A /* nesmapper2.c in Sources */ = {isa = PBXBuildFile; fileRef = 878700DF1B675EB4006841C9 /* nesmapper2.c */; };
|
||||
834A0CAD1B79371A0073803A /* nesmapper3.c in Sources */ = {isa = PBXBuildFile; fileRef = 878700E01B675EB4006841C9 /* nesmapper3.c */; };
|
||||
834A0CAE1B79371A0073803A /* nesmapper7.c in Sources */ = {isa = PBXBuildFile; fileRef = 878700E11B675EB4006841C9 /* nesmapper7.c */; };
|
||||
834A0CAF1B79371A0073803A /* nesmapper9.c in Sources */ = {isa = PBXBuildFile; fileRef = 878700E21B675EB4006841C9 /* nesmapper9.c */; };
|
||||
834A0CB01B79371A0073803A /* nesmapper66.c in Sources */ = {isa = PBXBuildFile; fileRef = 878700E31B675EB4006841C9 /* nesmapper66.c */; };
|
||||
8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; };
|
||||
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; settings = {ATTRIBUTES = (Required, ); }; };
|
||||
9443D4411715F42200E452AC /* CrabZ80.c in Sources */ = {isa = PBXBuildFile; fileRef = 9443D3FC1715F31000E452AC /* CrabZ80.c */; };
|
||||
@@ -80,7 +85,6 @@
|
||||
9443D4671715F74100E452AC /* mmc5_snd.c in Sources */ = {isa = PBXBuildFile; fileRef = 9443D42B1715F33C00E452AC /* mmc5_snd.c */; };
|
||||
9443D4681715F74F00E452AC /* nes_apu.c in Sources */ = {isa = PBXBuildFile; fileRef = 9443D42D1715F33C00E452AC /* nes_apu.c */; };
|
||||
9443D4691715F75700E452AC /* vrcvisnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 9443D42F1715F33C00E452AC /* vrcvisnd.c */; };
|
||||
9443D46D1715FFE600E452AC /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = 9443D46B1715FFE600E452AC /* list.c */; };
|
||||
94BE9E7A171695AE00AB08E6 /* OEColecoVisionSystemResponderClient.h in Resources */ = {isa = PBXBuildFile; fileRef = 94BE9E79171695AE00AB08E6 /* OEColecoVisionSystemResponderClient.h */; };
|
||||
C66DFC1A0F51D82F0080AA28 /* SMSGameCore.m in Sources */ = {isa = PBXBuildFile; fileRef = C66DFC180F51D82F0080AA28 /* SMSGameCore.m */; };
|
||||
C6D120E91711302600E868A8 /* OpenEmuBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6D120E71711302600E868A8 /* OpenEmuBase.framework */; };
|
||||
@@ -110,22 +114,38 @@
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
82CAFDB00FEDD88100CCDC7E /* config.yaml in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
1B46D09C14293CAD0025EF88 /* libz.1.2.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.2.5.dylib; path = usr/lib/libz.1.2.5.dylib; sourceTree = SDKROOT; };
|
||||
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
089C167EFE841241C02AAC07 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
1B46D09C14293CAD0025EF88 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
27E3D4E21B6543BC00D8D8B5 /* fmemopen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmemopen.c; sourceTree = "<group>"; };
|
||||
27E3D4E31B6543BC00D8D8B5 /* fmemopen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmemopen.h; sourceTree = "<group>"; };
|
||||
27E3D4E51B6543D500D8D8B5 /* open_memstream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = open_memstream.c; sourceTree = "<group>"; };
|
||||
27E3D4E61B6543D500D8D8B5 /* open_memstream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = open_memstream.h; sourceTree = "<group>"; };
|
||||
3720DB4C0F19510D00744A9A /* CrabEmu.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = CrabEmu.icns; sourceTree = "<group>"; };
|
||||
8291C4E21489595000A72540 /* OEGGSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OEGGSystemResponderClient.h; path = ../OpenEmu/GameGear/OEGGSystemResponderClient.h; sourceTree = "<group>"; };
|
||||
82CAFAD90FEDA5CA00CCDC7E /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = "<group>"; };
|
||||
82CAFADD0FEDA5EA00CCDC7E /* config.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = config.yaml; sourceTree = "<group>"; };
|
||||
8291C4E21489595000A72540 /* OEGGSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OEGGSystemResponderClient.h; path = ../OpenEmu/SystemPlugins/GameGear/OEGGSystemResponderClient.h; sourceTree = "<group>"; };
|
||||
878700D11B674AB3006841C9 /* queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue.h; path = utils/queue.h; sourceTree = "<group>"; };
|
||||
878700DA1B675E9C006841C9 /* chip8.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = chip8.c; path = chip8/chip8.c; sourceTree = "<group>"; };
|
||||
878700DB1B675E9C006841C9 /* chip8.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = chip8.h; path = chip8/chip8.h; sourceTree = "<group>"; };
|
||||
878700DC1B675E9C006841C9 /* chip8cpu.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = chip8cpu.c; path = chip8/chip8cpu.c; sourceTree = "<group>"; };
|
||||
878700DD1B675E9C006841C9 /* chip8cpu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = chip8cpu.h; path = chip8/chip8cpu.h; sourceTree = "<group>"; };
|
||||
878700DE1B675E9C006841C9 /* chip8cpuops.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = chip8cpuops.h; path = chip8/chip8cpuops.h; sourceTree = "<group>"; };
|
||||
878700DF1B675EB4006841C9 /* nesmapper2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nesmapper2.c; sourceTree = "<group>"; };
|
||||
878700E01B675EB4006841C9 /* nesmapper3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nesmapper3.c; sourceTree = "<group>"; };
|
||||
878700E11B675EB4006841C9 /* nesmapper7.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nesmapper7.c; sourceTree = "<group>"; };
|
||||
878700E21B675EB4006841C9 /* nesmapper9.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nesmapper9.c; sourceTree = "<group>"; };
|
||||
878700E31B675EB4006841C9 /* nesmapper66.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nesmapper66.c; sourceTree = "<group>"; };
|
||||
878700E41B675EF0006841C9 /* console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = console.h; sourceTree = "<group>"; };
|
||||
87ACB6E41D26C1F600138B7E /* CrabZ80_gbmacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CrabZ80_gbmacros.h; sourceTree = "<group>"; };
|
||||
87ACB6E51D26C1F600138B7E /* CrabZ80gbops.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CrabZ80gbops.h; sourceTree = "<group>"; };
|
||||
87ACB6E61D26C1F700138B7E /* CrabZ80gbopsCB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CrabZ80gbopsCB.h; sourceTree = "<group>"; };
|
||||
8D5B49B6048680CD000E48DA /* CrabEmu.oecoreplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CrabEmu.oecoreplugin; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9443D39B1715F2EB00E452AC /* colecomem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = colecomem.c; sourceTree = "<group>"; };
|
||||
@@ -177,7 +197,6 @@
|
||||
9443D3CC1715F2EB00E452AC /* smsvdp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smsvdp.c; sourceTree = "<group>"; };
|
||||
9443D3CD1715F2EB00E452AC /* smsvdp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = smsvdp.h; sourceTree = "<group>"; };
|
||||
9443D3CE1715F2EB00E452AC /* smsz80-cz80.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "smsz80-cz80.c"; sourceTree = "<group>"; };
|
||||
9443D3CF1715F2EB00E452AC /* smsz80-debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "smsz80-debug.c"; sourceTree = "<group>"; };
|
||||
9443D3D01715F2EB00E452AC /* smsz80.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smsz80.c; sourceTree = "<group>"; };
|
||||
9443D3D11715F2EB00E452AC /* smsz80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = smsz80.h; sourceTree = "<group>"; };
|
||||
9443D3D21715F2EB00E452AC /* terebi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = terebi.c; sourceTree = "<group>"; };
|
||||
@@ -213,8 +232,6 @@
|
||||
9443D4101715F31000E452AC /* cz80exec.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; path = cz80exec.inc; sourceTree = "<group>"; };
|
||||
9443D4111715F31000E452AC /* cz80jmp.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; path = cz80jmp.inc; sourceTree = "<group>"; };
|
||||
9443D4121715F31000E452AC /* readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = readme.txt; sourceTree = "<group>"; };
|
||||
9443D4141715F31000E452AC /* z80.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = z80.c; sourceTree = "<group>"; };
|
||||
9443D4151715F31000E452AC /* z80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = z80.h; sourceTree = "<group>"; };
|
||||
9443D4261715F33C00E452AC /* sound.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sound.c; sourceTree = "<group>"; };
|
||||
9443D4281715F33C00E452AC /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
|
||||
9443D4291715F33C00E452AC /* fds_snd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fds_snd.c; sourceTree = "<group>"; };
|
||||
@@ -234,16 +251,14 @@
|
||||
9443D4371715F33C00E452AC /* ym2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ym2413.h; sourceTree = "<group>"; };
|
||||
9443D45B1715F67D00E452AC /* rom.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rom.c; sourceTree = "<group>"; };
|
||||
9443D45C1715F67D00E452AC /* rom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rom.h; sourceTree = "<group>"; };
|
||||
9443D46B1715FFE600E452AC /* list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = list.c; path = utils/list.c; sourceTree = "<group>"; };
|
||||
9443D46C1715FFE600E452AC /* list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = list.h; path = utils/list.h; sourceTree = "<group>"; };
|
||||
946C18AF171491B300C64BF9 /* OESG1000SystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OESG1000SystemResponderClient.h; path = "../OpenEmu/SG-1000/OESG1000SystemResponderClient.h"; sourceTree = "<group>"; };
|
||||
94BE9E79171695AE00AB08E6 /* OEColecoVisionSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OEColecoVisionSystemResponderClient.h; path = ../OpenEmu/ColecoVision/OEColecoVisionSystemResponderClient.h; sourceTree = "<group>"; };
|
||||
946C18AF171491B300C64BF9 /* OESG1000SystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OESG1000SystemResponderClient.h; path = "../OpenEmu/SystemPlugins/SG-1000/OESG1000SystemResponderClient.h"; sourceTree = "<group>"; };
|
||||
94BE9E79171695AE00AB08E6 /* OEColecoVisionSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OEColecoVisionSystemResponderClient.h; path = ../OpenEmu/SystemPlugins/ColecoVision/OEColecoVisionSystemResponderClient.h; sourceTree = "<group>"; };
|
||||
B5DA7D8C0F5B28940008F047 /* CrabEmu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrabEmu.h; sourceTree = "<group>"; };
|
||||
C66DFC180F51D82F0080AA28 /* SMSGameCore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SMSGameCore.m; sourceTree = "<group>"; };
|
||||
C66DFC190F51D82F0080AA28 /* SMSGameCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SMSGameCore.h; sourceTree = "<group>"; };
|
||||
C6B3E67C1365255700D34947 /* OESMSSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OESMSSystemResponderClient.h; path = ../OpenEmu/SegaMasterSystem/OESMSSystemResponderClient.h; sourceTree = "<group>"; };
|
||||
C6B3E67C1365255700D34947 /* OESMSSystemResponderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OESMSSystemResponderClient.h; path = ../OpenEmu/SystemPlugins/SegaMasterSystem/OESMSSystemResponderClient.h; sourceTree = "<group>"; };
|
||||
C6D120E71711302600E868A8 /* OpenEmuBase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OpenEmuBase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
||||
D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -253,7 +268,7 @@
|
||||
files = (
|
||||
C6D120E91711302600E868A8 /* OpenEmuBase.framework in Frameworks */,
|
||||
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */,
|
||||
1B46D09D14293CAD0025EF88 /* libz.1.2.5.dylib in Frameworks */,
|
||||
1B46D09D14293CAD0025EF88 /* libz.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -266,26 +281,26 @@
|
||||
08FB77AFFE84173DC02AAC07 /* Classes */,
|
||||
32C88E010371C26100C91783 /* Other Sources */,
|
||||
089C167CFE841241C02AAC07 /* Resources */,
|
||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
|
||||
089C1671FE841209C02AAC07 /* Frameworks */,
|
||||
19C28FB8FE9D52D311CA2CBB /* Products */,
|
||||
);
|
||||
name = CrabEmu;
|
||||
sourceTree = "<group>";
|
||||
usesTabs = 0;
|
||||
};
|
||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = {
|
||||
089C1671FE841209C02AAC07 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6D120E71711302600E868A8 /* OpenEmuBase.framework */,
|
||||
1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */,
|
||||
1058C7AEFEA557BF11CA2CBB /* Other Frameworks */,
|
||||
);
|
||||
name = "Frameworks and Libraries";
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
089C167CFE841241C02AAC07 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
82CAFAD90FEDA5CA00CCDC7E /* dsa_pub.pem */,
|
||||
8D5B49B7048680CD000E48DA /* Info.plist */,
|
||||
089C167DFE841241C02AAC07 /* InfoPlist.strings */,
|
||||
3720DB4C0F19510D00744A9A /* CrabEmu.icns */,
|
||||
@@ -309,7 +324,7 @@
|
||||
1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1B46D09C14293CAD0025EF88 /* libz.1.2.5.dylib */,
|
||||
1B46D09C14293CAD0025EF88 /* libz.tbd */,
|
||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */,
|
||||
);
|
||||
name = "Linked Frameworks";
|
||||
@@ -333,10 +348,22 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
27E3D4E11B65439900D8D8B5 /* fmemopen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
27E3D4E21B6543BC00D8D8B5 /* fmemopen.c */,
|
||||
27E3D4E31B6543BC00D8D8B5 /* fmemopen.h */,
|
||||
27E3D4E51B6543D500D8D8B5 /* open_memstream.c */,
|
||||
27E3D4E61B6543D500D8D8B5 /* open_memstream.h */,
|
||||
);
|
||||
path = fmemopen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32C88E010371C26100C91783 /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
82CAFADD0FEDA5EA00CCDC7E /* config.yaml */,
|
||||
27E3D4E11B65439900D8D8B5 /* fmemopen */,
|
||||
878700E41B675EF0006841C9 /* console.h */,
|
||||
B5DA7D8C0F5B28940008F047 /* CrabEmu.h */,
|
||||
9443D45B1715F67D00E452AC /* rom.c */,
|
||||
9443D45C1715F67D00E452AC /* rom.h */,
|
||||
@@ -348,6 +375,18 @@
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
878700D21B675E74006841C9 /* chip8 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
878700DA1B675E9C006841C9 /* chip8.c */,
|
||||
878700DB1B675E9C006841C9 /* chip8.h */,
|
||||
878700DC1B675E9C006841C9 /* chip8cpu.c */,
|
||||
878700DD1B675E9C006841C9 /* chip8cpu.h */,
|
||||
878700DE1B675E9C006841C9 /* chip8cpuops.h */,
|
||||
);
|
||||
name = chip8;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9443D39A1715F2EB00E452AC /* colecovision */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -379,6 +418,11 @@
|
||||
children = (
|
||||
9443D3A11715F2EB00E452AC /* nesmapper0.c */,
|
||||
9443D3A21715F2EB00E452AC /* nesmapper1.c */,
|
||||
878700DF1B675EB4006841C9 /* nesmapper2.c */,
|
||||
878700E01B675EB4006841C9 /* nesmapper3.c */,
|
||||
878700E11B675EB4006841C9 /* nesmapper7.c */,
|
||||
878700E21B675EB4006841C9 /* nesmapper9.c */,
|
||||
878700E31B675EB4006841C9 /* nesmapper66.c */,
|
||||
);
|
||||
path = mappers;
|
||||
sourceTree = "<group>";
|
||||
@@ -422,7 +466,6 @@
|
||||
9443D3CC1715F2EB00E452AC /* smsvdp.c */,
|
||||
9443D3CD1715F2EB00E452AC /* smsvdp.h */,
|
||||
9443D3CE1715F2EB00E452AC /* smsz80-cz80.c */,
|
||||
9443D3CF1715F2EB00E452AC /* smsz80-debug.c */,
|
||||
9443D3D01715F2EB00E452AC /* smsz80.c */,
|
||||
9443D3D11715F2EB00E452AC /* smsz80.h */,
|
||||
9443D3D21715F2EB00E452AC /* terebi.c */,
|
||||
@@ -456,6 +499,9 @@
|
||||
9443D3FF1715F31000E452AC /* CrabZ80_tables.h */,
|
||||
9443D4001715F31000E452AC /* CrabZ80d.c */,
|
||||
9443D4011715F31000E452AC /* CrabZ80d.h */,
|
||||
87ACB6E41D26C1F600138B7E /* CrabZ80_gbmacros.h */,
|
||||
87ACB6E51D26C1F600138B7E /* CrabZ80gbops.h */,
|
||||
87ACB6E61D26C1F700138B7E /* CrabZ80gbopsCB.h */,
|
||||
9443D4021715F31000E452AC /* CrabZ80ops.h */,
|
||||
9443D4031715F31000E452AC /* CrabZ80opsCB.h */,
|
||||
9443D4041715F31000E452AC /* CrabZ80opsDD-FD.h */,
|
||||
@@ -483,15 +529,6 @@
|
||||
path = cz80;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9443D4131715F31000E452AC /* mamez80 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9443D4141715F31000E452AC /* z80.c */,
|
||||
9443D4151715F31000E452AC /* z80.h */,
|
||||
);
|
||||
path = mamez80;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9443D4251715F33C00E452AC /* coreaudio */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -519,8 +556,7 @@
|
||||
9443D46A1715FFA200E452AC /* utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9443D46B1715FFE600E452AC /* list.c */,
|
||||
9443D46C1715FFE600E452AC /* list.h */,
|
||||
878700D11B674AB3006841C9 /* queue.h */,
|
||||
);
|
||||
name = utils;
|
||||
sourceTree = "<group>";
|
||||
@@ -528,6 +564,7 @@
|
||||
B50C176B0E936FD500A8FA7E /* consoles */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
878700D21B675E74006841C9 /* chip8 */,
|
||||
9443D39A1715F2EB00E452AC /* colecovision */,
|
||||
9443D39F1715F2EB00E452AC /* nes */,
|
||||
9443D3AA1715F2EB00E452AC /* sms */,
|
||||
@@ -541,7 +578,6 @@
|
||||
9443D3F31715F31000E452AC /* Crab6502 */,
|
||||
9443D3FB1715F31000E452AC /* CrabZ80 */,
|
||||
9443D4071715F31000E452AC /* cz80 */,
|
||||
9443D4131715F31000E452AC /* mamez80 */,
|
||||
);
|
||||
path = cpu;
|
||||
sourceTree = "<group>";
|
||||
@@ -589,12 +625,11 @@
|
||||
089C1669FE841209C02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 0460;
|
||||
LastUpgradeCheck = 0700;
|
||||
};
|
||||
buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "CrabEmu" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
en,
|
||||
@@ -617,7 +652,6 @@
|
||||
files = (
|
||||
8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */,
|
||||
3720DB4D0F19510D00744A9A /* CrabEmu.icns in Resources */,
|
||||
82CAFADA0FEDA5CA00CCDC7E /* dsa_pub.pem in Resources */,
|
||||
94BE9E7A171695AE00AB08E6 /* OEColecoVisionSystemResponderClient.h in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -659,14 +693,21 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
834A0CAC1B79371A0073803A /* nesmapper2.c in Sources */,
|
||||
834A0CAD1B79371A0073803A /* nesmapper3.c in Sources */,
|
||||
834A0CAE1B79371A0073803A /* nesmapper7.c in Sources */,
|
||||
834A0CAF1B79371A0073803A /* nesmapper9.c in Sources */,
|
||||
834A0CB01B79371A0073803A /* nesmapper66.c in Sources */,
|
||||
C66DFC1A0F51D82F0080AA28 /* SMSGameCore.m in Sources */,
|
||||
9443D4411715F42200E452AC /* CrabZ80.c in Sources */,
|
||||
9443D4421715F42500E452AC /* CrabZ80d.c in Sources */,
|
||||
9443D4431715F44E00E452AC /* 93c46.c in Sources */,
|
||||
9443D4441715F45C00E452AC /* mappers.c in Sources */,
|
||||
9443D4451715F46100E452AC /* sms.c in Sources */,
|
||||
27E3D4E71B6543D500D8D8B5 /* open_memstream.c in Sources */,
|
||||
9443D4461715F46900E452AC /* smsmem.c in Sources */,
|
||||
9443D4471715F47A00E452AC /* terebi.c in Sources */,
|
||||
27E3D4E41B6543BC00D8D8B5 /* fmemopen.c in Sources */,
|
||||
9443D4481715F48300E452AC /* tms9918a.c in Sources */,
|
||||
9443D4491715F4AC00E452AC /* sn76489.c in Sources */,
|
||||
9443D44A1715F4F100E452AC /* smsmem-gg.c in Sources */,
|
||||
@@ -699,7 +740,6 @@
|
||||
9443D4681715F74F00E452AC /* nes_apu.c in Sources */,
|
||||
9443D4691715F75700E452AC /* vrcvisnd.c in Sources */,
|
||||
9443D4601715F6EB00E452AC /* nesapu-nosefart.c in Sources */,
|
||||
9443D46D1715FFE600E452AC /* list.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -722,7 +762,7 @@
|
||||
089C167DFE841241C02AAC07 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
089C167EFE841241C02AAC07 /* English */,
|
||||
089C167EFE841241C02AAC07 /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
@@ -744,6 +784,7 @@
|
||||
"-DNO_BZ2",
|
||||
"-DCRABEMU_32BIT_COLOR",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.openemu.${PRODUCT_NAME:identifier}";
|
||||
PRODUCT_NAME = CrabEmu;
|
||||
SKIP_INSTALL = YES;
|
||||
WRAPPER_EXTENSION = oecoreplugin;
|
||||
@@ -764,6 +805,7 @@
|
||||
"-DNO_BZ2",
|
||||
"-DCRABEMU_32BIT_COLOR",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.openemu.${PRODUCT_NAME:identifier}";
|
||||
PRODUCT_NAME = CrabEmu;
|
||||
SKIP_INSTALL = YES;
|
||||
WRAPPER_EXTENSION = oecoreplugin;
|
||||
@@ -774,24 +816,35 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEPLOYMENT_LOCATION = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INSTALL_PATH = "$(HOME)/Library/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
"MACOSX_DEPLOYMENT_TARGET[arch=arm64]" = 11.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-DCRABZ80_NO_READMAP_FALLBACK",
|
||||
"-DIN_CRABEMU",
|
||||
@@ -804,25 +857,34 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEPLOYMENT_LOCATION = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 3;
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
|
||||
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = "";
|
||||
INSTALL_PATH = "$(HOME)/Library/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
"MACOSX_DEPLOYMENT_TARGET[arch=arm64]" = 11.0;
|
||||
OTHER_CFLAGS = (
|
||||
"-DCRABZ80_NO_READMAP_FALLBACK",
|
||||
"-DIN_CRABEMU",
|
||||
@@ -834,7 +896,6 @@
|
||||
82CAFB620FEDA62500CCDC7E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INSTALL_PATH = "\"$(USER_LIBRARY_DIR)/Application Support/OpenEmu/Cores\"";
|
||||
PRODUCT_NAME = Distribution;
|
||||
@@ -845,7 +906,6 @@
|
||||
82CAFB630FEDA62500CCDC7E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INSTALL_PATH = "\"$(USER_LIBRARY_DIR)/Application Support/OpenEmu/Cores\"";
|
||||
PRODUCT_NAME = Distribution;
|
||||
|
||||
Binary file not shown.
+66
-13
@@ -3,13 +3,13 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>CrabEmu</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openemu.${PRODUCT_NAME:identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
@@ -17,31 +17,84 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>OpEm</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.2.0</string>
|
||||
<string>0.2.1.269</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>OEGameCoreController</string>
|
||||
<key>OEGameCoreClass</key>
|
||||
<string>SMSGameCore</string>
|
||||
<key>OEGameCoreOptions</key>
|
||||
<dict>
|
||||
<key>openemu.system.colecovision</key>
|
||||
<dict>
|
||||
<key>OEGameCoreRequiresFiles</key>
|
||||
<true/>
|
||||
<key>OEGameCoreRewindBufferSeconds</key>
|
||||
<integer>60</integer>
|
||||
<key>OEGameCoreRewindInterval</key>
|
||||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
<key>OERequiredFiles</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Description</key>
|
||||
<string>ColecoVision BIOS</string>
|
||||
<key>MD5</key>
|
||||
<string>2c66f5911e5b42b8ebe113403548eee7</string>
|
||||
<key>Name</key>
|
||||
<string>coleco.rom</string>
|
||||
<key>Size</key>
|
||||
<integer>8192</integer>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>openemu.system.gg</key>
|
||||
<dict>
|
||||
<key>OEGameCoreRewindBufferSeconds</key>
|
||||
<integer>60</integer>
|
||||
<key>OEGameCoreRewindInterval</key>
|
||||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>openemu.system.sg1000</key>
|
||||
<dict>
|
||||
<key>OEGameCoreRewindBufferSeconds</key>
|
||||
<integer>60</integer>
|
||||
<key>OEGameCoreRewindInterval</key>
|
||||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>openemu.system.sms</key>
|
||||
<dict>
|
||||
<key>OEGameCoreRewindBufferSeconds</key>
|
||||
<integer>60</integer>
|
||||
<key>OEGameCoreRewindInterval</key>
|
||||
<integer>0</integer>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsRewinding</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OEGameCorePlayerCount</key>
|
||||
<string>2</string>
|
||||
<key>OEGameCoreRequiresFiles</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsCheatCode</key>
|
||||
<true/>
|
||||
<key>OEProjectURL</key>
|
||||
<string>http://crabemu.sourceforge.net/</string>
|
||||
<key>OESystemIdentifiers</key>
|
||||
<array>
|
||||
<string>openemu.system.sms</string>
|
||||
<string>openemu.system.gg</string>
|
||||
<string>openemu.system.sg1000</string>
|
||||
<string>openemu.system.colecovision</string>
|
||||
</array>
|
||||
<key>SUEnableAutomaticChecks</key>
|
||||
<string>1</string>
|
||||
<key>SUFeedURL</key>
|
||||
<string>http://openemu.org/updater/crabemu_appcast.xml</string>
|
||||
<key>SUPublicDSAKeyFile</key>
|
||||
<string>dsa_pub.pem</string>
|
||||
<string>https://raw.github.com/OpenEmu/OpenEmu-Update/master/crabemu_appcast.xml</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -49,12 +49,4 @@ typedef enum SMSButtons {
|
||||
|
||||
OE_EXPORTED_CLASS
|
||||
@interface SMSGameCore : OEGameCore
|
||||
{
|
||||
unsigned char *tempBuffer;
|
||||
NSLock *bufLock;
|
||||
int oldrun;
|
||||
int position;
|
||||
BOOL paused;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+237
-232
@@ -25,7 +25,6 @@
|
||||
*/
|
||||
|
||||
#import "SMSGameCore.h"
|
||||
#import <IOKit/hid/IOHIDLib.h>
|
||||
#import <OpenEmuBase/OERingBuffer.h>
|
||||
#import <OpenGL/gl.h>
|
||||
#import "OESMSSystemResponderClient.h"
|
||||
@@ -33,8 +32,6 @@
|
||||
#import "OESG1000SystemResponderClient.h"
|
||||
#import "OEColecoVisionSystemResponderClient.h"
|
||||
|
||||
#define _UINT32
|
||||
|
||||
#include "sms.h"
|
||||
#include "smsmem.h"
|
||||
#include "sound.h"
|
||||
@@ -44,29 +41,29 @@
|
||||
#include "colecovision.h"
|
||||
#include "colecomem.h"
|
||||
#include "cheats.h"
|
||||
#include "console.h"
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13
|
||||
#include "fmemopen/fmemopen.h"
|
||||
#include "fmemopen/open_memstream.h"
|
||||
#endif
|
||||
|
||||
#define SAMPLERATE 44100
|
||||
|
||||
@interface SMSGameCore () <OESMSSystemResponderClient, OEGGSystemResponderClient, OESG1000SystemResponderClient, OEColecoVisionSystemResponderClient>
|
||||
{
|
||||
NSString *romName;
|
||||
NSLock *bufLock;
|
||||
BOOL paused;
|
||||
NSURL *romFile;
|
||||
NSMutableDictionary *cheatList;
|
||||
}
|
||||
- (int)crabButtonForButton:(OESMSButton)button player:(NSUInteger)player;
|
||||
- (int)crabButtonForSG1000Button:(OESG1000Button)button;
|
||||
- (int)crabButtonForColecoVisionButton:(OEColecoVisionButton)button player:(NSUInteger)player;
|
||||
@end
|
||||
|
||||
@implementation SMSGameCore
|
||||
|
||||
extern int sms_initialized;
|
||||
extern int sms_console;
|
||||
extern int coleco_initialized;
|
||||
|
||||
// Global variables because the callbacks need to access them...
|
||||
static OERingBuffer *ringBuffer;
|
||||
/*
|
||||
OpenEmu Core internal functions
|
||||
*/
|
||||
console_t *cur_console;
|
||||
|
||||
- (id)init
|
||||
{
|
||||
@@ -74,9 +71,7 @@ static OERingBuffer *ringBuffer;
|
||||
if(self != nil)
|
||||
{
|
||||
bufLock = [[NSLock alloc] init];
|
||||
tempBuffer = malloc(256 * 256 * 4);
|
||||
|
||||
position = 0;
|
||||
cheatList = [[NSMutableDictionary alloc] init];
|
||||
ringBuffer = [self ringBufferAtIndex:0];
|
||||
}
|
||||
return self;
|
||||
@@ -85,120 +80,141 @@ static OERingBuffer *ringBuffer;
|
||||
- (void)dealloc
|
||||
{
|
||||
DLog(@"releasing/deallocating CrabEmu memory");
|
||||
free(tempBuffer);
|
||||
|
||||
sms_initialized = 0;
|
||||
coleco_initialized = 0;
|
||||
|
||||
cur_console->shutdown();
|
||||
}
|
||||
|
||||
- (void)executeFrame
|
||||
{
|
||||
//DLog(@"Executing");
|
||||
//Get a reference to the emulator
|
||||
[bufLock lock];
|
||||
if(sms_console == CONSOLE_COLECOVISION)
|
||||
oldrun = coleco_frame(oldrun, 0);
|
||||
else
|
||||
oldrun = sms_frame(oldrun, 0);
|
||||
[bufLock unlock];
|
||||
}
|
||||
# pragma mark - Execution
|
||||
|
||||
- (BOOL)loadFileAtPath:(NSString *)path
|
||||
- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
romName = [path copy];
|
||||
int console = rom_detect_console([path UTF8String]);
|
||||
romFile = [NSURL fileURLWithPath:path];
|
||||
int console = rom_detect_console(path.fileSystemRepresentation);
|
||||
DLog(@"Loaded File");
|
||||
//TODO: add choice NTSC/PAL
|
||||
if(console == CONSOLE_COLECOVISION)
|
||||
{
|
||||
NSString *biosPath = [NSString pathWithComponents:@[
|
||||
[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject],
|
||||
@"OpenEmu", @"BIOS", @"coleco.rom"]];
|
||||
NSString *biosPath = [[self biosDirectoryPath] stringByAppendingPathComponent:@"coleco.rom"];
|
||||
coleco_init(VIDEO_NTSC);
|
||||
coleco_mem_load_bios([biosPath UTF8String]);
|
||||
coleco_mem_load_rom([path UTF8String]);
|
||||
coleco_mem_load_bios(biosPath.fileSystemRepresentation);
|
||||
coleco_mem_load_rom(path.fileSystemRepresentation);
|
||||
}
|
||||
else
|
||||
{
|
||||
sms_init(SMS_VIDEO_NTSC, SMS_REGION_DOMESTIC);
|
||||
sms_mem_load_rom([path UTF8String], console);
|
||||
sms_frame(0, 1);
|
||||
NSData *dataObj = [NSData dataWithContentsOfFile:[path stringByStandardizingPath]];
|
||||
const void *data = [dataObj bytes];
|
||||
uint16_t offset = 0;
|
||||
int region = SMS_REGION_DOMESTIC;
|
||||
|
||||
// Detect SMS ROM header
|
||||
if(memcmp(&data[0x1ff0], "TMR SEGA", 8) == 0)
|
||||
offset = 0x1ff0;
|
||||
else if (memcmp(&data[0x3ff0], "TMR SEGA", 8) == 0)
|
||||
offset = 0x3ff0;
|
||||
else if (memcmp(&data[0x7ff0], "TMR SEGA", 8) == 0)
|
||||
offset = 0x7ff0;
|
||||
|
||||
if(offset)
|
||||
{
|
||||
// Set machine region
|
||||
switch (((char *)data)[offset + 0x0f] >> 4)
|
||||
{
|
||||
case 3: // SMS Japan
|
||||
region = SMS_REGION_DOMESTIC;
|
||||
break;
|
||||
case 4: // SMS Export
|
||||
// Force system region to Japan if user locale is Japan and the cart is world/multi-region
|
||||
region = [[self systemRegion] isEqualToString: @"Japan"] ? SMS_REGION_DOMESTIC : SMS_REGION_EXPORT;
|
||||
break;
|
||||
case 5: // GG Japan
|
||||
case 6: // GG Export
|
||||
case 7: // GG International
|
||||
default:
|
||||
region = SMS_REGION_DOMESTIC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
// No header means Japan region
|
||||
region = SMS_REGION_DOMESTIC;
|
||||
|
||||
sms_init(SMS_VIDEO_NTSC, region, 0); // 1 = VDP borders
|
||||
sms_mem_load_rom(path.fileSystemRepresentation, console);
|
||||
cur_console->frame(0);
|
||||
}
|
||||
|
||||
NSString *extensionlessFilename = [[path lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
NSString *batterySavesDirectory = [self batterySavesDirectoryPath];
|
||||
|
||||
if([batterySavesDirectory length] != 0)
|
||||
|
||||
if(cur_console->console_type != CONSOLE_COLECOVISION)
|
||||
{
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:NULL];
|
||||
|
||||
NSString *filePath = [batterySavesDirectory stringByAppendingPathComponent:[extensionlessFilename stringByAppendingPathExtension:@"sav"]];
|
||||
|
||||
if(console != CONSOLE_COLECOVISION)
|
||||
sms_read_cartram_from_file([filePath UTF8String]);
|
||||
NSString *extensionlessFilename = [[romFile lastPathComponent] stringByDeletingPathExtension];
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:[self batterySavesDirectoryPath]];
|
||||
[[NSFileManager defaultManager] createDirectoryAtURL:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
NSURL *saveFile = [batterySavesDirectory URLByAppendingPathComponent:[extensionlessFilename stringByAppendingPathExtension:@"sav"]];
|
||||
|
||||
if([saveFile checkResourceIsReachableAndReturnError:nil] && sms_read_cartram_from_file(saveFile.path.fileSystemRepresentation) == 0)
|
||||
NSLog(@"CrabEmu: Loaded sram");
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)executeFrame
|
||||
{
|
||||
[bufLock lock];
|
||||
cur_console->frame(0);
|
||||
[bufLock unlock];
|
||||
}
|
||||
|
||||
- (void)resetEmulation
|
||||
{
|
||||
if(sms_console == CONSOLE_COLECOVISION)
|
||||
coleco_soft_reset();
|
||||
else
|
||||
sms_soft_reset();
|
||||
cur_console->soft_reset();
|
||||
}
|
||||
|
||||
- (void)stopEmulation
|
||||
{
|
||||
NSString *path = romName;
|
||||
NSString *extensionlessFilename = [[path lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
NSString *batterySavesDirectory = [self batterySavesDirectoryPath];
|
||||
|
||||
if([batterySavesDirectory length] != 0)
|
||||
if(cur_console->console_type != CONSOLE_COLECOVISION)
|
||||
{
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:batterySavesDirectory withIntermediateDirectories:YES attributes:nil error:NULL];
|
||||
|
||||
NSLog(@"Trying to save SRAM");
|
||||
|
||||
NSString *filePath = [batterySavesDirectory stringByAppendingPathComponent:[extensionlessFilename stringByAppendingPathExtension:@"sav"]];
|
||||
|
||||
if(sms_console != CONSOLE_COLECOVISION)
|
||||
sms_write_cartram_to_file([filePath UTF8String]);
|
||||
NSString *extensionlessFilename = [[romFile lastPathComponent] stringByDeletingPathExtension];
|
||||
NSURL *batterySavesDirectory = [NSURL fileURLWithPath:[self batterySavesDirectoryPath]];
|
||||
NSURL *saveFile = [batterySavesDirectory URLByAppendingPathComponent:[extensionlessFilename stringByAppendingPathExtension:@"sav"]];
|
||||
|
||||
cur_console->save_sram(saveFile.path.fileSystemRepresentation);
|
||||
}
|
||||
|
||||
|
||||
[super stopEmulation];
|
||||
}
|
||||
|
||||
- (IBAction)pauseEmulation:(id)sender
|
||||
- (NSTimeInterval)frameInterval
|
||||
{
|
||||
[bufLock lock];
|
||||
sms_z80_nmi();
|
||||
[bufLock unlock];
|
||||
return 60;
|
||||
}
|
||||
|
||||
# pragma mark - Video
|
||||
|
||||
- (OEIntSize)bufferSize
|
||||
{
|
||||
return OEIntSizeMake(sms_console == CONSOLE_GG ? 160 : 256, sms_console == CONSOLE_GG ? 144 : smsvdp.lines);
|
||||
uint32_t f_x, f_y;
|
||||
cur_console->frame_size(&f_x, &f_y);
|
||||
return OEIntSizeMake(f_x, f_y);
|
||||
}
|
||||
|
||||
- (OEIntRect)screenRect
|
||||
{
|
||||
uint32_t a_x, a_y, a_w, a_h;
|
||||
cur_console->active_size(&a_x, &a_y, &a_w, &a_h);
|
||||
return OEIntRectMake(a_x, a_y, a_w, a_h);
|
||||
}
|
||||
|
||||
- (OEIntSize)aspectSize
|
||||
{
|
||||
return OEIntSizeMake(sms_console == CONSOLE_GG ? 160 : 256, sms_console == CONSOLE_GG ? 144 : smsvdp.lines);
|
||||
return OEIntSizeMake(cur_console->console_type == CONSOLE_GG ? 160 : 256 * (8.0/7.0), cur_console->console_type == CONSOLE_GG ? 144 : 192);
|
||||
}
|
||||
|
||||
- (const void *)videoBuffer
|
||||
- (const void *)getVideoBufferWithHint:(void *)hint
|
||||
{
|
||||
if (sms_console != CONSOLE_GG)
|
||||
return smsvdp.framebuffer;
|
||||
else
|
||||
for (int i = 0; i < 144; i++)
|
||||
//jump 24 lines, skip 48 pixels and capture for each line of the buffer 160 pixels
|
||||
// sizeof(unsigned char) is always equal to 1 by definition
|
||||
memcpy(tempBuffer + i * 160 * 4, smsvdp.framebuffer + 24 * 256 * 1 + 48 * 1 + i * 256 * 1, 160 * 4);
|
||||
return tempBuffer;
|
||||
if (!hint) {
|
||||
return cur_console->framebuffer();
|
||||
}
|
||||
return smsvdp.framebuffer = (uint32*)hint;
|
||||
}
|
||||
|
||||
- (GLenum)pixelFormat
|
||||
@@ -211,10 +227,7 @@ static OERingBuffer *ringBuffer;
|
||||
return GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
|
||||
- (GLenum)internalPixelFormat
|
||||
{
|
||||
return GL_RGB8;
|
||||
}
|
||||
# pragma mark - Audio
|
||||
|
||||
- (double)audioSampleRate
|
||||
{
|
||||
@@ -226,20 +239,79 @@ static OERingBuffer *ringBuffer;
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (BOOL)saveStateToFileAtPath:(NSString *)fileName
|
||||
# pragma mark - Save States
|
||||
|
||||
- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
|
||||
{
|
||||
if(sms_console == CONSOLE_COLECOVISION)
|
||||
return coleco_save_state([fileName fileSystemRepresentation]) == 0;
|
||||
else
|
||||
return sms_save_state([fileName fileSystemRepresentation]) == 0;
|
||||
block(cur_console->save_state([fileName fileSystemRepresentation]) == 0, nil);
|
||||
}
|
||||
|
||||
- (BOOL)loadStateFromFileAtPath:(NSString *)fileName
|
||||
- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
|
||||
{
|
||||
if(sms_console == CONSOLE_COLECOVISION)
|
||||
return coleco_load_state([fileName fileSystemRepresentation]) == 0;
|
||||
if([[self systemIdentifier] isEqualToString:@"openemu.system.sg1000"])
|
||||
{
|
||||
cur_console->load_state([fileName fileSystemRepresentation]);
|
||||
block(YES, nil);
|
||||
}
|
||||
else
|
||||
return sms_load_state([fileName fileSystemRepresentation]) == 0;
|
||||
block(cur_console->load_state([fileName fileSystemRepresentation]) == 0, nil);
|
||||
}
|
||||
|
||||
- (NSData *)serializeStateWithError:(NSError **)outError
|
||||
{
|
||||
void *bytes;
|
||||
size_t length;
|
||||
FILE *fp = open_memstream((char **)&bytes, &length);
|
||||
|
||||
int status;
|
||||
if(cur_console->console_type == CONSOLE_COLECOVISION)
|
||||
status = coleco_write_state(fp);
|
||||
else
|
||||
status = sms_write_state(fp);
|
||||
|
||||
if(status == 0) {
|
||||
fclose(fp);
|
||||
return [NSData dataWithBytesNoCopy:bytes length:length];
|
||||
}
|
||||
|
||||
if(outError) {
|
||||
*outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotSaveStateError userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"Save state data could not be written",
|
||||
NSLocalizedRecoverySuggestionErrorKey : @"The emulator could not write the state data."
|
||||
}];
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(bytes);
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
|
||||
{
|
||||
const void *bytes = [state bytes];
|
||||
size_t length = [state length];
|
||||
|
||||
FILE *fp = fmemopen((void *)bytes, length, "rb");
|
||||
|
||||
int status;
|
||||
if(cur_console->console_type == CONSOLE_COLECOVISION)
|
||||
status = coleco_read_state(fp);
|
||||
else
|
||||
status = sms_read_state(fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if(status == 0)
|
||||
return YES;
|
||||
|
||||
if(outError) {
|
||||
*outError = [NSError errorWithDomain:OEGameCoreErrorDomain code:OEGameCoreCouldNotLoadStateError userInfo:@{
|
||||
NSLocalizedDescriptionKey : @"The save state data could not be read",
|
||||
NSLocalizedRecoverySuggestionErrorKey : @"Could not load data from the save state"
|
||||
}];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -259,12 +331,12 @@ int sound_init(int channels, int region)
|
||||
|
||||
void sound_shutdown(void)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void sound_reset_buffer(void)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void gui_set_viewport(int w, int h)
|
||||
@@ -282,124 +354,44 @@ void gui_set_title(const char *str)
|
||||
//NSLog(@"set_title%s", str);
|
||||
}
|
||||
|
||||
- (int)crabButtonForButton:(OESMSButton)button player:(NSUInteger)player;
|
||||
void gui_set_console(console_t *c)
|
||||
{
|
||||
int btn = -1;
|
||||
switch(button)
|
||||
{
|
||||
case OESMSButtonUp : btn = SMS_UP; break;
|
||||
case OESMSButtonDown : btn = SMS_DOWN; break;
|
||||
case OESMSButtonLeft : btn = SMS_LEFT; break;
|
||||
case OESMSButtonRight : btn = SMS_RIGHT; break;
|
||||
case OESMSButtonA : btn = SMS_BUTTON_1; break;
|
||||
case OESMSButtonB : btn = SMS_BUTTON_2; break;
|
||||
default : break;
|
||||
}
|
||||
|
||||
return btn;
|
||||
cur_console = c;
|
||||
}
|
||||
|
||||
- (int)crabButtonForButton:(OEGGButton)button;
|
||||
{
|
||||
int btn = -1;
|
||||
switch(button)
|
||||
{
|
||||
case OEGGButtonUp: btn = SMS_UP; break;
|
||||
case OEGGButtonDown: btn = SMS_DOWN; break;
|
||||
case OEGGButtonLeft: btn = SMS_LEFT; break;
|
||||
case OEGGButtonRight: btn = SMS_RIGHT; break;
|
||||
case OEGGButtonA: btn = SMS_BUTTON_1; break;
|
||||
case OEGGButtonB: btn = SMS_BUTTON_2; break;
|
||||
case OEGGButtonStart: btn = GAMEGEAR_START; break;
|
||||
default : break;
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
# pragma mark - Input
|
||||
|
||||
- (int)crabButtonForSG1000Button:(OESG1000Button)button;
|
||||
{
|
||||
int btn = -1;
|
||||
switch(button)
|
||||
{
|
||||
case OESG1000ButtonUp: btn = SMS_UP; break;
|
||||
case OESG1000ButtonDown: btn = SMS_DOWN; break;
|
||||
case OESG1000ButtonLeft: btn = SMS_LEFT; break;
|
||||
case OESG1000ButtonRight: btn = SMS_RIGHT; break;
|
||||
case OESG1000Button1: btn = SMS_BUTTON_1; break;
|
||||
case OESG1000Button2: btn = SMS_BUTTON_2; break;
|
||||
default : break;
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
- (int)crabButtonForColecoVisionButton:(OEColecoVisionButton)button player:(NSUInteger)player;
|
||||
{
|
||||
int btn = -1;
|
||||
switch(button)
|
||||
{
|
||||
case OEColecoVisionButtonUp : btn = COLECOVISION_UP; break;
|
||||
case OEColecoVisionButtonDown : btn = COLECOVISION_DOWN; break;
|
||||
case OEColecoVisionButtonLeft : btn = COLECOVISION_LEFT; break;
|
||||
case OEColecoVisionButtonRight : btn = COLECOVISION_RIGHT; break;
|
||||
case OEColecoVisionButtonLeftAction : btn = COLECOVISION_L_ACTION; break;
|
||||
case OEColecoVisionButtonRightAction : btn = COLECOVISION_R_ACTION; break;
|
||||
case OEColecoVisionButton1 : btn = COLECOVISION_1; break;
|
||||
case OEColecoVisionButton2 : btn = COLECOVISION_2; break;
|
||||
case OEColecoVisionButton3 : btn = COLECOVISION_3; break;
|
||||
case OEColecoVisionButton4 : btn = COLECOVISION_4; break;
|
||||
case OEColecoVisionButton5 : btn = COLECOVISION_5; break;
|
||||
case OEColecoVisionButton6 : btn = COLECOVISION_6; break;
|
||||
case OEColecoVisionButton7 : btn = COLECOVISION_7; break;
|
||||
case OEColecoVisionButton8 : btn = COLECOVISION_8; break;
|
||||
case OEColecoVisionButton9 : btn = COLECOVISION_9; break;
|
||||
case OEColecoVisionButton0 : btn = COLECOVISION_0; break;
|
||||
case OEColecoVisionButtonAsterisk : btn = COLECOVISION_STAR; break;
|
||||
case OEColecoVisionButtonPound : btn = COLECOVISION_POUND; break;
|
||||
default : break;
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
const int MasterSystemMap[] = {SMS_UP, SMS_DOWN, SMS_LEFT, SMS_RIGHT, SMS_BUTTON_1, SMS_BUTTON_2, GAMEGEAR_START};
|
||||
const int ColecoVisionMap[] = {COLECOVISION_UP, COLECOVISION_DOWN, COLECOVISION_LEFT, COLECOVISION_RIGHT, COLECOVISION_L_ACTION, COLECOVISION_R_ACTION, COLECOVISION_1, COLECOVISION_2, COLECOVISION_3, COLECOVISION_4, COLECOVISION_5, COLECOVISION_6, COLECOVISION_7, COLECOVISION_8, COLECOVISION_9, COLECOVISION_0, COLECOVISION_STAR, COLECOVISION_POUND};
|
||||
|
||||
- (oneway void)didPushGGButton:(OEGGButton)button;
|
||||
{
|
||||
int btn = [self crabButtonForButton:button];
|
||||
if(btn > -1) sms_button_pressed(1, btn);
|
||||
sms_button_pressed(1, MasterSystemMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseGGButton:(OEGGButton)button;
|
||||
{
|
||||
int btn = [self crabButtonForButton:button];
|
||||
if(btn > -1) sms_button_released(1, btn);
|
||||
sms_button_released(1, MasterSystemMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didPushSMSButton:(OESMSButton)button forPlayer:(NSUInteger)player;
|
||||
{
|
||||
int btn = [self crabButtonForButton:button player:player];
|
||||
|
||||
if(btn > -1) sms_button_pressed(player, btn);
|
||||
sms_button_pressed((int)player, MasterSystemMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseSMSButton:(OESMSButton)button forPlayer:(NSUInteger)player;
|
||||
{
|
||||
int btn = [self crabButtonForButton:button player:player];
|
||||
|
||||
if(btn > -1) sms_button_released(player, btn);
|
||||
sms_button_released((int)player, MasterSystemMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didPushSMSStartButton;
|
||||
{
|
||||
if(sms_console != CONSOLE_GG)
|
||||
[self pauseEmulation:self];
|
||||
else
|
||||
sms_button_pressed(1, GAMEGEAR_START);
|
||||
sms_button_pressed(1, GAMEGEAR_START);
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseSMSStartButton;
|
||||
{
|
||||
|
||||
sms_button_released(1, GAMEGEAR_START);
|
||||
}
|
||||
|
||||
- (oneway void)didPushSMSResetButton;
|
||||
@@ -412,68 +404,81 @@ void gui_set_title(const char *str)
|
||||
sms_button_released(1, SMS_CONSOLE_RESET);
|
||||
}
|
||||
|
||||
- (oneway void)didPushSG1000Button:(OESG1000Button)button;
|
||||
- (oneway void)didPushSG1000Button:(OESG1000Button)button forPlayer:(NSUInteger)player
|
||||
{
|
||||
int btn = [self crabButtonForSG1000Button:button];
|
||||
if(btn > -1) sms_button_pressed(1, btn);
|
||||
//console pause, sms_z80_nmi()
|
||||
sms_button_pressed((int)player, MasterSystemMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseSG1000Button:(OESG1000Button)button;
|
||||
- (oneway void)didReleaseSG1000Button:(OESG1000Button)button forPlayer:(NSUInteger)player
|
||||
{
|
||||
int btn = [self crabButtonForSG1000Button:button];
|
||||
if(btn > -1) sms_button_released(1, btn);
|
||||
sms_button_released((int)player, MasterSystemMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didPushColecoVisionButton:(OEColecoVisionButton)button forPlayer:(NSUInteger)player;
|
||||
{
|
||||
int btn = [self crabButtonForColecoVisionButton:button player:player];
|
||||
|
||||
if(btn > -1) coleco_button_pressed(player, btn);
|
||||
coleco_button_pressed((int)player, ColecoVisionMap[button]);
|
||||
}
|
||||
|
||||
- (oneway void)didReleaseColecoVisionButton:(OEColecoVisionButton)button forPlayer:(NSUInteger)player;
|
||||
{
|
||||
int btn = [self crabButtonForColecoVisionButton:button player:player];
|
||||
|
||||
if(btn > -1) coleco_button_released(player, btn);
|
||||
coleco_button_released((int)player, ColecoVisionMap[button]);
|
||||
}
|
||||
|
||||
#pragma mark - Cheats
|
||||
|
||||
- (void)setCheat:(NSString *)code setType:(NSString *)type setEnabled:(BOOL)enabled
|
||||
{
|
||||
// Sanitize
|
||||
code = [code stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
|
||||
// Remove any spaces
|
||||
code = [code stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
|
||||
|
||||
// Remove address-value separator
|
||||
code = [code stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
||||
|
||||
|
||||
if (enabled)
|
||||
[cheatList setValue:@YES forKey:code];
|
||||
else
|
||||
[cheatList removeObjectForKey:code];
|
||||
|
||||
sms_cheat_reset();
|
||||
|
||||
NSArray *multipleCodes = [[NSArray alloc] init];
|
||||
multipleCodes = [code componentsSeparatedByString:@"+"];
|
||||
|
||||
for (NSString *singleCode in multipleCodes)
|
||||
|
||||
// Apply enabled cheats found in dictionary
|
||||
for (id key in cheatList)
|
||||
{
|
||||
if ([singleCode length] == 8)
|
||||
if ([[cheatList valueForKey:key] isEqual:@YES])
|
||||
{
|
||||
// Action Replay GG/SMS format: XXXX-YYYY
|
||||
NSString *address = [singleCode substringWithRange:NSMakeRange(0, 4)];
|
||||
NSString *value = [singleCode substringWithRange:NSMakeRange(4, 4)];
|
||||
|
||||
// Convert AR hex to int
|
||||
uint32_t outAddress, outValue;
|
||||
NSScanner* scanAddress = [NSScanner scannerWithString:address];
|
||||
NSScanner* scanValue = [NSScanner scannerWithString:value];
|
||||
[scanAddress scanHexInt:&outAddress];
|
||||
[scanValue scanHexInt:&outValue];
|
||||
|
||||
sms_cheat_t *arCode = (sms_cheat_t *)malloc(sizeof(sms_cheat_t));
|
||||
arCode->ar_code = (outAddress << 16) | outValue;
|
||||
strcpy(arCode->desc, [singleCode UTF8String]);
|
||||
arCode->enabled = 1;
|
||||
|
||||
sms_cheat_enable();
|
||||
sms_cheat_add(arCode);
|
||||
// Handle multi-line cheats
|
||||
multipleCodes = [key componentsSeparatedByString:@"+"];
|
||||
for (NSString *singleCode in multipleCodes)
|
||||
{
|
||||
if ([singleCode length] == 8)
|
||||
{
|
||||
// Action Replay GG/SMS format: XXXX-YYYY
|
||||
NSString *address = [singleCode substringWithRange:NSMakeRange(0, 4)];
|
||||
NSString *value = [singleCode substringWithRange:NSMakeRange(4, 4)];
|
||||
|
||||
// Convert AR hex to int
|
||||
uint32_t outAddress, outValue;
|
||||
NSScanner *scanAddress = [NSScanner scannerWithString:address];
|
||||
NSScanner *scanValue = [NSScanner scannerWithString:value];
|
||||
[scanAddress scanHexInt:&outAddress];
|
||||
[scanValue scanHexInt:&outValue];
|
||||
|
||||
sms_cheat_t *arCode = (sms_cheat_t *)malloc(sizeof(sms_cheat_t));
|
||||
memset(arCode, 0, sizeof(sms_cheat_t));
|
||||
arCode->ar_code = (outAddress << 16) | outValue;
|
||||
strcpy(arCode->desc, [singleCode UTF8String]);
|
||||
arCode->enabled = 1;
|
||||
|
||||
sms_cheat_add(arCode);
|
||||
sms_cheat_enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
build_now: 'YES'
|
||||
create_core_path: 'YES'
|
||||
download_base_url: http://github.com/downloads/openemu/openemu/
|
||||
release_notes_base_url: http://openemu.org/category/releasenotes/crabemu/
|
||||
appcast_basefolder: '/Users/jweinberg/Desktop/Appcast'
|
||||
appcast_xml_name: 'crabemu_appcast.xml'
|
||||
keychain_privkey_name: 'Sparkle Private Key'
|
||||
css_file_name: 'rnotes.css'
|
||||
bundle_extension: 'oecoreplugin'
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2014, 2015, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
/* All of the consoles that are supported currently. These may eventually go
|
||||
away if there is ever a push to abstract this even more... */
|
||||
#define CONSOLE_NULL 0 /* No console */
|
||||
#define CONSOLE_SMS 1 /* Sega Master System or Mark III */
|
||||
#define CONSOLE_GG 2 /* Sega Game Gear */
|
||||
#define CONSOLE_SG1000 3 /* Sega SG-1000 */
|
||||
#define CONSOLE_SC3000 4 /* Sega SC-3000 */
|
||||
#define CONSOLE_COLECOVISION 5 /* ColecoVision */
|
||||
#define CONSOLE_NES 6 /* Nintendo Entertainment System */
|
||||
/* 7 left blank for now... */
|
||||
#define CONSOLE_CHIP8 8 /* Chip-8 "Console" */
|
||||
|
||||
/* Region codes. */
|
||||
#define REGION_NONE 0x00
|
||||
#define REGION_JAPAN 0x01
|
||||
#define REGION_US 0x02
|
||||
#define REGION_EUROPE 0x04
|
||||
|
||||
/* This structure is meant to provide an abstraction of a console, such that
|
||||
there can be less console-specific code up at the GUI level. Rignt now, this
|
||||
doesn't take care of everything, but that might eventually change... Each
|
||||
console should create and initialize a "subclass" of one of these (provide
|
||||
a structure such that a console_t is the first member thereof). */
|
||||
typedef struct crabemu_console {
|
||||
int console_family;
|
||||
int console_type;
|
||||
int initialized;
|
||||
|
||||
/* Initialization-related stuff */
|
||||
int (*shutdown)(void);
|
||||
int (*reset)(void);
|
||||
int (*soft_reset)(void);
|
||||
|
||||
/* Run one frame of output. */
|
||||
void (*frame)(int skip);
|
||||
|
||||
/* Save state management */
|
||||
int (*load_state)(const char *fn);
|
||||
int (*save_state)(const char *fn);
|
||||
int (*save_sram)(const char *fn);
|
||||
|
||||
/* Input */
|
||||
void (*button_pressed)(int player, int button);
|
||||
void (*button_released)(int player, int button);
|
||||
|
||||
/* Video-related functionaity */
|
||||
void *(*framebuffer)(void);
|
||||
void (*frame_size)(uint32_t *x, uint32_t *y);
|
||||
void (*active_size)(uint32_t *x, uint32_t *y, uint32_t *w, uint32_t *h);
|
||||
|
||||
/* Cheats */
|
||||
int (*save_cheats)(const char *fn);
|
||||
|
||||
/* Debugging related functions. */
|
||||
void (*scanline)(void);
|
||||
void (*step)(void);
|
||||
void (*finish_frame)(void);
|
||||
void (*finish_line)(void);
|
||||
int (*current_scanline)(void);
|
||||
int (*current_cycles)(void);
|
||||
|
||||
void (*set_control)(int player, int control);
|
||||
} console_t;
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !CONSOLE_H */
|
||||
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
#include <zlib/zlib.h>
|
||||
#include <bzlib/bzlib.h>
|
||||
#else
|
||||
#ifndef NO_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#ifndef NO_BZ2
|
||||
#include <bzlib.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "rom.h"
|
||||
#include "chip8.h"
|
||||
#include "chip8cpu.h"
|
||||
#include "sound.h"
|
||||
|
||||
#ifndef NO_ZLIB
|
||||
#include "unzip.h"
|
||||
#endif
|
||||
|
||||
/* Forward declaration... */
|
||||
chip8_t chip8_cons;
|
||||
|
||||
static uint8 *cart_rom;
|
||||
static uint32 cart_len;
|
||||
static uint8 memory[4096];
|
||||
static uint32 rom_crc, rom_adler;
|
||||
static pixel_t fb[64 * 32];
|
||||
static Chip8CPU_t cpu;
|
||||
static uint8 buttons[16];
|
||||
static uint8 dt, st;
|
||||
|
||||
static const uint8 font[80] = {
|
||||
0xF0, 0x90, 0x90, 0x90, 0xF0,
|
||||
0x20, 0x60, 0x20, 0x20, 0x70,
|
||||
0xF0, 0x10, 0xF0, 0x80, 0xF0,
|
||||
0xF0, 0x10, 0xF0, 0x10, 0xF0,
|
||||
0x90, 0x90, 0xF0, 0x10, 0x10,
|
||||
0xF0, 0x80, 0xF0, 0x10, 0xF0,
|
||||
0xF0, 0x80, 0xF0, 0x90, 0xF0,
|
||||
0xF0, 0x10, 0x20, 0x40, 0x40,
|
||||
0xF0, 0x90, 0xF0, 0x90, 0xF0,
|
||||
0xF0, 0x90, 0xF0, 0x10, 0xF0,
|
||||
0xF0, 0x90, 0xF0, 0x90, 0x90,
|
||||
0xE0, 0x90, 0xE0, 0x90, 0xE0,
|
||||
0xF0, 0x80, 0x80, 0x80, 0xF0,
|
||||
0xE0, 0x90, 0x90, 0x90, 0xE0,
|
||||
0xF0, 0x80, 0xF0, 0x80, 0xF0,
|
||||
0xF0, 0x80, 0xF0, 0x80, 0x80
|
||||
};
|
||||
|
||||
#define PIXEL_SET ((pixel_t)(0xFFFFFFFF))
|
||||
|
||||
static uint8 chip8_mem_read(void *cpu, uint16 addr) {
|
||||
(void)cpu;
|
||||
return memory[addr & 0xFFF];
|
||||
}
|
||||
|
||||
static void chip8_mem_write(void *cpu, uint16 addr, uint8 val) {
|
||||
(void)cpu;
|
||||
memory[addr & 0xFFF] = val;
|
||||
}
|
||||
|
||||
static uint16 chip8_mem_read16(void *cpu, uint16 addr) {
|
||||
(void)cpu;
|
||||
return (memory[addr & 0xFFF] << 8) | memory[(addr + 1) & 0xFFF];
|
||||
}
|
||||
|
||||
static uint8 chip8_reg_read(void *cpu, uint8 reg) {
|
||||
(void)cpu;
|
||||
|
||||
if(reg < CHIP8_REG_DT)
|
||||
return buttons[reg];
|
||||
else if(reg == CHIP8_REG_DT)
|
||||
return dt;
|
||||
else if(reg == CHIP8_REG_ST)
|
||||
return st;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chip8_reg_write(void *cpu, uint8 reg, uint8 val) {
|
||||
(void)cpu;
|
||||
|
||||
if(reg == CHIP8_REG_DT)
|
||||
dt = val;
|
||||
else if(reg == CHIP8_REG_ST)
|
||||
st = val;
|
||||
}
|
||||
|
||||
static void chip8_mem_clear_fb(void *cpu) {
|
||||
(void)cpu;
|
||||
memset(fb, 0, 64 * 32 * sizeof(pixel_t));
|
||||
}
|
||||
|
||||
static int chip8_mem_draw_spr(void *cpu, uint16 addr, uint8 x, uint8 y,
|
||||
uint8 height) {
|
||||
uint8 i, j, bp;
|
||||
int rv = 0;
|
||||
int ptr;
|
||||
|
||||
y &= 0x1F;
|
||||
x &= 0x3F;
|
||||
ptr = (y << 6) + x;
|
||||
|
||||
if(height + y > 32)
|
||||
height = 32 - y;
|
||||
|
||||
for(i = 0; i < height; ++i, ptr += 64) {
|
||||
bp = chip8_mem_read(cpu, addr + i);
|
||||
|
||||
for(j = 0; j < 8 && x + j < 64; ++j) {
|
||||
if((bp & (0x80 >> j))) {
|
||||
fb[ptr + j] ^= PIXEL_SET;
|
||||
if(!fb[ptr + j])
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void finalize_load(const char *fn) {
|
||||
char *name;
|
||||
|
||||
/* Calculate the checksums of the game */
|
||||
rom_adler = rom_adler32(cart_rom, cart_len);
|
||||
rom_crc = rom_crc32(cart_rom, cart_len);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Checksums: Adler-32: 0x%08" PRIX32 " CRC32: 0x%08" PRIX32 "\n",
|
||||
(uint32_t)rom_adler, (uint32_t)rom_crc);
|
||||
#endif
|
||||
|
||||
/* Copy the rom and font into the memory space. */
|
||||
memcpy(memory + 0x200, cart_rom, cart_len);
|
||||
memcpy(memory, font, 80);
|
||||
chip8_mem_clear_fb(NULL);
|
||||
|
||||
name = strrchr(fn, '/');
|
||||
|
||||
if(name != NULL) {
|
||||
/* we have a filename, but the first char is /, so skip that */
|
||||
gui_set_title(name + 1);
|
||||
}
|
||||
else {
|
||||
gui_set_title("CrabEmu");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NO_ZLIB
|
||||
static int load_gz_rom(const char *fn) {
|
||||
int len;
|
||||
gzFile fp;
|
||||
void *tmp;
|
||||
|
||||
/* Open up the file in question. */
|
||||
if(!(fp = gzopen(fn, "rb"))) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Could not open ROM: %s!\n", fn);
|
||||
#endif
|
||||
return ROM_LOAD_E_OPEN;
|
||||
}
|
||||
|
||||
/* Read one chunk of the file at a time, until we have no more left. */
|
||||
cart_rom = (uint8 *)malloc(0x100);
|
||||
|
||||
if(!cart_rom) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't allocate space!\n");
|
||||
#endif
|
||||
gzclose(fp);
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
cart_len = 0;
|
||||
len = gzread(fp, cart_rom, 0x100);
|
||||
|
||||
while(len != 0 && len != -1) {
|
||||
cart_len += len;
|
||||
tmp = realloc(cart_rom, cart_len + 0x100);
|
||||
|
||||
if(!tmp) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't allocate space!\n");
|
||||
#endif
|
||||
gzclose(fp);
|
||||
free(cart_rom);
|
||||
cart_rom = NULL;
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
cart_rom = (uint8 *)tmp;
|
||||
memset(cart_rom + cart_len, 0xFF, 0x100);
|
||||
len = gzread(fp, cart_rom + cart_len, 0x100);
|
||||
}
|
||||
|
||||
gzclose(fp);
|
||||
finalize_load(fn);
|
||||
|
||||
return ROM_LOAD_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_BZ2
|
||||
static int load_bz2_rom(const char *fn) {
|
||||
int len;
|
||||
BZFILE *fp;
|
||||
void *tmp;
|
||||
|
||||
/* Open up the file in question. */
|
||||
if(!(fp = BZ2_bzopen(fn, "rb"))) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Could not open ROM: %s!\n", fn);
|
||||
#endif
|
||||
return ROM_LOAD_E_OPEN;
|
||||
}
|
||||
|
||||
/* Read one chunk of the file at a time, until we have no more left. */
|
||||
if(!(cart_rom = (uint8 *)malloc(0x100))) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't allocate space!\n");
|
||||
#endif
|
||||
BZ2_bzclose(fp);
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
cart_len = 0;
|
||||
len = BZ2_bzread(fp, cart_rom, 0x100);
|
||||
|
||||
while(len != 0 && len != -1) {
|
||||
cart_len += len;
|
||||
tmp = realloc(cart_rom, cart_len + 0x100);
|
||||
|
||||
if(!tmp) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't allocate space!\n");
|
||||
#endif
|
||||
BZ2_bzclose(fp);
|
||||
free(cart_rom);
|
||||
cart_rom = NULL;
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
cart_rom = (uint8 *)tmp;
|
||||
memset(cart_rom + cart_len, 0xFF, 0x100);
|
||||
len = BZ2_bzread(fp, cart_rom + cart_len, 0x100);
|
||||
}
|
||||
|
||||
BZ2_bzclose(fp);
|
||||
finalize_load(fn);
|
||||
|
||||
return ROM_LOAD_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_ZLIB
|
||||
static int load_zip_rom(const char *fn) {
|
||||
int found = 0;
|
||||
unzFile fp;
|
||||
unz_file_info inf;
|
||||
char fn2[4096];
|
||||
char *ext;
|
||||
int size;
|
||||
|
||||
/* Open up the file in question. */
|
||||
fp = unzOpen(fn);
|
||||
|
||||
if(!fp) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Could not open ROM: %s!\n", fn);
|
||||
#endif
|
||||
return ROM_LOAD_E_OPEN;
|
||||
}
|
||||
|
||||
if(unzGoToFirstFile(fp) != UNZ_OK) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't find file in zip!\n");
|
||||
#endif
|
||||
unzClose(fp);
|
||||
return ROM_LOAD_E_CORRUPT;
|
||||
}
|
||||
|
||||
/* Keep looking at files until we find a rom. */
|
||||
while(!found) {
|
||||
if(unzGetCurrentFileInfo(fp, &inf, fn2, 4096, NULL, 0, NULL,
|
||||
0) != UNZ_OK) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Error parsing Zip file!\n");
|
||||
#endif
|
||||
unzClose(fp);
|
||||
return ROM_LOAD_E_CORRUPT;
|
||||
}
|
||||
|
||||
/* Check if this file looks like a rom. */
|
||||
ext = strrchr(fn2, '.');
|
||||
|
||||
if(ext) {
|
||||
/* XXXX */
|
||||
if(!strcmp(ext, ".ch8") || !strcmp(ext, ".c8")) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we haven't found a rom yet, move to the next file. */
|
||||
if(!found) {
|
||||
if(unzGoToNextFile(fp) != UNZ_OK) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: End of zip file!\n");
|
||||
#endif
|
||||
unzClose(fp);
|
||||
return ROM_LOAD_E_NO_ROM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the size isn't a nice even amount, round it. */
|
||||
size = inf.uncompressed_size;
|
||||
|
||||
if(!(cart_rom = (uint8 *)malloc(size))) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Could not load ROM: %s!\n", fn);
|
||||
fprintf(stderr, "Reason: %s\n", strerror(errno));
|
||||
#endif
|
||||
unzClose(fp);
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
/* Attempt to open the file we think is a rom. */
|
||||
if(unzOpenCurrentFile(fp) != UNZ_OK) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't open rom from zip!\n");
|
||||
#endif
|
||||
unzClose(fp);
|
||||
free(cart_rom);
|
||||
cart_rom = NULL;
|
||||
return ROM_LOAD_E_CORRUPT;
|
||||
}
|
||||
|
||||
/* Attempt to read the file. */
|
||||
if(unzReadCurrentFile(fp, cart_rom, size) < 0) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Couldn't read rom from zip!\n");
|
||||
#endif
|
||||
unzCloseCurrentFile(fp);
|
||||
unzClose(fp);
|
||||
free(cart_rom);
|
||||
cart_rom = NULL;
|
||||
return ROM_LOAD_E_CORRUPT;
|
||||
}
|
||||
|
||||
/* We're done with the file, close everything. */
|
||||
unzCloseCurrentFile(fp);
|
||||
unzClose(fp);
|
||||
|
||||
cart_len = size;
|
||||
finalize_load(fn);
|
||||
|
||||
return ROM_LOAD_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
int chip8_mem_load_rom(const char *fn) {
|
||||
FILE *fp;
|
||||
int size;
|
||||
char *ext;
|
||||
|
||||
/* If its a compressed format, go onto the handler for it... */
|
||||
ext = strrchr(fn, '.');
|
||||
|
||||
if(ext) {
|
||||
#ifndef NO_ZLIB
|
||||
if(!strcmp(ext, ".gz")) {
|
||||
return load_gz_rom(fn);
|
||||
}
|
||||
else if(!strcmp(ext, ".zip")) {
|
||||
return load_zip_rom(fn);
|
||||
}
|
||||
#endif
|
||||
#ifndef NO_BZ2
|
||||
if(!strcmp(ext, ".bz2")) {
|
||||
return load_bz2_rom(fn);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!(fp = fopen(fn, "rb"))) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Could not open ROM: %s!\n", fn);
|
||||
#endif
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
/* Determine the size of the file */
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
if(!(cart_rom = (uint8 *)malloc(size))) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "chip8_mem_load_rom: Could not load ROM: %s!\n", fn);
|
||||
fprintf(stderr, "Reason: %s\n", strerror(errno));
|
||||
#endif
|
||||
return ROM_LOAD_E_ERRNO;
|
||||
}
|
||||
|
||||
fread(cart_rom, size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
cart_len = size;
|
||||
finalize_load(fn);
|
||||
|
||||
return ROM_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int chip8_mem_init(void) {
|
||||
memset(memory, 0, 0x1000);
|
||||
chip8_mem_clear_fb(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chip8_mem_shutdown(void) {
|
||||
if(cart_rom)
|
||||
free(cart_rom);
|
||||
|
||||
cart_rom = NULL;
|
||||
cart_len = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chip8_mem_reset(void) {
|
||||
memset(memory, 0, 0x1000);
|
||||
memcpy(memory + 0x200, cart_rom, cart_len);
|
||||
memcpy(memory, font, 80);
|
||||
chip8_mem_clear_fb(NULL);
|
||||
}
|
||||
|
||||
void chip8_get_checksums(uint32 *crc, uint32 *adler) {
|
||||
*crc = rom_crc;
|
||||
*adler = rom_adler;
|
||||
}
|
||||
|
||||
/* Console interface for CrabEmu's core... */
|
||||
int chip8_init(void) {
|
||||
if(chip8_cons._base.initialized)
|
||||
return -1;
|
||||
|
||||
gui_set_console((console_t *)&chip8_cons);
|
||||
|
||||
Chip8CPU_init(&cpu);
|
||||
Chip8CPU_set_memread(&cpu, &chip8_mem_read);
|
||||
Chip8CPU_set_memread16(&cpu, &chip8_mem_read16);
|
||||
Chip8CPU_set_memwrite(&cpu, &chip8_mem_write);
|
||||
Chip8CPU_set_regread(&cpu, &chip8_reg_read);
|
||||
Chip8CPU_set_regwrite(&cpu, &chip8_reg_write);
|
||||
Chip8CPU_set_clearfb(&cpu, &chip8_mem_clear_fb);
|
||||
Chip8CPU_set_drawspr(&cpu, &chip8_mem_draw_spr);
|
||||
|
||||
chip8_mem_init();
|
||||
|
||||
sound_init(1, VIDEO_NTSC);
|
||||
|
||||
chip8_cons._base.initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chip8_shutdown(void) {
|
||||
if(!chip8_cons._base.initialized)
|
||||
return -1;
|
||||
|
||||
chip8_mem_clear_fb(NULL);
|
||||
Chip8CPU_reset(&cpu);
|
||||
chip8_mem_shutdown();
|
||||
sound_shutdown();
|
||||
|
||||
chip8_cons._base.initialized = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chip8_reset(void) {
|
||||
if(!chip8_cons._base.initialized)
|
||||
return -1;
|
||||
|
||||
sound_reset_buffer();
|
||||
chip8_mem_shutdown();
|
||||
chip8_mem_init();
|
||||
Chip8CPU_reset(&cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chip8_soft_reset(void) {
|
||||
if(!chip8_cons._base.initialized)
|
||||
return 0;
|
||||
|
||||
sound_reset_buffer();
|
||||
chip8_mem_reset();
|
||||
Chip8CPU_reset(&cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chip8_frame(int skip) {
|
||||
int i;
|
||||
|
||||
(void)skip;
|
||||
|
||||
/* XXXX: How many cycles to execute? We punt and do 15. */
|
||||
for(i = 0; i < 15; ++i) {
|
||||
Chip8CPU_execute(&cpu);
|
||||
}
|
||||
|
||||
/* Update the timers, if they're running. */
|
||||
if(dt)
|
||||
--dt;
|
||||
|
||||
if(st) {
|
||||
/* Need to deal with sound still... */
|
||||
--st;
|
||||
}
|
||||
}
|
||||
|
||||
static void chip8_button_pressed(int player, int button) {
|
||||
if(player != 1)
|
||||
return;
|
||||
|
||||
if(button < 0 || button > 15)
|
||||
return;
|
||||
|
||||
buttons[button] = 1;
|
||||
}
|
||||
|
||||
static void chip8_button_released(int player, int button) {
|
||||
if(player != 1)
|
||||
return;
|
||||
|
||||
if(button < 0 || button > 15)
|
||||
return;
|
||||
|
||||
buttons[button] = 0;
|
||||
}
|
||||
|
||||
static void *chip8_framebuffer(void) {
|
||||
return fb;
|
||||
}
|
||||
|
||||
static void chip8_framesize(uint32_t *x, uint32_t *y) {
|
||||
*x = 64;
|
||||
*y = 32;
|
||||
}
|
||||
|
||||
static void chip8_activeframe(uint32_t *x, uint32_t *y, uint32_t *w,
|
||||
uint32_t *h) {
|
||||
*x = *y = 0;
|
||||
*w = 64;
|
||||
*h = 32;
|
||||
}
|
||||
|
||||
/* Console declaration... */
|
||||
chip8_t chip8_cons = {
|
||||
{
|
||||
CONSOLE_CHIP8,
|
||||
CONSOLE_CHIP8,
|
||||
0,
|
||||
&chip8_shutdown,
|
||||
&chip8_reset,
|
||||
&chip8_soft_reset,
|
||||
&chip8_frame,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&chip8_button_pressed,
|
||||
&chip8_button_released,
|
||||
&chip8_framebuffer,
|
||||
&chip8_framesize,
|
||||
&chip8_activeframe,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef CHIP8_H
|
||||
#define CHIP8_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
#include "console.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
extern int chip8_init(void);
|
||||
extern int chip8_mem_load_rom(const char *fn);
|
||||
|
||||
/* Console definition. */
|
||||
typedef struct crabemu_chip8 {
|
||||
console_t _base;
|
||||
} chip8_t;
|
||||
|
||||
extern chip8_t chip8_cons;
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !CHIP8_H */
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "chip8cpu.h"
|
||||
|
||||
static uint8 Chip8CPU_dummy_read(void *cpu, uint16 addr) {
|
||||
(void)cpu;
|
||||
(void)addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16 Chip8CPU_dummy_read16(void *cpu, uint16 addr) {
|
||||
Chip8CPU_t *c = (Chip8CPU_t *)cpu;
|
||||
|
||||
return (c->mread(cpu, addr) << 8) | c->mread(cpu, addr + 1);
|
||||
}
|
||||
|
||||
static void Chip8CPU_dummy_write(void *cpu, uint16 addr, uint8 data) {
|
||||
(void)cpu;
|
||||
(void)addr;
|
||||
(void)data;
|
||||
}
|
||||
|
||||
static uint8 Chip8CPU_dummy_rread(void *cpu, uint8 reg) {
|
||||
(void)cpu;
|
||||
(void)reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Chip8CPU_dummy_rwrite(void *cpu, uint8 reg, uint8 data) {
|
||||
(void)cpu;
|
||||
(void)reg;
|
||||
(void)data;
|
||||
}
|
||||
|
||||
static void Chip8CPU_dummy_clear(void *cpu) {
|
||||
(void)cpu;
|
||||
}
|
||||
|
||||
static int Chip8CPU_dummy_draw(void *cpu, uint16 a, uint8 x, uint8 y, uint8 h) {
|
||||
(void)cpu;
|
||||
(void)a;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_memread(Chip8CPU_t *cpu, uint8 (*mread)(void *, uint16)) {
|
||||
if(mread == NULL)
|
||||
cpu->mread = &Chip8CPU_dummy_read;
|
||||
else
|
||||
cpu->mread = mread;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_memread16(Chip8CPU_t *cpu, uint16 (*mread)(void *, uint16)) {
|
||||
if(mread == NULL)
|
||||
cpu->mread16 = &Chip8CPU_dummy_read16;
|
||||
else
|
||||
cpu->mread16 = mread;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_memwrite(Chip8CPU_t *cpu,
|
||||
void (*mwrite)(void *, uint16, uint8)) {
|
||||
if(mwrite == NULL)
|
||||
cpu->mwrite = &Chip8CPU_dummy_write;
|
||||
else
|
||||
cpu->mwrite = mwrite;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_regread(Chip8CPU_t *cpu, uint8 (*rread)(void *, uint8)) {
|
||||
if(rread == NULL)
|
||||
cpu->rread = &Chip8CPU_dummy_rread;
|
||||
else
|
||||
cpu->rread = rread;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_regwrite(Chip8CPU_t *cpu,
|
||||
void (*rwrite)(void *, uint8, uint8)) {
|
||||
if(rwrite == NULL)
|
||||
cpu->rwrite = &Chip8CPU_dummy_rwrite;
|
||||
else
|
||||
cpu->rwrite = rwrite;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_clearfb(Chip8CPU_t *cpu,
|
||||
void (*clearfb)(void *)) {
|
||||
if(clearfb == NULL)
|
||||
cpu->clear_fb = &Chip8CPU_dummy_clear;
|
||||
else
|
||||
cpu->clear_fb = clearfb;
|
||||
}
|
||||
|
||||
void Chip8CPU_set_drawspr(Chip8CPU_t *cpu,
|
||||
int (*drawspr)(void *, uint16, uint8, uint8, uint8)) {
|
||||
if(drawspr == NULL)
|
||||
cpu->draw_spr = &Chip8CPU_dummy_draw;
|
||||
else
|
||||
cpu->draw_spr = drawspr;
|
||||
}
|
||||
|
||||
void Chip8CPU_init(Chip8CPU_t *cpu) {
|
||||
cpu->mread = &Chip8CPU_dummy_read;
|
||||
cpu->mwrite = &Chip8CPU_dummy_write;
|
||||
cpu->mread16 = &Chip8CPU_dummy_read16;
|
||||
cpu->rread = &Chip8CPU_dummy_rread;
|
||||
cpu->rwrite = &Chip8CPU_dummy_rwrite;
|
||||
cpu->clear_fb = &Chip8CPU_dummy_clear;
|
||||
cpu->draw_spr = &Chip8CPU_dummy_draw;
|
||||
}
|
||||
|
||||
void Chip8CPU_reset(Chip8CPU_t *cpu) {
|
||||
cpu->pc = 0x200;
|
||||
cpu->i = 0;
|
||||
cpu->sp = 0;
|
||||
memset(cpu->stack, 0, sizeof(uint16) * 16);
|
||||
memset(cpu->v, 0, sizeof(uint8) * 16);
|
||||
}
|
||||
|
||||
void Chip8CPU_execute(Chip8CPU_t *cpu) {
|
||||
uint16 inst;
|
||||
|
||||
inst = cpu->mread16(cpu, cpu->pc);
|
||||
cpu->pc += 2;
|
||||
#define INSIDE_CHIP8CPU_EXECUTE
|
||||
#include "chip8cpuops.h"
|
||||
#undef INSIDE_CHIP8CPU_EXECUTE
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef CHIP8CPU_H
|
||||
#define CHIP8CPU_H
|
||||
|
||||
#ifdef IN_CRABEMU
|
||||
#include "CrabEmu.h"
|
||||
#else
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define CLINKAGE extern "C" {
|
||||
#define ENDCLINK }
|
||||
#else
|
||||
#define CLINKAGE
|
||||
#define ENDCLINK
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifndef CRABEMU_TYPEDEFS
|
||||
#define CRABEMU_TYPEDEFS
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
#include <arch/types.h>
|
||||
#else
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
|
||||
typedef int8_t int8;
|
||||
typedef int16_t int16;
|
||||
typedef uint32_t int32;
|
||||
#endif /* _arch_dreamcast */
|
||||
#endif /* CRABEMU_TYPEDEFS */
|
||||
#endif /* IN_CRABEMU */
|
||||
|
||||
CLINKAGE
|
||||
|
||||
typedef struct Chip8CPU_struct Chip8CPU_t;
|
||||
|
||||
struct Chip8CPU_struct {
|
||||
uint16 pc;
|
||||
uint16 i;
|
||||
uint16 stack[16];
|
||||
uint8 v[16];
|
||||
uint8 sp;
|
||||
|
||||
uint8 (*mread)(void *, uint16);
|
||||
void (*mwrite)(void *, uint16, uint8);
|
||||
uint16 (*mread16)(void *, uint16);
|
||||
uint8 (*rread)(void *, uint8);
|
||||
void (*rwrite)(void *, uint8, uint8);
|
||||
|
||||
void (*clear_fb)(void *);
|
||||
int (*draw_spr)(void *, uint16, uint8, uint8, uint8);
|
||||
};
|
||||
|
||||
/* Key statuses are defined as registers 0x00-0x0F */
|
||||
#define CHIP8_REG_DT 0x10
|
||||
#define CHIP8_REG_ST 0x11
|
||||
|
||||
/* Function definitions */
|
||||
void Chip8CPU_init(Chip8CPU_t *cpu);
|
||||
void Chip8CPU_reset(Chip8CPU_t *cpu);
|
||||
|
||||
void Chip8CPU_execute(Chip8CPU_t *cpu);
|
||||
|
||||
void Chip8CPU_set_memread(Chip8CPU_t *cpu,
|
||||
uint8 (*mread)(void *cpu, uint16 addr));
|
||||
void Chip8CPU_set_memread16(Chip8CPU_t *cpu,
|
||||
uint16 (*mread)(void *cpu, uint16 addr));
|
||||
void Chip8CPU_set_memwrite(Chip8CPU_t *cpu,
|
||||
void (*mwrite)(void *cpu, uint16 addr, uint8 data));
|
||||
|
||||
void Chip8CPU_set_regread(Chip8CPU_t *cpu,
|
||||
uint8 (*rread)(void *cpu, uint8 reg));
|
||||
void Chip8CPU_set_regwrite(Chip8CPU_t *cpu,
|
||||
void (*rwrite)(void *cpu, uint8 reg, uint8 data));
|
||||
|
||||
void Chip8CPU_set_clearfb(Chip8CPU_t *cpu, void (*clearfb)(void*));
|
||||
void Chip8CPU_set_drawspr(Chip8CPU_t *cpu,
|
||||
int (*drawspr)(void *, uint16, uint8, uint8, uint8));
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !CHIP8CPU_H */
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef INSIDE_CHIP8CPU_EXECUTE
|
||||
#error This file can only be compiled inside of chip8cpu.c. Do not try to
|
||||
#error include this file in other files.
|
||||
#endif
|
||||
|
||||
uint8 _tmp, _tmp2;
|
||||
int _tmp3;
|
||||
|
||||
switch(inst >> 12) {
|
||||
case 0x0:
|
||||
switch(inst) {
|
||||
case 0xE0: /* CLS */
|
||||
cpu->clear_fb(cpu);
|
||||
goto out;
|
||||
|
||||
case 0xEE: /* RET */
|
||||
--cpu->sp;
|
||||
cpu->pc = cpu->stack[cpu->sp & 0xF];
|
||||
goto out;
|
||||
|
||||
default:
|
||||
/* XXXX */
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x1: /* JP #addr12 */
|
||||
cpu->pc = inst & 0x0FFF;
|
||||
goto out;
|
||||
|
||||
case 0x2: /* CALL #addr12 */
|
||||
cpu->stack[cpu->sp & 0xF] = cpu->pc;
|
||||
++cpu->sp;
|
||||
cpu->pc = inst & 0x0FFF;
|
||||
goto out;
|
||||
|
||||
case 0x3: /* SE Vx, #imm8 */
|
||||
_tmp = cpu->v[(inst >> 8) & 0x0F];
|
||||
_tmp2 = (uint8)inst;
|
||||
|
||||
if(_tmp == _tmp2)
|
||||
cpu->pc += 2;
|
||||
goto out;
|
||||
|
||||
case 0x4: /* SNE VX, #imm8 */
|
||||
_tmp = cpu->v[(inst >> 8) & 0x0F];
|
||||
_tmp2 = (uint8)inst;
|
||||
|
||||
if(_tmp != _tmp2)
|
||||
cpu->pc += 2;
|
||||
goto out;
|
||||
|
||||
case 0x5: /* SE Vx, Vy */
|
||||
/* XXXX: What if the last nibble isn't 0? */
|
||||
_tmp = cpu->v[(inst >> 8) & 0x0F];
|
||||
_tmp2 = cpu->v[(inst >> 4) & 0x0F];
|
||||
|
||||
if(_tmp == _tmp2)
|
||||
cpu->pc += 2;
|
||||
goto out;
|
||||
|
||||
case 0x6: /* LD Vx, #imm8 */
|
||||
cpu->v[(inst >> 8) & 0x0F] = (uint8)inst;
|
||||
goto out;
|
||||
|
||||
case 0x7: /* ADD Vx, #imm8 */
|
||||
cpu->v[(inst >> 8) & 0x0F] += (uint8)inst;
|
||||
goto out;
|
||||
|
||||
case 0x8:
|
||||
switch(inst & 0x000F) {
|
||||
case 0x0: /* LD Vx, Vy */
|
||||
cpu->v[(inst >> 8) & 0x0F] = cpu->v[(inst >> 4) & 0x0F];
|
||||
goto out;
|
||||
|
||||
case 0x1: /* OR Vx, Vy */
|
||||
cpu->v[(inst >> 8) & 0x0F] |= cpu->v[(inst >> 4) & 0x0F];
|
||||
goto out;
|
||||
|
||||
case 0x2: /* AND Vx, Vy */
|
||||
cpu->v[(inst >> 8) & 0x0F] &= cpu->v[(inst >> 4) & 0x0F];
|
||||
goto out;
|
||||
|
||||
case 0x3: /* XOR Vx, Vy */
|
||||
cpu->v[(inst >> 8) & 0x0F] ^= cpu->v[(inst >> 4) & 0x0F];
|
||||
goto out;
|
||||
|
||||
case 0x4: /* ADD Vx, Vy */
|
||||
_tmp = (inst >> 8) & 0x0F;
|
||||
_tmp3 = cpu->v[_tmp] + cpu->v[(inst >> 4) & 0x0F];
|
||||
cpu->v[_tmp] = (uint8)_tmp3;
|
||||
cpu->v[0xF] = (uint8)(_tmp3 >> 8) & 0x01;
|
||||
goto out;
|
||||
|
||||
case 0x5: /* SUB Vx, Vy */
|
||||
_tmp = (inst >> 8) & 0xF;
|
||||
_tmp2 = (inst >> 4) & 0xF;
|
||||
_tmp3 = cpu->v[_tmp] - cpu->v[_tmp2];
|
||||
cpu->v[0xF] = !!(cpu->v[_tmp] >= cpu->v[_tmp2]);
|
||||
cpu->v[_tmp] = (uint8)_tmp3;
|
||||
goto out;
|
||||
|
||||
case 0x6: /* SHR Vx {, Vy} */
|
||||
_tmp = (inst >> 8) & 0x0F;
|
||||
cpu->v[0xF] = cpu->v[_tmp] & 0x01;
|
||||
cpu->v[_tmp] >>= 1;
|
||||
goto out;
|
||||
|
||||
case 0x7: /* SUBN Vx, Vy */
|
||||
_tmp = (inst >> 8) & 0xF;
|
||||
_tmp2 = (inst >> 4) & 0xF;
|
||||
_tmp3 = cpu->v[_tmp2] - cpu->v[_tmp];
|
||||
cpu->v[0xF] = !!(cpu->v[_tmp2] >= cpu->v[_tmp]);
|
||||
cpu->v[_tmp] = (uint8)_tmp3;
|
||||
goto out;
|
||||
|
||||
case 0xE: /* SHL Vx {, Vy} */
|
||||
_tmp = (inst >> 8) & 0x0F;
|
||||
cpu->v[0xF] = (cpu->v[_tmp] >> 7) & 0x01;
|
||||
cpu->v[_tmp] <<= 1;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
/* XXXX */
|
||||
goto out;
|
||||
}
|
||||
|
||||
case 0x9: /* SNE Vx, Vy */
|
||||
/* XXXX: What if the last nibble isn't 0? */
|
||||
_tmp = cpu->v[(inst >> 8) & 0x0F];
|
||||
_tmp2 = cpu->v[(inst >> 4) & 0x0F];
|
||||
|
||||
if(_tmp != _tmp2)
|
||||
cpu->pc += 2;
|
||||
goto out;
|
||||
|
||||
case 0xA: /* LD I, #addr12 */
|
||||
cpu->i = inst & 0x0FFF;
|
||||
goto out;
|
||||
|
||||
case 0xB: /* JP V0, #addr12 */
|
||||
cpu->pc = (inst & 0x0FFF) + cpu->v[0];
|
||||
goto out;
|
||||
|
||||
case 0xC: /* RND Vx, #imm8 */
|
||||
_tmp = (uint8)inst;
|
||||
cpu->v[(inst >> 8) & 0xF] = rand() & _tmp;
|
||||
goto out;
|
||||
|
||||
case 0xD: /* DRW Vx, Vy, #imm4 */
|
||||
cpu->v[0xF] = cpu->draw_spr(cpu, cpu->i, cpu->v[(inst >> 8) & 0xF],
|
||||
cpu->v[(inst >> 4) & 0xF], inst & 0xF);
|
||||
goto out;
|
||||
|
||||
case 0xE:
|
||||
switch(inst & 0xFF) {
|
||||
case 0x9E: /* SKP Vx */
|
||||
_tmp = cpu->v[(inst >> 8) & 0xF] & 0xF;
|
||||
if(cpu->rread(cpu, _tmp))
|
||||
cpu->pc += 2;
|
||||
goto out;
|
||||
|
||||
case 0xA1: /* SKNP Vx */
|
||||
_tmp = cpu->v[(inst >> 8) & 0xF] & 0xF;
|
||||
if(!cpu->rread(cpu, _tmp))
|
||||
cpu->pc += 2;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
/* XXXX */
|
||||
goto out;
|
||||
}
|
||||
|
||||
case 0xF:
|
||||
switch(inst & 0xFF) {
|
||||
case 0x07: /* LD Vx, DT */
|
||||
cpu->v[(inst >> 8) & 0xF] = cpu->rread(cpu, CHIP8_REG_DT);
|
||||
goto out;
|
||||
|
||||
case 0x0A: /* LD Vx, K */
|
||||
for(_tmp = 0; _tmp < 15; ++_tmp) {
|
||||
if(cpu->rread(cpu, _tmp)) {
|
||||
cpu->v[(inst >> 8) & 0xF] = _tmp;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Didn't find a key pressed, so repeat the instruction. */
|
||||
cpu->pc -= 2;
|
||||
goto out;
|
||||
|
||||
case 0x15: /* LD DT, Vx */
|
||||
cpu->rwrite(cpu, CHIP8_REG_DT, cpu->v[(inst >> 8) & 0xF]);
|
||||
goto out;
|
||||
|
||||
case 0x18: /* LD ST, Vx */
|
||||
cpu->rwrite(cpu, CHIP8_REG_ST, cpu->v[(inst >> 8) & 0xF]);
|
||||
goto out;
|
||||
|
||||
case 0x1E: /* ADD I, Vx */
|
||||
cpu->i += cpu->v[(inst >> 8) & 0xF];
|
||||
cpu->v[0xF] = !!(cpu->i >> 12);
|
||||
goto out;
|
||||
|
||||
case 0x29: /* LD F, Vx */
|
||||
/* The font is at 0x000, so this is easy. */
|
||||
cpu->i = (cpu->v[(inst >> 8) & 0xF] & 0xF) * 5;
|
||||
goto out;
|
||||
|
||||
case 0x33: /* LD B, Vx */
|
||||
_tmp = cpu->v[(inst >> 8) & 0xF];
|
||||
cpu->mwrite(cpu, cpu->i, _tmp / 100);
|
||||
cpu->mwrite(cpu, cpu->i + 1, (_tmp % 100) / 10);
|
||||
cpu->mwrite(cpu, cpu->i + 2, _tmp % 10);
|
||||
goto out;
|
||||
|
||||
case 0x55: /* LD [I], Vx */
|
||||
_tmp2 = (inst >> 8) & 0xF;
|
||||
|
||||
for(_tmp = 0; _tmp <= _tmp2; ++_tmp) {
|
||||
cpu->mwrite(cpu, cpu->i + _tmp, cpu->v[_tmp]);
|
||||
}
|
||||
|
||||
cpu->i += _tmp2 + 1;
|
||||
|
||||
goto out;
|
||||
|
||||
case 0x65: /* LD Vx, [I] */
|
||||
_tmp2 = (inst >> 8) & 0xF;
|
||||
|
||||
for(_tmp = 0; _tmp <= _tmp2; ++_tmp) {
|
||||
cpu->v[_tmp] = cpu->mread(cpu, cpu->i + _tmp);
|
||||
}
|
||||
|
||||
cpu->i += _tmp2 + 1;
|
||||
|
||||
goto out;
|
||||
|
||||
default:
|
||||
/* XXXX */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
;
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012,
|
||||
2014, 2015, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -43,8 +44,8 @@
|
||||
#include "colecovision.h"
|
||||
#include "colecomem.h"
|
||||
#include "sn76489.h"
|
||||
#include "smsvdp.h"
|
||||
#include "smsz80.h"
|
||||
#include "tms9918a.h"
|
||||
#include "sms.h"
|
||||
#include "rom.h"
|
||||
|
||||
@@ -65,25 +66,30 @@ static uint32 cart_len = 0;
|
||||
static uint8 cont_mode = 0;
|
||||
static uint32 rom_crc, rom_adler;
|
||||
|
||||
static uint32 megacart_page = 0;
|
||||
static uint32 megacart_pages = 0;
|
||||
|
||||
extern sn76489_t psg;
|
||||
|
||||
uint16 coleco_cont_bits[2];
|
||||
|
||||
uint8 coleco_port_read(uint16 port) {
|
||||
uint8 tmp;
|
||||
|
||||
switch(port & 0xE0) {
|
||||
case 0xA0:
|
||||
if(port & 0x01)
|
||||
return sms_vdp_status_read();
|
||||
return tms9918a_vdp_status_read();
|
||||
else
|
||||
return sms_vdp_data_read();
|
||||
return tms9918a_vdp_data_read();
|
||||
|
||||
case 0xE0:
|
||||
if(cont_mode == 0) {
|
||||
return ~((uint8)(coleco_cont_bits[port & 0x01] & 0xFF));
|
||||
}
|
||||
else {
|
||||
return ~((uint8)(coleco_cont_bits[port & 0x01] >> 8));
|
||||
}
|
||||
if(cont_mode == 0)
|
||||
tmp = ~((uint8)(coleco_cont_bits[(port & 0x02) >> 1] & 0xFF));
|
||||
else
|
||||
tmp = ~((uint8)(coleco_cont_bits[(port & 0x02) >> 1] >> 8));
|
||||
|
||||
return tmp & 0x7f;
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
@@ -94,10 +100,9 @@ void coleco_port_write(uint16 port, uint8 data) {
|
||||
switch(port & 0xE0) {
|
||||
case 0xA0:
|
||||
if(port & 0x01)
|
||||
sms_vdp_ctl_write(data);
|
||||
tms9918a_vdp_ctl_write(data);
|
||||
else
|
||||
sms_vdp_data_write(data);
|
||||
|
||||
tms9918a_vdp_data_write(data);
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
@@ -115,10 +120,36 @@ void coleco_port_write(uint16 port, uint8 data) {
|
||||
}
|
||||
|
||||
uint8 coleco_mem_read(uint16 addr) {
|
||||
if(megacart_pages && addr >= 0xFFC0) {
|
||||
int i;
|
||||
|
||||
megacart_page = (addr & (megacart_pages - 1));
|
||||
|
||||
/* Fix the paging... */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
read_map[i + 0xC0] = cart_rom + (megacart_page << 14) + (i << 8);
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(read_map);
|
||||
}
|
||||
|
||||
return read_map[addr >> 8][addr & 0xFF];
|
||||
}
|
||||
|
||||
void coleco_mem_write(uint16 addr, uint8 data) {
|
||||
if(megacart_pages && addr >= 0xFFC0) {
|
||||
int i;
|
||||
|
||||
megacart_page = (addr & (megacart_pages - 1));
|
||||
|
||||
/* Fix the paging... */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
read_map[i + 0xC0] = cart_rom + (megacart_page << 14) + (i << 8);
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(read_map);
|
||||
}
|
||||
|
||||
write_map[addr >> 8][addr & 0xFF] = data;
|
||||
}
|
||||
|
||||
@@ -126,6 +157,20 @@ uint16 coleco_mem_read16(uint16 addr) {
|
||||
uint16 rv = (uint16)read_map[addr >> 8][addr & 0xFF];
|
||||
++addr;
|
||||
|
||||
/* Nobody'd be stupid enough to do this, would they?... */
|
||||
if(megacart_pages && addr >= 0xFFC0) {
|
||||
int i;
|
||||
|
||||
megacart_page = (addr & (megacart_pages - 1));
|
||||
|
||||
/* Fix the paging... */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
read_map[i + 0xC0] = cart_rom + (megacart_page << 14) + (i << 8);
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(read_map);
|
||||
}
|
||||
|
||||
rv |= ((uint16)read_map[addr >> 8][addr & 0xFF]) << 8;
|
||||
return rv;
|
||||
}
|
||||
@@ -133,6 +178,22 @@ uint16 coleco_mem_read16(uint16 addr) {
|
||||
void coleco_mem_write16(uint16 addr, uint16 data) {
|
||||
write_map[addr >> 8][addr & 0xFF] = (uint8)data;
|
||||
++addr;
|
||||
|
||||
/* Hopefully if nobody's stupid enough to trigger the one above, there
|
||||
really won't be anyone triggering THIS one... */
|
||||
if(megacart_pages && addr >= 0xFFC0) {
|
||||
int i;
|
||||
|
||||
megacart_page = (addr & (megacart_pages - 1));
|
||||
|
||||
/* Fix the paging... */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
read_map[i + 0xC0] = cart_rom + (megacart_page << 14) + (i << 8);
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(read_map);
|
||||
}
|
||||
|
||||
write_map[addr >> 8][addr & 0xFF] = (uint8)(data >> 8);
|
||||
}
|
||||
|
||||
@@ -167,13 +228,38 @@ static void finalize_load(const char *fn) {
|
||||
}
|
||||
|
||||
/* ROM */
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
write_map[i + 0x80] = dummy_areaw;
|
||||
if(cart_len > 0x8000) {
|
||||
/* We have a MegaCart... */
|
||||
megacart_page = 0;
|
||||
megacart_pages = cart_len >> 14;
|
||||
|
||||
if(i < (cart_len >> 8))
|
||||
read_map[i + 0x80] = cart_rom + (i << 8);
|
||||
else
|
||||
read_map[i + 0x80] = dummy_arear;
|
||||
#ifdef DEBUG
|
||||
printf("coleco_mem_load_rom: Detected MegaCart\n"
|
||||
" %" PRIu32 " bytes long = %" PRIu32 " pages\n",
|
||||
cart_len, megacart_pages);
|
||||
#endif
|
||||
|
||||
/* Always map 0x8000-0xBFFF to the top 16k of the cart... The page
|
||||
selected for 0xC000-0xFFFF starts out as page 0. */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
write_map[i + 0x80] = dummy_areaw;
|
||||
write_map[i + 0xC0] = dummy_areaw;
|
||||
read_map[i + 0x80] = cart_rom + (cart_len - 0x4000) + (i << 8);
|
||||
read_map[i + 0xC0] = cart_rom + (i << 8);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
megacart_pages = megacart_page = 0;
|
||||
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
write_map[i + 0x80] = dummy_areaw;
|
||||
|
||||
if(i < (cart_len >> 8))
|
||||
read_map[i + 0x80] = cart_rom + (i << 8);
|
||||
else
|
||||
read_map[i + 0x80] = dummy_arear;
|
||||
}
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(read_map);
|
||||
@@ -254,7 +340,7 @@ static int load_gz_rom(const char *fn) {
|
||||
len = gzread(fp, cart_rom, 0x100);
|
||||
|
||||
while(len != 0 && len != -1) {
|
||||
cart_len += len;
|
||||
cart_len += 0x100;
|
||||
tmp = realloc(cart_rom, cart_len + 0x100);
|
||||
|
||||
if(!tmp) {
|
||||
@@ -306,7 +392,7 @@ static int load_bz2_rom(const char *fn) {
|
||||
len = BZ2_bzread(fp, cart_rom, 0x100);
|
||||
|
||||
while(len != 0 && len != -1) {
|
||||
cart_len += len;
|
||||
cart_len += 0x100;
|
||||
tmp = realloc(cart_rom, cart_len + 0x100);
|
||||
|
||||
if(!tmp) {
|
||||
@@ -434,7 +520,7 @@ static int load_zip_rom(const char *fn) {
|
||||
unzCloseCurrentFile(fp);
|
||||
unzClose(fp);
|
||||
|
||||
cart_len = size;
|
||||
cart_len = realsize;
|
||||
finalize_load(fn);
|
||||
|
||||
return ROM_LOAD_SUCCESS;
|
||||
@@ -493,7 +579,7 @@ int coleco_mem_load_rom(const char *fn) {
|
||||
fread(cart_rom, size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
cart_len = size;
|
||||
cart_len = realsize;
|
||||
finalize_load(fn);
|
||||
|
||||
return ROM_LOAD_SUCCESS;
|
||||
@@ -543,6 +629,18 @@ int coleco_regs_read_context(const uint8 *buf) {
|
||||
/* Copy in the registers */
|
||||
cont_mode = buf[16];
|
||||
|
||||
if(megacart_pages) {
|
||||
int i;
|
||||
|
||||
megacart_page = buf[17];
|
||||
|
||||
/* Fix the paging... */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
read_map[i + 0xC0] = cart_rom + (megacart_page << 14) + (i << 8);
|
||||
sms_z80_set_readmap(read_map);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -586,7 +684,8 @@ int coleco_mem_write_context(FILE *fp) {
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
|
||||
data[0] = cont_mode;
|
||||
data[1] = data[2] = data[3] = 0;
|
||||
data[1] = megacart_page;
|
||||
data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
return 0;
|
||||
@@ -656,6 +755,7 @@ int coleco_mem_init(void) {
|
||||
memset(bios_rom, 0, 8192);
|
||||
|
||||
bios_loaded = 0;
|
||||
megacart_page = megacart_pages = 0;
|
||||
coleco_cont_bits[0] = 0;
|
||||
coleco_cont_bits[1] = 0;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "colecomem.h"
|
||||
#include "sms.h"
|
||||
#include "smsvdp.h"
|
||||
#include "tms9918a.h"
|
||||
#include "sn76489.h"
|
||||
#include "smsz80.h"
|
||||
#include "sound.h"
|
||||
@@ -45,13 +46,10 @@
|
||||
extern int sms_psg_enabled;
|
||||
extern sn76489_t psg;
|
||||
extern uint32 psg_samples[313];
|
||||
extern int sms_console;
|
||||
extern int sms_region;
|
||||
extern int sms_cycles_run;
|
||||
extern int sms_cycles_to_run;
|
||||
|
||||
int coleco_initialized = 0;
|
||||
|
||||
extern uint16 coleco_cont_bits[2];
|
||||
|
||||
static const float NTSC_Z80_CLOCK = 3579545.0f;
|
||||
@@ -69,6 +67,58 @@ static const int cont_bit_map[12] = {
|
||||
0x02, 0x08, 0x03, 0x0D, 0x0C, 0x01, 0x0A, 0x0E, 0x04, 0x06, 0x05, 0x09
|
||||
};
|
||||
|
||||
static int cycles_run, cycles_to_run, scanline;
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
static void coleco_frame(int);
|
||||
static void coleco_scanline(void);
|
||||
static void coleco_single_step(void);
|
||||
static void coleco_finish_frame(void);
|
||||
static void coleco_finish_scanline(void);
|
||||
#endif
|
||||
static int coleco_current_scanline(void);
|
||||
static int coleco_cycles_left(void);
|
||||
|
||||
/* Console declaration... */
|
||||
colecovision_t colecovision_cons = {
|
||||
{
|
||||
CONSOLE_COLECOVISION,
|
||||
CONSOLE_COLECOVISION,
|
||||
0,
|
||||
&coleco_shutdown,
|
||||
&coleco_reset,
|
||||
&coleco_soft_reset,
|
||||
#ifndef _arch_dreamcast
|
||||
&coleco_frame,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
&coleco_load_state,
|
||||
&coleco_save_state,
|
||||
NULL, /* save_sram */
|
||||
&coleco_button_pressed,
|
||||
&coleco_button_released,
|
||||
&sms_vdp_framebuffer,
|
||||
&sms_vdp_framesize,
|
||||
&tms9918a_vdp_activeframe,
|
||||
NULL, /* save_cheats */
|
||||
#ifndef _arch_dreamcast
|
||||
&coleco_scanline,
|
||||
&coleco_single_step,
|
||||
&coleco_finish_frame,
|
||||
&coleco_finish_scanline,
|
||||
#else
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
#endif
|
||||
&coleco_current_scanline,
|
||||
&coleco_cycles_left,
|
||||
NULL /* set_control */
|
||||
}
|
||||
};
|
||||
|
||||
int coleco_init(int video_system) {
|
||||
int i;
|
||||
float tmp;
|
||||
@@ -98,7 +148,7 @@ int coleco_init(int video_system) {
|
||||
|
||||
for(i = 0; i < PAL_LINES_PER_FRAME; ++i) {
|
||||
psg_samples[i] = (uint32) (tmp * (i + 1)) -
|
||||
(uint32) (tmp * i);
|
||||
(uint32) (tmp * i);
|
||||
}
|
||||
|
||||
/* We need 882 samples per frame @ 44100 Hz, 50fps. */
|
||||
@@ -109,33 +159,34 @@ int coleco_init(int video_system) {
|
||||
}
|
||||
|
||||
sms_region = region;
|
||||
sms_console = CONSOLE_COLECOVISION;
|
||||
|
||||
gui_set_console((console_t *)&colecovision_cons);
|
||||
|
||||
coleco_mem_init();
|
||||
|
||||
sms_vdp_init(video_system);
|
||||
sms_vdp_init(video_system, 0);
|
||||
sms_z80_init();
|
||||
sound_init(2, video_system);
|
||||
|
||||
sms_z80_set_pread(&coleco_port_read);
|
||||
sms_z80_set_pwrite(&coleco_port_write);
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
colecovision_cons._base.initialized = 1;
|
||||
|
||||
coleco_initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coleco_reset(void) {
|
||||
if(coleco_initialized == 0)
|
||||
if(!colecovision_cons._base.initialized)
|
||||
return 0;
|
||||
|
||||
if(sms_region & SMS_VIDEO_NTSC) {
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
sn76489_reset(&psg, NTSC_Z80_CLOCK, 44100.0f,
|
||||
SN76489_NOISE_BITS_NORMAL, SN76489_NOISE_TAPPED_NORMAL);
|
||||
}
|
||||
else {
|
||||
else
|
||||
sn76489_reset(&psg, PAL_Z80_CLOCK, 44100.0f,
|
||||
SN76489_NOISE_BITS_NORMAL, SN76489_NOISE_TAPPED_NORMAL);
|
||||
}
|
||||
|
||||
sound_reset_buffer();
|
||||
|
||||
@@ -144,18 +195,22 @@ int coleco_reset(void) {
|
||||
|
||||
sms_z80_reset();
|
||||
sms_vdp_reset();
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void coleco_soft_reset(void) {
|
||||
if(coleco_initialized == 0)
|
||||
return;
|
||||
int coleco_soft_reset(void) {
|
||||
if(!colecovision_cons._base.initialized)
|
||||
return 0;
|
||||
|
||||
coleco_mem_reset();
|
||||
|
||||
sms_z80_reset();
|
||||
sms_vdp_reset();
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coleco_shutdown(void) {
|
||||
@@ -166,7 +221,7 @@ int coleco_shutdown(void) {
|
||||
sound_shutdown();
|
||||
|
||||
/* Reset a few things in case we reinit later. */
|
||||
coleco_initialized = 0;
|
||||
colecovision_cons._base.initialized = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -253,12 +308,18 @@ void coleco_button_released(int player, int button) {
|
||||
}
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
int coleco_frame(int run, int skip) {
|
||||
int16 buf[882 << 1];
|
||||
int line, total_lines, samples = 0;
|
||||
static __INLINE__ int update_sound(int16 buf[], int start, int line) {
|
||||
if(sms_psg_enabled)
|
||||
sn76489_execute_samples(&psg, buf + start, psg_samples[line]);
|
||||
else
|
||||
memset(buf + start, 0, psg_samples[line] << 2);
|
||||
|
||||
sms_cycles_run = run;
|
||||
sms_cycles_to_run = 0;
|
||||
return start + (psg_samples[line] << 1);
|
||||
}
|
||||
|
||||
static void coleco_frame(int skip) {
|
||||
int16 buf[882 << 1];
|
||||
int samples = 0, total_lines, line;
|
||||
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
@@ -266,29 +327,151 @@ int coleco_frame(int run, int skip) {
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
for(line = 0; line < total_lines; ++line) {
|
||||
sms_cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
|
||||
sms_cycles_run += tms9918a_vdp_execute(line, &sms_z80_nmi, skip);
|
||||
sms_cycles_run += sms_z80_run(sms_cycles_to_run - sms_cycles_run);
|
||||
cycles_run += tms9918a_vdp_execute(line, &sms_z80_nmi, skip);
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
if(sms_psg_enabled) {
|
||||
sn76489_execute_samples(&psg, buf + samples, psg_samples[line]);
|
||||
}
|
||||
else {
|
||||
memset(buf + samples, 0, psg_samples[line] << 2);
|
||||
}
|
||||
|
||||
samples += psg_samples[line] << 1;
|
||||
samples = update_sound(buf, samples, line);
|
||||
}
|
||||
|
||||
#ifndef TIMING_TEST
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
#endif
|
||||
|
||||
return sms_cycles_run - sms_cycles_to_run;
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
static void coleco_scanline(void) {
|
||||
int16 buf[882 << 1];
|
||||
int total_lines, samples = 0;
|
||||
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
|
||||
/* Run the VDP and Z80 for the whole line. */
|
||||
cycles_run += sms_vdp_execute(scanline, 0);
|
||||
cycles_run += tms9918a_vdp_execute(scanline, &sms_z80_nmi, 0);
|
||||
|
||||
samples = update_sound(buf, 0, scanline);
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* See if we hit the end of a frame by running this scanline. */
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
if(++scanline == total_lines) {
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void coleco_single_step(void) {
|
||||
int16 buf[882 << 1];
|
||||
int total_lines, run;
|
||||
|
||||
/* If we're at the start of a frame, set things up for the first line. Also,
|
||||
if we finished a line last time (or are starting a new frame), run the
|
||||
VDP for the line. */
|
||||
if(!cycles_to_run || cycles_run >= cycles_to_run) {
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
if((run = tms9918a_vdp_execute(scanline, &sms_z80_nmi, 0))) {
|
||||
cycles_run += run;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Run our one instruction. */
|
||||
cycles_run += sms_z80_run(1);
|
||||
|
||||
/* Did we finish a line? */
|
||||
if(cycles_run >= cycles_to_run) {
|
||||
run = update_sound(buf, 0, scanline);
|
||||
sound_update_buffer(buf, run << 1);
|
||||
|
||||
/* Was it the last line in the frame? */
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
if(++scanline == total_lines) {
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void coleco_finish_frame(void) {
|
||||
int16 buf[882 << 1];
|
||||
int samples = 0, total_lines, line;
|
||||
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
for(line = scanline; line < total_lines; ++line) {
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
|
||||
cycles_run += tms9918a_vdp_execute(scanline, &sms_z80_nmi, 0);
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
samples = update_sound(buf, samples, line);
|
||||
}
|
||||
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
static void coleco_finish_scanline(void) {
|
||||
int16 buf[882 << 1];
|
||||
int total_lines, samples = 0;
|
||||
|
||||
/* Make sure we have something to do. */
|
||||
if(cycles_run >= cycles_to_run)
|
||||
return;
|
||||
|
||||
/* Run the Z80 for the rest of the line. The VDP should've already been
|
||||
run. */
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
samples = update_sound(buf, 0, scanline);
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* See if we hit the end of a frame by finishing this line. */
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
if(++scanline == total_lines) {
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int coleco_current_scanline(void) {
|
||||
return scanline;
|
||||
}
|
||||
|
||||
static int coleco_cycles_left(void) {
|
||||
return cycles_to_run - cycles_run;
|
||||
}
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
static int coleco_save_state_int(const char *filename)
|
||||
#else
|
||||
@@ -298,7 +481,7 @@ int coleco_save_state(const char *filename)
|
||||
FILE *fp;
|
||||
uint8 data[4];
|
||||
|
||||
if(!coleco_initialized)
|
||||
if(!colecovision_cons._base.initialized)
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
|
||||
@@ -641,7 +824,7 @@ int coleco_load_state(const char *filename)
|
||||
char byte;
|
||||
int rv;
|
||||
|
||||
if(!coleco_initialized) {
|
||||
if(!colecovision_cons._base.initialized) {
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
}
|
||||
@@ -738,3 +921,105 @@ int coleco_load_state(const char *filename __UNUSED__) {
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
int coleco_write_state(FILE *fp)
|
||||
{
|
||||
uint8 data[4];
|
||||
|
||||
if(!colecovision_cons._base.initialized)
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
|
||||
if(!fp)
|
||||
return -1;
|
||||
|
||||
fprintf(fp, "CrabEmu Save State");
|
||||
|
||||
/* Write save state version */
|
||||
data[0] = 0x00;
|
||||
data[1] = 0x02;
|
||||
fwrite(data, 1, 2, fp);
|
||||
|
||||
/* Write out the Console Metadata block */
|
||||
data[0] = 'C';
|
||||
data[1] = 'O';
|
||||
data[2] = 'N';
|
||||
data[3] = 'S';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(24, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 1) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
UINT32_TO_BUF(1, data);
|
||||
fwrite(data, 1, 4, fp); /* Console (1 = ColecoVision) */
|
||||
|
||||
data[0] = 0; /* Console sub-type */
|
||||
data[1] = 0; /* Region code */
|
||||
data[2] = sms_region >> 4; /* Video system */
|
||||
data[3] = 0; /* Reserved */
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
/* Write each block's state */
|
||||
if(coleco_game_write_context(fp)) {
|
||||
return -1;
|
||||
}
|
||||
else if(sms_z80_write_context(fp)) {
|
||||
return -1;
|
||||
}
|
||||
else if(sms_psg_write_context(fp)) {
|
||||
return -1;
|
||||
}
|
||||
else if(sms_vdp_write_context(fp)) {
|
||||
return -1;
|
||||
}
|
||||
else if(coleco_mem_write_context(fp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int coleco_read_state(FILE *fp)
|
||||
{
|
||||
char str[19];
|
||||
char byte;
|
||||
int rv;
|
||||
|
||||
if(!colecovision_cons._base.initialized) {
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!fp)
|
||||
return -1;
|
||||
|
||||
fread(str, 18, 1, fp);
|
||||
str[18] = 0;
|
||||
if(strcmp("CrabEmu Save State", str)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Read save state version */
|
||||
fread(&byte, 1, 1, fp);
|
||||
if(byte != 0x00) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
fread(&byte, 1, 1, fp);
|
||||
|
||||
if(byte != 0x02) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
rv = coleco_load_state_v2(fp);
|
||||
|
||||
sound_reset_buffer();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012 Lawrence Sebald
|
||||
Copyright (C) 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -21,11 +21,10 @@
|
||||
#define COLECOVISION_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
#include "console.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
#define CONSOLE_COLECOVISION 5
|
||||
|
||||
/* Button defines */
|
||||
#define COLECOVISION_UP 0
|
||||
#define COLECOVISION_DOWN 1
|
||||
@@ -48,17 +47,25 @@ CLINKAGE
|
||||
|
||||
extern int coleco_init(int video_system);
|
||||
extern int coleco_reset(void);
|
||||
extern void coleco_soft_reset(void);
|
||||
extern int coleco_soft_reset(void);
|
||||
extern int coleco_shutdown(void);
|
||||
|
||||
extern int coleco_frame(int run, int skip);
|
||||
|
||||
extern void coleco_button_pressed(int player, int button);
|
||||
extern void coleco_button_released(int player, int button);
|
||||
|
||||
extern int coleco_save_state(const char *fn);
|
||||
extern int coleco_load_state(const char *fn);
|
||||
|
||||
extern int coleco_write_state(FILE *fp);
|
||||
extern int coleco_read_state(FILE *fp);
|
||||
|
||||
/* Console definition. */
|
||||
typedef struct crabemu_colecovision {
|
||||
console_t _base;
|
||||
} colecovision_t;
|
||||
|
||||
extern colecovision_t colecovision_cons;
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !COLECOVISION_H */
|
||||
|
||||
@@ -32,8 +32,6 @@ static int chr_is_rom;
|
||||
static int prg_banks;
|
||||
static int chr_size;
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void fill_prg_map(void) {
|
||||
int i, page;
|
||||
|
||||
@@ -44,14 +42,18 @@ static void fill_prg_map(void) {
|
||||
/* Which page should be swapped? */
|
||||
if(bank_regs[0] & 0x04) {
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i + page) << 8);
|
||||
nes_read_map[i + 0xC0] = nes_prg_rom + ((i + 0x3C0) << 8);
|
||||
nes_read_map[i + 0x80] = nes_prg_rom +
|
||||
(((i + page) << 8) & (nes_prg_rom_size - 1));
|
||||
nes_read_map[i + 0xC0] = nes_prg_rom +
|
||||
(((i + 0x3C0) << 8) & (nes_prg_rom_size - 1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + (i << 8);
|
||||
nes_read_map[i + 0xC0] = nes_prg_rom + ((i + page) << 8);
|
||||
nes_read_map[i + 0x80] = nes_prg_rom +
|
||||
((i << 8) & (nes_prg_rom_size - 1));
|
||||
nes_read_map[i + 0xC0] = nes_prg_rom +
|
||||
(((i + page) << 8) & (nes_prg_rom_size - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +61,8 @@ static void fill_prg_map(void) {
|
||||
page = (bank_regs[3] & 0x0E) << 6;
|
||||
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i + page) << 8);
|
||||
nes_read_map[i + 0x80] = nes_prg_rom +
|
||||
(((i + page) << 8) & (nes_prg_rom_size - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2013 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "nesmem.h"
|
||||
#include "nesppu.h"
|
||||
|
||||
/* iNES Mapper 2: UxROM */
|
||||
static uint8 bank_reg = 0;
|
||||
static int prg_banks;
|
||||
|
||||
static void fill_prg_map(void) {
|
||||
int i, page = bank_reg << 6;
|
||||
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i + page) << 8);
|
||||
}
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
int is_rom = 1, i, b;
|
||||
|
||||
prg_banks = nes_prg_rom_size >> 8;
|
||||
b = prg_banks - 0x40;
|
||||
bank_reg = 0;
|
||||
|
||||
/* Fill in the read map */
|
||||
for(i = 0; i < 0x40; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + (i << 8);
|
||||
nes_read_map[i + 0xC0] = nes_prg_rom + ((i + b) << 8);
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
nes_read_map[i + 0x60] = nes_sram + (i << 8);
|
||||
nes_write_map[i + 0x60] = nes_sram + (i << 8);
|
||||
}
|
||||
|
||||
/* Set up the PPU */
|
||||
if(!nes_chr_rom_size)
|
||||
is_rom = 0;
|
||||
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom, is_rom);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + 0x1000, is_rom);
|
||||
|
||||
if(nes_cur_hdr.flags[0] & 0x08)
|
||||
nes_ppu_set_tblmirrors(0, 1, 2, 3);
|
||||
else if(nes_cur_hdr.flags[0] & 0x01)
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
else
|
||||
nes_ppu_set_tblmirrors(0, 0, 1, 1);
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void reset(void) {
|
||||
bank_reg = 0;
|
||||
fill_prg_map();
|
||||
}
|
||||
|
||||
static uint8 read_byte(void *cpu __UNUSED__, uint16 addr) {
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
return nes_ppu_readreg(addr & 0x0007);
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
return nes_mem_readreg(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return nes_read_map[addr >> 8][(uint8)addr];
|
||||
}
|
||||
|
||||
static void write_byte(void *cpu __UNUSED__, uint16 addr, uint8 val) {
|
||||
/* Is the write bound anywhere interesting? */
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
nes_ppu_writereg(addr & 0x0007, val);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
nes_mem_writereg(addr, val);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
bank_reg = val;
|
||||
fill_prg_map();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
nes_write_map[addr >> 8][(uint8)addr] = val;
|
||||
}
|
||||
|
||||
static int write_cxt(FILE *fp) {
|
||||
uint8 data[4];
|
||||
|
||||
data[0] = 'M';
|
||||
data[1] = 'P';
|
||||
data[2] = 'P';
|
||||
data[3] = 'R';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(20, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
UINT16_TO_BUF(0, data);
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 0) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
|
||||
data[0] = bank_reg;
|
||||
data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cxt(const uint8 *buf) {
|
||||
uint16 ver;
|
||||
uint32 len;
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len != 20)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
BUF_TO_UINT16(buf + 8, ver);
|
||||
if(ver != 1)
|
||||
return -1;
|
||||
|
||||
/* Check the child pointer */
|
||||
if(buf[12] != 0 || buf[13] != 0 || buf[14] != 0 || buf[15] != 0)
|
||||
return -1;
|
||||
|
||||
/* Read in the registers */
|
||||
bank_reg = buf[16];
|
||||
|
||||
fill_prg_map();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32 cxt_len(void) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
/* Mapper definition. */
|
||||
nes_mapper_t nes_mapper_2 = {
|
||||
2,
|
||||
&init,
|
||||
&reset,
|
||||
&read_byte,
|
||||
&write_byte,
|
||||
&write_cxt,
|
||||
&read_cxt,
|
||||
&cxt_len
|
||||
};
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2013 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "nesmem.h"
|
||||
#include "nesppu.h"
|
||||
|
||||
/* iNES Mapper 3: CNROM */
|
||||
static uint8 bank_reg = 0;
|
||||
static int chr_banks;
|
||||
|
||||
static void fill_chr_map(void) {
|
||||
int b = bank_reg << 13;
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom + b, 1);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + b + 0x1000, 1);
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
int i, banks = nes_prg_rom_size >> 8;
|
||||
|
||||
chr_banks = nes_chr_rom_size >> 8;
|
||||
bank_reg = 0;
|
||||
|
||||
/* Fill in the read map */
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i & (banks - 1)) << 8);
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
nes_read_map[i + 0x60] = nes_sram + (i << 8);
|
||||
nes_write_map[i + 0x60] = nes_sram + (i << 8);
|
||||
}
|
||||
|
||||
/* Set up the PPU */
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom, 1);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + 0x1000, 1);
|
||||
|
||||
if(nes_cur_hdr.flags[0] & 0x08)
|
||||
nes_ppu_set_tblmirrors(0, 1, 2, 3);
|
||||
else if(nes_cur_hdr.flags[0] & 0x01)
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
else
|
||||
nes_ppu_set_tblmirrors(0, 0, 1, 1);
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void reset(void) {
|
||||
bank_reg = 0;
|
||||
fill_chr_map();
|
||||
}
|
||||
|
||||
static uint8 read_byte(void *cpu __UNUSED__, uint16 addr) {
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
return nes_ppu_readreg(addr & 0x0007);
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
return nes_mem_readreg(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return nes_read_map[addr >> 8][(uint8)addr];
|
||||
}
|
||||
|
||||
static void write_byte(void *cpu __UNUSED__, uint16 addr, uint8 val) {
|
||||
/* Is the write bound anywhere interesting? */
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
nes_ppu_writereg(addr & 0x0007, val);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
nes_mem_writereg(addr, val);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
bank_reg = val & 0x03;
|
||||
fill_chr_map();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
nes_write_map[addr >> 8][(uint8)addr] = val;
|
||||
}
|
||||
|
||||
static int write_cxt(FILE *fp) {
|
||||
uint8 data[4];
|
||||
|
||||
data[0] = 'M';
|
||||
data[1] = 'P';
|
||||
data[2] = 'P';
|
||||
data[3] = 'R';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(20, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
UINT16_TO_BUF(0, data);
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 0) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
|
||||
data[0] = bank_reg;
|
||||
data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cxt(const uint8 *buf) {
|
||||
uint16 ver;
|
||||
uint32 len;
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len != 20)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
BUF_TO_UINT16(buf + 8, ver);
|
||||
if(ver != 1)
|
||||
return -1;
|
||||
|
||||
/* Check the child pointer */
|
||||
if(buf[12] != 0 || buf[13] != 0 || buf[14] != 0 || buf[15] != 0)
|
||||
return -1;
|
||||
|
||||
/* Read in the registers */
|
||||
bank_reg = buf[16] & 0x03;
|
||||
|
||||
fill_chr_map();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32 cxt_len(void) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
/* Mapper definition. */
|
||||
nes_mapper_t nes_mapper_3 = {
|
||||
3,
|
||||
&init,
|
||||
&reset,
|
||||
&read_byte,
|
||||
&write_byte,
|
||||
&write_cxt,
|
||||
&read_cxt,
|
||||
&cxt_len
|
||||
};
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2013 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "nesmem.h"
|
||||
#include "nesppu.h"
|
||||
|
||||
/* iNES Mapper 66: GxROM */
|
||||
static uint8 bank_reg = 0, chr_is_rom;
|
||||
static int prg_banks;
|
||||
|
||||
static void fill_map(void) {
|
||||
int i, page = (bank_reg & 0x30) << 3, cpage = (bank_reg & 0x03) << 13;
|
||||
|
||||
/* Deal with PRG first. */
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i + page) << 8);
|
||||
}
|
||||
|
||||
/* Now deal with CHR. */
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom + cpage, chr_is_rom);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + cpage + 0x1000, chr_is_rom);
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
int i;
|
||||
|
||||
prg_banks = nes_prg_rom_size >> 8;
|
||||
bank_reg = 0;
|
||||
chr_is_rom = 1;
|
||||
|
||||
/* Fill in the read map */
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i & (prg_banks - 1)) << 8);
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
nes_read_map[i + 0x60] = nes_sram + (i << 8);
|
||||
nes_write_map[i + 0x60] = nes_sram + (i << 8);
|
||||
}
|
||||
|
||||
/* Set up the PPU */
|
||||
if(!nes_chr_rom_size)
|
||||
chr_is_rom = 0;
|
||||
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom, chr_is_rom);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + 0x1000, chr_is_rom);
|
||||
|
||||
if(nes_cur_hdr.flags[0] & 0x08)
|
||||
nes_ppu_set_tblmirrors(0, 1, 2, 3);
|
||||
else if(nes_cur_hdr.flags[0] & 0x01)
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
else
|
||||
nes_ppu_set_tblmirrors(0, 0, 1, 1);
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void reset(void) {
|
||||
bank_reg = 0;
|
||||
fill_map();
|
||||
}
|
||||
|
||||
static uint8 read_byte(void *cpu __UNUSED__, uint16 addr) {
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
return nes_ppu_readreg(addr & 0x0007);
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
return nes_mem_readreg(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return nes_read_map[addr >> 8][(uint8)addr];
|
||||
}
|
||||
|
||||
static void write_byte(void *cpu __UNUSED__, uint16 addr, uint8 val) {
|
||||
/* Is the write bound anywhere interesting? */
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
nes_ppu_writereg(addr & 0x0007, val);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
nes_mem_writereg(addr, val);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
bank_reg = val;
|
||||
fill_map();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
nes_write_map[addr >> 8][(uint8)addr] = val;
|
||||
}
|
||||
|
||||
static int write_cxt(FILE *fp) {
|
||||
uint8 data[4];
|
||||
|
||||
data[0] = 'M';
|
||||
data[1] = 'P';
|
||||
data[2] = 'P';
|
||||
data[3] = 'R';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(20, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
UINT16_TO_BUF(0, data);
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 0) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
|
||||
data[0] = bank_reg;
|
||||
data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cxt(const uint8 *buf) {
|
||||
uint16 ver;
|
||||
uint32 len;
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len != 20)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
BUF_TO_UINT16(buf + 8, ver);
|
||||
if(ver != 1)
|
||||
return -1;
|
||||
|
||||
/* Check the child pointer */
|
||||
if(buf[12] != 0 || buf[13] != 0 || buf[14] != 0 || buf[15] != 0)
|
||||
return -1;
|
||||
|
||||
/* Read in the registers */
|
||||
bank_reg = buf[16];
|
||||
|
||||
fill_map();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32 cxt_len(void) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
/* Mapper definition. */
|
||||
nes_mapper_t nes_mapper_66 = {
|
||||
66,
|
||||
&init,
|
||||
&reset,
|
||||
&read_byte,
|
||||
&write_byte,
|
||||
&write_cxt,
|
||||
&read_cxt,
|
||||
&cxt_len
|
||||
};
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2013 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "nesmem.h"
|
||||
#include "nesppu.h"
|
||||
|
||||
/* iNES Mapper 7: AxROM */
|
||||
static uint8 bank_reg = 0;
|
||||
static int prg_banks;
|
||||
|
||||
static void fill_map(void) {
|
||||
int i, page = (bank_reg & 0x07) << 7;
|
||||
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i + page) << 8);
|
||||
}
|
||||
|
||||
nes_mem_update_map();
|
||||
|
||||
if(bank_reg & 0x10)
|
||||
nes_ppu_set_tblmirrors(1, 1, 1, 1);
|
||||
else
|
||||
nes_ppu_set_tblmirrors(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
int is_rom = 1, i;
|
||||
|
||||
prg_banks = nes_prg_rom_size >> 8;
|
||||
bank_reg = 0;
|
||||
|
||||
/* Fill in the read map */
|
||||
for(i = 0; i < 0x80; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i & (prg_banks - 1)) << 8);
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
nes_read_map[i + 0x60] = nes_sram + (i << 8);
|
||||
nes_write_map[i + 0x60] = nes_sram + (i << 8);
|
||||
}
|
||||
|
||||
/* Set up the PPU */
|
||||
if(!nes_chr_rom_size)
|
||||
is_rom = 0;
|
||||
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom, is_rom);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + 0x1000, is_rom);
|
||||
nes_ppu_set_tblmirrors(0, 0, 0, 0);
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void reset(void) {
|
||||
bank_reg = 0;
|
||||
fill_map();
|
||||
}
|
||||
|
||||
static uint8 read_byte(void *cpu __UNUSED__, uint16 addr) {
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
return nes_ppu_readreg(addr & 0x0007);
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
return nes_mem_readreg(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return nes_read_map[addr >> 8][(uint8)addr];
|
||||
}
|
||||
|
||||
static void write_byte(void *cpu __UNUSED__, uint16 addr, uint8 val) {
|
||||
/* Is the write bound anywhere interesting? */
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
nes_ppu_writereg(addr & 0x0007, val);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
nes_mem_writereg(addr, val);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
bank_reg = val;
|
||||
fill_map();
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
nes_write_map[addr >> 8][(uint8)addr] = val;
|
||||
}
|
||||
|
||||
static int write_cxt(FILE *fp) {
|
||||
uint8 data[4];
|
||||
|
||||
data[0] = 'M';
|
||||
data[1] = 'P';
|
||||
data[2] = 'P';
|
||||
data[3] = 'R';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(20, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
UINT16_TO_BUF(0, data);
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 0) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
|
||||
data[0] = bank_reg;
|
||||
data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cxt(const uint8 *buf) {
|
||||
uint16 ver;
|
||||
uint32 len;
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len != 20)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
BUF_TO_UINT16(buf + 8, ver);
|
||||
if(ver != 1)
|
||||
return -1;
|
||||
|
||||
/* Check the child pointer */
|
||||
if(buf[12] != 0 || buf[13] != 0 || buf[14] != 0 || buf[15] != 0)
|
||||
return -1;
|
||||
|
||||
/* Read in the registers */
|
||||
bank_reg = buf[16];
|
||||
|
||||
fill_map();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32 cxt_len(void) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
/* Mapper definition. */
|
||||
nes_mapper_t nes_mapper_7 = {
|
||||
7,
|
||||
&init,
|
||||
&reset,
|
||||
&read_byte,
|
||||
&write_byte,
|
||||
&write_cxt,
|
||||
&read_cxt,
|
||||
&cxt_len
|
||||
};
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2013 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "nesmem.h"
|
||||
#include "nesppu.h"
|
||||
|
||||
/* iNES Mapper 9: MMC2/PxROM */
|
||||
static uint8 prg_bank = 0;
|
||||
static uint8 chr_bank[4] = { 0, 0, 0, 0 };
|
||||
static int latches[2] = { 0, 0 }, mirror = 0;
|
||||
static int prg_banks, chr_size;
|
||||
|
||||
static void fill_prg_map(void) {
|
||||
int i, page = (prg_bank & 0x0F) << 5;
|
||||
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
nes_read_map[i + 0x80] = nes_prg_rom + ((i + page) << 8);
|
||||
}
|
||||
|
||||
nes_mem_update_map();
|
||||
}
|
||||
|
||||
static void fill_chr_map(void) {
|
||||
int page;
|
||||
|
||||
if(latches[0])
|
||||
page = (chr_bank[1] << 12) & (chr_size - 1);
|
||||
else
|
||||
page = (chr_bank[0] << 12) & (chr_size - 1);
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom + page, 1);
|
||||
|
||||
if(latches[1])
|
||||
page = (chr_bank[3] << 12) & (chr_size - 1);
|
||||
else
|
||||
page = (chr_bank[2] << 12) & (chr_size - 1);
|
||||
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom + page, 1);
|
||||
}
|
||||
|
||||
static void ppu_cb(uint16 pn) {
|
||||
/* Set/clear the appropriate latch */
|
||||
if((pn & 0x00FF) == 0xFD)
|
||||
latches[(pn >> 8) & 1] = 0;
|
||||
else
|
||||
latches[(pn >> 8) & 1] = 1;
|
||||
|
||||
fill_chr_map();
|
||||
}
|
||||
|
||||
static void init(void) {
|
||||
int i, j;
|
||||
|
||||
prg_banks = j = nes_prg_rom_size >> 8;
|
||||
chr_size = nes_chr_rom_size;
|
||||
prg_bank = 0;
|
||||
chr_bank[0] = chr_bank[1] = chr_bank[2] = chr_bank[3] = 0;
|
||||
latches[0] = latches[1] = mirror = 0;
|
||||
|
||||
/* Fill in the read map */
|
||||
for(i = 0xFF; i > 0x9F; --i) {
|
||||
nes_read_map[i] = nes_prg_rom + (--j << 8);
|
||||
}
|
||||
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
nes_read_map[i + 0x60] = nes_sram + (i << 8);
|
||||
nes_write_map[i + 0x60] = nes_sram + (i << 8);
|
||||
}
|
||||
|
||||
fill_prg_map();
|
||||
|
||||
/* Set up the PPU */
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom, 1);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom, 1);
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
nes_ppu_set_mmc2(&ppu_cb);
|
||||
}
|
||||
|
||||
static void reset(void) {
|
||||
/* Reset all the registers and the CPU's mapping */
|
||||
prg_bank = 0;
|
||||
chr_bank[0] = chr_bank[1] = chr_bank[2] = chr_bank[3] = 0;
|
||||
latches[0] = latches[1] = mirror = 0;
|
||||
fill_prg_map();
|
||||
|
||||
/* Reset the PPU */
|
||||
nes_ppu_set_patterntbl(0, nes_chr_rom, 1);
|
||||
nes_ppu_set_patterntbl(1, nes_chr_rom, 1);
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
nes_ppu_set_mmc2(&ppu_cb);
|
||||
}
|
||||
|
||||
static uint8 read_byte(void *cpu __UNUSED__, uint16 addr) {
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
return nes_ppu_readreg(addr & 0x0007);
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
return nes_mem_readreg(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return nes_read_map[addr >> 8][(uint8)addr];
|
||||
}
|
||||
|
||||
static void write_byte(void *cpu __UNUSED__, uint16 addr, uint8 val) {
|
||||
/* Is the write bound anywhere interesting? */
|
||||
switch(addr >> 12) {
|
||||
case 2:
|
||||
case 3:
|
||||
nes_ppu_writereg(addr & 0x0007, val);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(addr < 0x4020)
|
||||
nes_mem_writereg(addr, val);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
prg_bank = val;
|
||||
fill_prg_map();
|
||||
break;
|
||||
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
chr_bank[(addr >> 12) - 11] = val;
|
||||
fill_chr_map();
|
||||
break;
|
||||
|
||||
case 15:
|
||||
if((mirror = val & 0x01))
|
||||
nes_ppu_set_tblmirrors(0, 0, 1, 1);
|
||||
else
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
}
|
||||
|
||||
nes_write_map[addr >> 8][(uint8)addr] = val;
|
||||
}
|
||||
|
||||
static int write_cxt(FILE *fp) {
|
||||
uint8 data[4];
|
||||
|
||||
data[0] = 'M';
|
||||
data[1] = 'P';
|
||||
data[2] = 'P';
|
||||
data[3] = 'R';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(24, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
UINT16_TO_BUF(0, data);
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 0) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
|
||||
data[0] = prg_bank;
|
||||
data[1] = (latches[0]) | (latches[1] << 1) | (mirror << 2);
|
||||
data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
fwrite(chr_bank, 1, 4, fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cxt(const uint8 *buf) {
|
||||
uint16 ver;
|
||||
uint32 len;
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len != 24)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
BUF_TO_UINT16(buf + 8, ver);
|
||||
if(ver != 1)
|
||||
return -1;
|
||||
|
||||
/* Check the child pointer */
|
||||
if(buf[12] != 0 || buf[13] != 0 || buf[14] != 0 || buf[15] != 0)
|
||||
return -1;
|
||||
|
||||
/* Read in the registers */
|
||||
prg_bank = buf[16];
|
||||
latches[0] = buf[17] & 0x01;
|
||||
latches[1] = (buf[17] & 0x02) >> 1;
|
||||
mirror = (buf[17] & 0x04) >> 2;
|
||||
|
||||
chr_bank[0] = buf[20];
|
||||
chr_bank[1] = buf[21];
|
||||
chr_bank[2] = buf[22];
|
||||
chr_bank[3] = buf[23];
|
||||
|
||||
fill_prg_map();
|
||||
fill_chr_map();
|
||||
|
||||
if(mirror)
|
||||
nes_ppu_set_tblmirrors(0, 0, 1, 1);
|
||||
else
|
||||
nes_ppu_set_tblmirrors(0, 1, 0, 1);
|
||||
nes_ppu_set_mmc2(&ppu_cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32 cxt_len(void) {
|
||||
return 24;
|
||||
}
|
||||
|
||||
/* Mapper definition. */
|
||||
nes_mapper_t nes_mapper_9 = {
|
||||
9,
|
||||
&init,
|
||||
&reset,
|
||||
&read_byte,
|
||||
&write_byte,
|
||||
&write_cxt,
|
||||
&read_cxt,
|
||||
&cxt_len
|
||||
};
|
||||
+255
-27
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012, 2013 Lawrence Sebald
|
||||
Copyright (C) 2012, 2013, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -50,35 +50,68 @@ static const float PAL_FPS = 53.355f;
|
||||
/* From nesmem.c */
|
||||
extern uint16 nes_pad;
|
||||
|
||||
/* XXXX */
|
||||
extern int sms_console;
|
||||
|
||||
int nes_initialized = 0;
|
||||
Crab6502_t nescpu;
|
||||
|
||||
static int cycles_run, cycles_to_run;
|
||||
static int cycles_run, cycles_to_run, scanline;
|
||||
|
||||
static void nes_scanline(void);
|
||||
static void nes_single_step(void);
|
||||
static void nes_finish_frame(void);
|
||||
static void nes_finish_scanline(void);
|
||||
static int nes_current_scanline(void);
|
||||
static int nes_cycles_left(void);
|
||||
|
||||
/* Console declaration... */
|
||||
nes_t nes_cons = {
|
||||
{
|
||||
CONSOLE_NES,
|
||||
CONSOLE_NES,
|
||||
0,
|
||||
&nes_shutdown,
|
||||
&nes_reset,
|
||||
&nes_soft_reset,
|
||||
&nes_frame,
|
||||
&nes_load_state,
|
||||
&nes_save_state,
|
||||
&nes_mem_write_sram,
|
||||
&nes_button_pressed,
|
||||
&nes_button_released,
|
||||
&nes_ppu_framebuffer,
|
||||
&nes_ppu_framesize,
|
||||
&nes_ppu_activeframe,
|
||||
NULL,
|
||||
&nes_scanline,
|
||||
&nes_single_step,
|
||||
&nes_finish_frame,
|
||||
&nes_finish_scanline,
|
||||
&nes_current_scanline,
|
||||
&nes_cycles_left,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
int nes_init(int video_system) {
|
||||
(void)video_system;
|
||||
|
||||
if(nes_initialized)
|
||||
if(nes_cons._base.initialized)
|
||||
return -1;
|
||||
|
||||
sms_console = CONSOLE_NES;
|
||||
gui_set_console((console_t *)&nes_cons);
|
||||
|
||||
Crab6502_init(&nescpu);
|
||||
|
||||
nes_mem_init();
|
||||
nes_ppu_init();
|
||||
nes_apu_init();
|
||||
nes_initialized = 1;
|
||||
|
||||
sound_init(1, video_system);
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
nes_cons._base.initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nes_reset(void) {
|
||||
if(!nes_initialized)
|
||||
if(!nes_cons._base.initialized)
|
||||
return -1;
|
||||
|
||||
sound_reset_buffer();
|
||||
@@ -90,12 +123,14 @@ int nes_reset(void) {
|
||||
nes_ppu_reset();
|
||||
nes_apu_reset();
|
||||
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nes_soft_reset(void) {
|
||||
if(!nes_initialized)
|
||||
return;
|
||||
int nes_soft_reset(void) {
|
||||
if(!nes_cons._base.initialized)
|
||||
return 0;
|
||||
|
||||
sound_reset_buffer();
|
||||
|
||||
@@ -103,10 +138,14 @@ void nes_soft_reset(void) {
|
||||
Crab6502_reset(&nescpu);
|
||||
nes_ppu_reset();
|
||||
nes_apu_reset();
|
||||
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nes_shutdown(void) {
|
||||
if(!nes_initialized)
|
||||
if(!nes_cons._base.initialized)
|
||||
return -1;
|
||||
|
||||
nes_ppu_shutdown();
|
||||
@@ -114,16 +153,14 @@ int nes_shutdown(void) {
|
||||
nes_apu_shutdown();
|
||||
sound_shutdown();
|
||||
|
||||
nes_initialized = 0;
|
||||
nes_cons._base.initialized = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nes_frame(int run, int skip) {
|
||||
void nes_frame(int skip) {
|
||||
int i;
|
||||
|
||||
cycles_run = run;
|
||||
cycles_to_run = 113;
|
||||
|
||||
/* Lines 0-239 */
|
||||
for(i = 0; i < 240; ++i) {
|
||||
cycles_to_run += 113;
|
||||
@@ -157,7 +194,199 @@ int nes_frame(int run, int skip) {
|
||||
|
||||
nes_apu_execute(cycles_run);
|
||||
|
||||
return cycles_run - cycles_to_run;
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
static void nes_scanline(void) {
|
||||
cycles_to_run += 113;
|
||||
|
||||
if(scanline < 240) {
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
nes_ppu_execute(scanline, 0);
|
||||
}
|
||||
else if(scanline == 240) {
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
}
|
||||
else if(scanline == 241) {
|
||||
nes_ppu_vblank_in();
|
||||
|
||||
if(cycles_to_run == cycles_run)
|
||||
cycles_run += Crab6502_execute(&nescpu, 1);
|
||||
|
||||
if(nes_ppu_vblnmi_enabled())
|
||||
Crab6502_pulse_nmi(&nescpu);
|
||||
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
}
|
||||
else if(scanline < 261) {
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
}
|
||||
else if(scanline == 262) {
|
||||
nes_ppu_vblank_out();
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
nes_apu_execute(cycles_run);
|
||||
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
++scanline;
|
||||
}
|
||||
|
||||
static void nes_finish_frame(void) {
|
||||
int i = scanline;
|
||||
|
||||
if(scanline < 240)
|
||||
goto line_0;
|
||||
else if(scanline == 240)
|
||||
goto line_240;
|
||||
else if(scanline == 241)
|
||||
goto line_241;
|
||||
else if(scanline >= 242 && scanline < 261)
|
||||
goto line_242;
|
||||
else if(scanline == 261)
|
||||
goto line_261;
|
||||
|
||||
line_0:
|
||||
/* Lines 0-239 */
|
||||
for(; i < 240; ++i) {
|
||||
cycles_to_run += 113;
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
nes_ppu_execute(i, 0);
|
||||
}
|
||||
|
||||
line_240:
|
||||
/* Line 240 */
|
||||
cycles_to_run += 113;
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
|
||||
line_241:
|
||||
/* Line 241 */
|
||||
nes_ppu_vblank_in();
|
||||
if(cycles_to_run == cycles_run)
|
||||
cycles_run += Crab6502_execute(&nescpu, 1);
|
||||
if(nes_ppu_vblnmi_enabled())
|
||||
Crab6502_pulse_nmi(&nescpu);
|
||||
cycles_to_run += 113;
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
|
||||
line_242:
|
||||
/* Lines 242-260 */
|
||||
for(i = 242; i < 261; ++i) {
|
||||
cycles_to_run += 113;
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
}
|
||||
|
||||
line_261:
|
||||
/* Line 261 (aka, line -1) */
|
||||
nes_ppu_vblank_out();
|
||||
cycles_to_run += 113;
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
|
||||
nes_apu_execute(cycles_run);
|
||||
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
static void nes_single_step(void) {
|
||||
/* If we're at the start of a frame, set things up for the first line. */
|
||||
if(!cycles_to_run)
|
||||
cycles_to_run += 113;
|
||||
|
||||
/* Run the one instruction we need to run. */
|
||||
cycles_run += Crab6502_execute(&nescpu, 1);
|
||||
|
||||
/* See if we finished a scanline... */
|
||||
if(cycles_run >= cycles_to_run) {
|
||||
/* Which scanline did we just finish? */
|
||||
if(scanline < 240) {
|
||||
nes_ppu_execute(scanline, 0);
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
}
|
||||
else if(scanline == 240) {
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
nes_ppu_vblank_in();
|
||||
if(nes_ppu_vblnmi_enabled())
|
||||
Crab6502_pulse_nmi(&nescpu);
|
||||
}
|
||||
else if(scanline < 260) {
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
}
|
||||
else if(scanline == 260) {
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
nes_ppu_vblank_out();
|
||||
}
|
||||
else {
|
||||
nes_apu_execute(cycles_run);
|
||||
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void nes_finish_scanline(void) {
|
||||
/* Make sure we have something to do... */
|
||||
if(cycles_run >= cycles_to_run)
|
||||
return;
|
||||
|
||||
/* Run instructions to the end of the line. */
|
||||
cycles_run += Crab6502_execute(&nescpu, cycles_to_run - cycles_run);
|
||||
|
||||
/* We should have finished a scanline, so do whatever we need to for that
|
||||
particular line. */
|
||||
if(cycles_run >= cycles_to_run) {
|
||||
/* Which scanline did we just finish? */
|
||||
if(scanline < 240) {
|
||||
nes_ppu_execute(scanline, 0);
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
}
|
||||
else if(scanline == 240) {
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
nes_ppu_vblank_in();
|
||||
if(nes_ppu_vblnmi_enabled())
|
||||
Crab6502_pulse_nmi(&nescpu);
|
||||
}
|
||||
else if(scanline < 260) {
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
}
|
||||
else if(scanline == 260) {
|
||||
++scanline;
|
||||
cycles_to_run += 113;
|
||||
nes_ppu_vblank_out();
|
||||
}
|
||||
else {
|
||||
nes_apu_execute(cycles_run);
|
||||
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int nes_current_scanline(void) {
|
||||
return scanline;
|
||||
}
|
||||
|
||||
static int nes_cycles_left(void) {
|
||||
return cycles_to_run - cycles_run;
|
||||
}
|
||||
|
||||
int nes_cycles_elapsed(void) {
|
||||
@@ -301,8 +530,8 @@ int nes_save_state(const char *filename)
|
||||
FILE *fp;
|
||||
uint8 data[4];
|
||||
|
||||
if(nes_initialized == 0)
|
||||
/* This shouldn't happen.... */
|
||||
if(!nes_cons._base.initialized)
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
|
||||
fp = fopen(filename, "wb");
|
||||
@@ -408,10 +637,9 @@ int nes_load_state(const char *filename)
|
||||
uint8 *ptr;
|
||||
size_t read;
|
||||
|
||||
if(!nes_initialized) {
|
||||
if(!nes_cons._base.initialized)
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
}
|
||||
|
||||
fp = fopen(filename, "rb");
|
||||
if(!fp)
|
||||
|
||||
+11
-5
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012, 2013 Lawrence Sebald
|
||||
Copyright (C) 2012, 2013, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -21,16 +21,15 @@
|
||||
#define NES_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
#include "console.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
#define CONSOLE_NES 6
|
||||
|
||||
extern int nes_init(int video_system);
|
||||
extern int nes_reset(void);
|
||||
extern void nes_soft_reset(void);
|
||||
extern int nes_soft_reset(void);
|
||||
extern int nes_shutdown(void);
|
||||
extern int nes_frame(int run, int skip);
|
||||
extern void nes_frame(int skip);
|
||||
|
||||
extern int nes_cycles_elapsed(void);
|
||||
extern void nes_assert_irq(void);
|
||||
@@ -56,6 +55,13 @@ extern void nes_button_released(int player, int button);
|
||||
extern int nes_save_state(const char *filename);
|
||||
extern int nes_load_state(const char *filename);
|
||||
|
||||
/* Console definition. */
|
||||
typedef struct crabemu_nes {
|
||||
console_t _base;
|
||||
} nes_t;
|
||||
|
||||
extern nes_t nes_cons;
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !NES_H */
|
||||
|
||||
@@ -77,11 +77,12 @@ static uint8 *rom_data = NULL;
|
||||
extern Crab6502_t nescpu;
|
||||
|
||||
/* Various mappers */
|
||||
extern nes_mapper_t nes_mapper_0;
|
||||
extern nes_mapper_t nes_mapper_1;
|
||||
extern nes_mapper_t nes_mapper_0, nes_mapper_1, nes_mapper_2, nes_mapper_3;
|
||||
extern nes_mapper_t nes_mapper_7, nes_mapper_9, nes_mapper_66;
|
||||
|
||||
static nes_mapper_t *mappers[] = {
|
||||
&nes_mapper_0, &nes_mapper_1,
|
||||
&nes_mapper_0, &nes_mapper_1, &nes_mapper_2, &nes_mapper_3, &nes_mapper_7,
|
||||
&nes_mapper_9, &nes_mapper_66,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
+88
-16
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012, 2013 Lawrence Sebald
|
||||
Copyright (C) 2012, 2013, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -50,6 +50,7 @@ static int ppu_patterns_rom = 0;
|
||||
static nes_ppu_pattern_t ppu_patterns[512];
|
||||
|
||||
static pixel_t *ppu_framebuffer;
|
||||
static void (*mmc2_cb)(uint16 pn);
|
||||
|
||||
struct ppu_spr {
|
||||
int pattern;
|
||||
@@ -147,6 +148,17 @@ void *nes_ppu_framebuffer(void) {
|
||||
return ppu_framebuffer;
|
||||
}
|
||||
|
||||
void nes_ppu_framesize(uint32_t *x, uint32_t *y) {
|
||||
*x = 256;
|
||||
*y = 256;
|
||||
}
|
||||
|
||||
void nes_ppu_activeframe(uint32_t *x, uint32_t *y, uint32_t *w, uint32_t *h) {
|
||||
*x = *y = 0;
|
||||
*w = 256;
|
||||
*h = 240;
|
||||
}
|
||||
|
||||
void nes_ppu_writereg(int reg, uint8 val) {
|
||||
uint16 addr;
|
||||
|
||||
@@ -255,7 +267,7 @@ void nes_ppu_writereg(int reg, uint8 val) {
|
||||
|
||||
uint8 nes_ppu_readreg(int reg) {
|
||||
uint8 rv = ppu_regs[reg & 0x07];
|
||||
uint16 addr;
|
||||
uint16 addr, addr2;
|
||||
|
||||
switch(reg & 0x07) {
|
||||
case 2:
|
||||
@@ -267,7 +279,7 @@ uint8 nes_ppu_readreg(int reg) {
|
||||
case 7:
|
||||
/* Grab the address and update what we're looking at for next time
|
||||
we try to read/write. */
|
||||
addr = ppu_v & 0x3FFF;
|
||||
addr = addr2 = ppu_v & 0x3FFF;
|
||||
ppu_v += (ppu_regs[0] & 0x04) ? 32 : 1;
|
||||
|
||||
/* See where we're reading... */
|
||||
@@ -282,18 +294,26 @@ uint8 nes_ppu_readreg(int reg) {
|
||||
if(ppu_regs[1] & 0x01)
|
||||
rv &= 0x30;
|
||||
|
||||
/* Mask the address for filling the buffer below. */
|
||||
/* Mask the address and fill the buffer. */
|
||||
addr &= 0x2FFF;
|
||||
ppu_buffer = ppu_map[(addr >> 8)][(uint8)addr];
|
||||
}
|
||||
else {
|
||||
/* Make sure we handle any sort of mirroring caused by accessing
|
||||
0x3000-0x3EFF. */
|
||||
if(addr >= 0x3000)
|
||||
addr &= 0x2FFF;
|
||||
rv = ppu_buffer;
|
||||
}
|
||||
addr2 &= 0x2FFF;
|
||||
|
||||
ppu_buffer = ppu_map[(addr >> 8)][(uint8)addr];
|
||||
rv = ppu_buffer;
|
||||
ppu_buffer = ppu_map[(addr2 >> 8)][(uint8)addr2];
|
||||
|
||||
/* Grumble... grumble... Stupid MMC2... */
|
||||
if(mmc2_cb) {
|
||||
if((addr >= 0x0FD0 && addr < 0x0FF0) ||
|
||||
(addr >= 0x1FD0 && addr < 0x1FF0))
|
||||
mmc2_cb(addr >> 4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -400,7 +420,7 @@ static void ppu_calc_spr(void) {
|
||||
pal = (attr & 0x03) << 2;
|
||||
tex = 0;
|
||||
bg = 0;
|
||||
|
||||
|
||||
if(attr & 0x80)
|
||||
tex = 2;
|
||||
if(attr & 0x40)
|
||||
@@ -515,6 +535,12 @@ static void ppu_bg_draw(int line __UNUSED__, uint8 *px,
|
||||
RENDER_BG_PIXEL();
|
||||
RENDER_BG_PIXEL();
|
||||
|
||||
/* If we've got an MMC2 or an MMC4 and we need to set the latches, do
|
||||
so now. */
|
||||
if(mmc2_cb &&
|
||||
(pn == 0x0FD || pn == 0x0FE || pn == 0x1FD || pn == 0x1FE))
|
||||
mmc2_cb(pn);
|
||||
|
||||
/* We're done with this tile, move onto the next one */
|
||||
if(xc != 31) {
|
||||
++nt;
|
||||
@@ -596,6 +622,12 @@ static void ppu_bg_skip(int line, uint8 bg_pixels[256 + 16]) {
|
||||
SKIP_BG_PIXEL();
|
||||
SKIP_BG_PIXEL();
|
||||
|
||||
/* If we've got an MMC2 or an MMC4 and we need to set the latches, do
|
||||
so now. */
|
||||
if(mmc2_cb &&
|
||||
(pn == 0x0FD || pn == 0x0FE || pn == 0x1FD || pn == 0x1FE))
|
||||
mmc2_cb(pn);
|
||||
|
||||
/* We're done with this tile, move onto the next one */
|
||||
if(xc != 31) {
|
||||
++nt;
|
||||
@@ -662,7 +694,7 @@ static void ppu_bg_skip(int line, uint8 bg_pixels[256 + 16]) {
|
||||
|
||||
static void ppu_spr_draw(int line, uint8 *px, uint8 bg_pixels[256 + 16]) {
|
||||
static uint8 col_tab[256 + 16];
|
||||
int i = 0, x, pal;
|
||||
int i = 0, x, pal, pn;
|
||||
nes_ppu_pattern_t *pat;
|
||||
uint8 *pixels;
|
||||
uint8 pixel;
|
||||
@@ -674,11 +706,12 @@ static void ppu_spr_draw(int line, uint8 *px, uint8 bg_pixels[256 + 16]) {
|
||||
in how we deal with it. */
|
||||
if(sprs[line][0].spr_num == 0) {
|
||||
i = 1;
|
||||
pat = ppu_patterns + sprs[line][0].pattern;
|
||||
pn = sprs[line][0].pattern;
|
||||
pat = ppu_patterns + pn;
|
||||
|
||||
/* Update the cache if the pattern is marked as dirty */
|
||||
if(pat->dirty)
|
||||
nes_ppu_update_cache(sprs[line][0].pattern);
|
||||
nes_ppu_update_cache(pn);
|
||||
|
||||
pixels = &pat->texture[sprs[line][0].tex][sprs[line][0].y << 3];
|
||||
x = sprs[line][0].x + 8;
|
||||
@@ -704,15 +737,22 @@ static void ppu_spr_draw(int line, uint8 *px, uint8 bg_pixels[256 + 16]) {
|
||||
RENDER_SPR_PIXEL_FG_SPR0();
|
||||
RENDER_SPR_PIXEL_FG_SPR0();
|
||||
}
|
||||
|
||||
/* If we've got an MMC2 or an MMC4 and we need to set the latches, do
|
||||
so now. */
|
||||
if(mmc2_cb &&
|
||||
(pn == 0x0FD || pn == 0x0FE || pn == 0x1FD || pn == 0x1FE))
|
||||
mmc2_cb(pn);
|
||||
}
|
||||
|
||||
/* Go through all the sprites on this line. */
|
||||
for(; i < 8 && sprs[line][i].spr_num != 0xFF; ++i) {
|
||||
pat = ppu_patterns + sprs[line][i].pattern;
|
||||
pn = sprs[line][i].pattern;
|
||||
pat = ppu_patterns + pn;
|
||||
|
||||
/* Update the cache if the pattern is marked as dirty */
|
||||
if(pat->dirty)
|
||||
nes_ppu_update_cache(sprs[line][i].pattern);
|
||||
nes_ppu_update_cache(pn);
|
||||
|
||||
pixels = &pat->texture[sprs[line][i].tex][sprs[line][i].y << 3];
|
||||
x = sprs[line][i].x + 8;
|
||||
@@ -738,6 +778,12 @@ static void ppu_spr_draw(int line, uint8 *px, uint8 bg_pixels[256 + 16]) {
|
||||
RENDER_SPR_PIXEL_FG();
|
||||
RENDER_SPR_PIXEL_FG();
|
||||
}
|
||||
|
||||
/* If we've got an MMC2 or an MMC4 and we need to set the latches, do
|
||||
so now. */
|
||||
if(mmc2_cb &&
|
||||
(pn == 0x0FD || pn == 0x0FE || pn == 0x1FD || pn == 0x1FE))
|
||||
mmc2_cb(pn);
|
||||
}
|
||||
|
||||
/* Set the sprite overflow bit, if appropriate. */
|
||||
@@ -755,15 +801,18 @@ static void ppu_spr_skip(int line, uint8 bg_pixels[256 + 16]) {
|
||||
int x;
|
||||
nes_ppu_pattern_t *pat;
|
||||
uint8 *pixels;
|
||||
int pn, i = 0;
|
||||
|
||||
/* See if sprite 0 is on this line, since its really the only sprite we care
|
||||
about for now... */
|
||||
if(sprs[line][0].spr_num == 0) {
|
||||
pat = ppu_patterns + sprs[line][0].pattern;
|
||||
i = 1;
|
||||
pn = sprs[line][0].pattern;
|
||||
pat = ppu_patterns + pn;
|
||||
|
||||
/* Update the cache if the pattern is marked as dirty */
|
||||
if(pat->dirty)
|
||||
nes_ppu_update_cache(sprs[line][0].pattern);
|
||||
nes_ppu_update_cache(pn);
|
||||
|
||||
pixels = &pat->texture[sprs[line][0].tex][sprs[line][0].y << 3];
|
||||
x = sprs[line][0].x + 8;
|
||||
@@ -776,6 +825,23 @@ static void ppu_spr_skip(int line, uint8 bg_pixels[256 + 16]) {
|
||||
SKIP_SPR_PIXEL_SPR0();
|
||||
SKIP_SPR_PIXEL_SPR0();
|
||||
SKIP_SPR_PIXEL_SPR0();
|
||||
|
||||
/* If we've got an MMC2 or an MMC4 and we need to set the latches, do
|
||||
so now. */
|
||||
if(mmc2_cb &&
|
||||
(pn == 0x0FD || pn == 0x0FE || pn == 0x1FD || pn == 0x1FE))
|
||||
mmc2_cb(pn);
|
||||
}
|
||||
|
||||
/* We only have to look at the other sprites if we've got either an MMC2 or
|
||||
an MMC4... */
|
||||
if(mmc2_cb) {
|
||||
for(; i < 8 && sprs[line][i].spr_num != 0xFF; ++i) {
|
||||
pn = sprs[line][i].pattern;
|
||||
|
||||
if(pn == 0x0FD || pn == 0x0FE || pn == 0x1FD || pn == 0x1FE)
|
||||
mmc2_cb(pn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the sprite overflow bit, if appropriate. */
|
||||
@@ -910,6 +976,7 @@ int nes_ppu_init(void) {
|
||||
ppu_x = 0;
|
||||
ppu_oam_addr = 0;
|
||||
ppu_patterns_rom = 0;
|
||||
mmc2_cb = NULL;
|
||||
|
||||
for(i = 0; i < 256; ++i) {
|
||||
ppu_lut[i] = (i & 0x01) | ((i & 0x02) << 1) | ((i & 0x04) << 2) |
|
||||
@@ -956,6 +1023,7 @@ int nes_ppu_reset(void) {
|
||||
ppu_x = 0;
|
||||
ppu_oam_addr = 0;
|
||||
ppu_patterns_rom = 0;
|
||||
mmc2_cb = NULL;
|
||||
|
||||
memset(ppu_framebuffer, 0, 256 * 256 * sizeof(pixel_t));
|
||||
gui_set_aspect(16.0f, 15.0f);
|
||||
@@ -963,6 +1031,10 @@ int nes_ppu_reset(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nes_ppu_set_mmc2(void (*cb)(uint16 pn)) {
|
||||
mmc2_cb = cb;
|
||||
}
|
||||
|
||||
int nes_ppu_shutdown(void) {
|
||||
#ifdef _arch_dreamcast
|
||||
/* Put the framebuffer back into P1. */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012, 2013 Lawrence Sebald
|
||||
Copyright (C) 2012, 2013, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -37,7 +37,11 @@ extern void nes_ppu_update_cache(int pat);
|
||||
extern const nes_ppu_pattern_t *nes_ppu_fetch_pattern(int pat);
|
||||
extern void nes_ppu_fetch_bg_pal(uint8 pal[16]);
|
||||
extern void nes_ppu_fetch_spr_pal(uint8 pal[16]);
|
||||
|
||||
extern void *nes_ppu_framebuffer(void);
|
||||
extern void nes_ppu_framesize(uint32_t *x, uint32_t *y);
|
||||
extern void nes_ppu_activeframe(uint32_t *x, uint32_t *y, uint32_t *w,
|
||||
uint32_t *h);
|
||||
|
||||
extern void nes_ppu_writereg(int reg, uint8 value);
|
||||
extern uint8 nes_ppu_readreg(int reg);
|
||||
@@ -52,6 +56,7 @@ extern void nes_ppu_set_tblmirrors(int n1, int n2, int n3, int n4);
|
||||
extern void nes_ppu_set_patterntbl(int tbl, uint8 *ptr, int rom);
|
||||
|
||||
extern void nes_ppu_execute(int line, int skip);
|
||||
extern void nes_ppu_set_mmc2(void (*cb)(uint16 pn));
|
||||
|
||||
extern int nes_ppu_init(void);
|
||||
extern int nes_ppu_reset(void);
|
||||
|
||||
+75
-55
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2009, 2012 Lawrence Sebald
|
||||
Copyright (C) 2009, 2012, 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -22,66 +22,55 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cheats.h"
|
||||
#include "list.h"
|
||||
#include "smsmem.h"
|
||||
|
||||
cc_dlist_t *sms_active_cheats = NULL;
|
||||
|
||||
int sms_cheats_enabled = 0;
|
||||
static int sms_cheats_initted = 0;
|
||||
static int count = 0;
|
||||
|
||||
TAILQ_HEAD(cheat_queue, smscheat_s) sms_cheats;
|
||||
|
||||
extern uint8 *sms_write_map[256];
|
||||
extern int sms_bios_active;
|
||||
|
||||
int sms_cheat_init(void) {
|
||||
if(sms_active_cheats)
|
||||
if(sms_cheats_initted)
|
||||
return 0;
|
||||
|
||||
sms_active_cheats = cc_dlist_create();
|
||||
|
||||
if(sms_active_cheats)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
TAILQ_INIT(&sms_cheats);
|
||||
sms_cheats_initted = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sms_cheat_shutdown(void) {
|
||||
cc_dlist_node_t *i;
|
||||
|
||||
if(!sms_active_cheats)
|
||||
return;
|
||||
|
||||
CC_DLIST_FOREACH(sms_active_cheats, i) {
|
||||
free(i->data);
|
||||
}
|
||||
|
||||
cc_dlist_destroy(sms_active_cheats);
|
||||
|
||||
sms_cheats_enabled = 0;
|
||||
sms_active_cheats = NULL;
|
||||
sms_cheat_reset();
|
||||
sms_cheats_initted = 0;
|
||||
}
|
||||
|
||||
void sms_cheat_reset(void) {
|
||||
cc_dlist_node_t *i;
|
||||
sms_cheat_t *i, *next;
|
||||
|
||||
if(!sms_active_cheats)
|
||||
if(!sms_cheats_initted)
|
||||
return;
|
||||
|
||||
sms_cheats_enabled = 0;
|
||||
|
||||
while((i = sms_active_cheats->head)) {
|
||||
free(i->data);
|
||||
cc_dlist_remove(sms_active_cheats, i);
|
||||
i = TAILQ_FIRST(&sms_cheats);
|
||||
while(i) {
|
||||
next = TAILQ_NEXT(i, qentry);
|
||||
TAILQ_REMOVE(&sms_cheats, i, qentry);
|
||||
free(i);
|
||||
i = next;
|
||||
}
|
||||
|
||||
sms_cheats_enabled = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void sms_cheat_frame(void) {
|
||||
cc_dlist_node_t *i;
|
||||
sms_cheat_t *c;
|
||||
sms_cheat_t *i;
|
||||
uint16 addr;
|
||||
uint8 data;
|
||||
|
||||
if(!sms_active_cheats)
|
||||
return;
|
||||
|
||||
if(!sms_cheats_enabled)
|
||||
return;
|
||||
|
||||
@@ -91,12 +80,10 @@ void sms_cheat_frame(void) {
|
||||
if(sms_bios_active)
|
||||
return;
|
||||
|
||||
CC_DLIST_FOREACH(sms_active_cheats, i) {
|
||||
c = (sms_cheat_t *)i->data;
|
||||
|
||||
if(c->enabled) {
|
||||
addr = (uint16)(c->ar_code >> 8);
|
||||
data = (uint8)c->ar_code;
|
||||
TAILQ_FOREACH(i, &sms_cheats, qentry) {
|
||||
if(i->enabled){
|
||||
addr = (uint16)(i->ar_code >> 8);
|
||||
data = (uint8)i->ar_code;
|
||||
|
||||
sms_write_map[addr >> 8][(uint8)addr] = data;
|
||||
}
|
||||
@@ -104,10 +91,42 @@ void sms_cheat_frame(void) {
|
||||
}
|
||||
|
||||
int sms_cheat_add(sms_cheat_t *c) {
|
||||
return cc_dlist_insert_tail(sms_active_cheats, c);
|
||||
TAILQ_INSERT_TAIL(&sms_cheats, c, qentry);
|
||||
++count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sms_cheat_read(const char *fn) {
|
||||
int sms_cheat_remove(int index) {
|
||||
sms_cheat_t *i;
|
||||
|
||||
TAILQ_FOREACH(i, &sms_cheats, qentry) {
|
||||
if(!index--) {
|
||||
TAILQ_REMOVE(&sms_cheats, i, qentry);
|
||||
free(i);
|
||||
--count;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sms_cheat_count(void) {
|
||||
return count;
|
||||
}
|
||||
|
||||
sms_cheat_t *sms_cheat_get(int index) {
|
||||
sms_cheat_t *i;
|
||||
|
||||
TAILQ_FOREACH(i, &sms_cheats, qentry) {
|
||||
if(!index--)
|
||||
return i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sms_cheat_read(const char *fn) {
|
||||
FILE *fp;
|
||||
char linebuf[256], str[64];
|
||||
size_t len;
|
||||
@@ -116,7 +135,7 @@ void sms_cheat_read(const char *fn) {
|
||||
|
||||
fp = fopen(fn, "r");
|
||||
if(!fp)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
while(fgets(linebuf, 256, fp)) {
|
||||
/* Ignore shell-style comments. */
|
||||
@@ -153,30 +172,31 @@ void sms_cheat_read(const char *fn) {
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sms_cheat_write(const char *fn) {
|
||||
int sms_cheat_write(const char *fn) {
|
||||
FILE *fp;
|
||||
sms_cheat_t *c;
|
||||
cc_dlist_node_t *i;
|
||||
sms_cheat_t *i;
|
||||
|
||||
if(!sms_active_cheats || !sms_active_cheats->count)
|
||||
return;
|
||||
if(!sms_cheats_initted || TAILQ_EMPTY(&sms_cheats))
|
||||
return 0;
|
||||
|
||||
fp = fopen(fn, "w");
|
||||
if(!fp)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
fprintf(fp, "# Cheats file generated by CrabEmu\n");
|
||||
|
||||
CC_DLIST_FOREACH(sms_active_cheats, i) {
|
||||
c = (sms_cheat_t *)i->data;
|
||||
|
||||
fprintf(fp, "%04X-%04X %s", (uint16)(c->ar_code >> 16),
|
||||
(uint16)c->ar_code, c->desc);
|
||||
TAILQ_FOREACH(i, &sms_cheats, qentry) {
|
||||
fprintf(fp, "%04X-%04X %s\n", (uint16)(i->ar_code >> 16),
|
||||
(uint16)i->ar_code, i->desc);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sms_cheat_enable(void) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2009 Lawrence Sebald
|
||||
Copyright (C) 2009, 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -21,10 +21,12 @@
|
||||
#define CHEATS_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
#include "queue.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
typedef struct smscheat_s {
|
||||
TAILQ_ENTRY(smscheat_s) qentry;
|
||||
uint32 ar_code;
|
||||
char desc[64];
|
||||
int enabled;
|
||||
@@ -37,8 +39,11 @@ extern void sms_cheat_reset(void);
|
||||
extern void sms_cheat_frame(void);
|
||||
|
||||
extern int sms_cheat_add(sms_cheat_t *c);
|
||||
extern void sms_cheat_read(const char *fn);
|
||||
extern void sms_cheat_write(const char *fn);
|
||||
extern int sms_cheat_remove(int index);
|
||||
extern int sms_cheat_count(void);
|
||||
extern sms_cheat_t *sms_cheat_get(int index);
|
||||
extern int sms_cheat_read(const char *fn);
|
||||
extern int sms_cheat_write(const char *fn);
|
||||
|
||||
extern void sms_cheat_enable(void);
|
||||
extern void sms_cheat_disable(void);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#ifndef MAPPER_NONE_H
|
||||
#define MAPEPR_NONE_H
|
||||
#define MAPPER_NONE_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#ifndef MAPPER_SEGA_H
|
||||
#define MAPEPR_SEGA_H
|
||||
#define MAPPER_SEGA_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2008, 2012 Lawrence Sebald
|
||||
Copyright (C) 2008, 2012, 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -40,7 +40,7 @@ typedef struct srom_s {
|
||||
uint32 mapper;
|
||||
} special_rom_t;
|
||||
|
||||
static const int rom_count = 26;
|
||||
static const int rom_count = 27;
|
||||
static special_rom_t romlist[] = {
|
||||
{ 0xBF3A0EDC, 0x092F29D6, SMS_MAPPER_CASTLE }, /* The Castle - SG-1000 */
|
||||
{ 0x86429577, 0x4ED45BDA, SMS_MAPPER_93C46 }, /* Nomo's World Series Baseball */
|
||||
@@ -68,6 +68,7 @@ static special_rom_t romlist[] = {
|
||||
{ 0x9CABE756, 0xAAAC12CF, SMS_MAPPER_TW_MSX_TYPE_B }, /* Rally-X */
|
||||
{ 0x7C5AC4A0, 0xD2EDD329, SMS_MAPPER_TW_MSX_TYPE_B }, /* Road Fighter */
|
||||
{ 0x6E11F0D2, 0x72542786, SMS_MAPPER_NONE }, /* Monaco GP */
|
||||
{ 0x1282E24F, 0x704F6A61, SMS_MAPPER_NONE }, /* Lander 2 (homebrew) */
|
||||
};
|
||||
|
||||
uint32 sms_find_mapper(const uint8 *rom, uint32 len, uint32 *rcrc,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2009 Lawrence Sebald
|
||||
Copyright (C) 2009, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
#ifdef ENABLE_SDSC_TERMINAL
|
||||
|
||||
extern int sms_console;
|
||||
extern uint8 *sms_read_map[256];
|
||||
|
||||
static char console[25][80 * 2];
|
||||
@@ -128,10 +127,10 @@ static void sdsc_write_format_num(void) {
|
||||
if(cur_data_param1 < 0x10) {
|
||||
num = smsvdp.regs[cur_data_param1];
|
||||
}
|
||||
else if(sms_console == CONSOLE_SMS) {
|
||||
else if(sms_cons._base.console_type == CONSOLE_SMS) {
|
||||
num = smsvdp.cram[cur_data_param1 - 0x10];
|
||||
}
|
||||
else if(sms_console == CONSOLE_GG) {
|
||||
else if(sms_cons._base.console_type == CONSOLE_GG) {
|
||||
addr = (cur_data_param1 - 0x10) << 1;
|
||||
num = smsvdp.cram[addr] | (smsvdp.cram[addr + 1] << 8);
|
||||
bits = 16;
|
||||
|
||||
+514
-122
@@ -1,20 +1,21 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2014,
|
||||
2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -42,16 +43,14 @@
|
||||
#include "sound.h"
|
||||
#include "cheats.h"
|
||||
#include "sdscterminal.h"
|
||||
#include "console.h"
|
||||
|
||||
uint16 sms_pad = 0xFFFF;
|
||||
int sms_psg_enabled = 1;
|
||||
int sms_ym2413_enabled = 1;
|
||||
int sms_initialized = 0;
|
||||
sn76489_t psg;
|
||||
int sms_console = CONSOLE_SMS;
|
||||
int sms_region = SMS_REGION_EXPORT;
|
||||
int sms_cycles_run;
|
||||
int sms_cycles_to_run;
|
||||
YM2413 *sms_fm = NULL;
|
||||
|
||||
uint32 psg_samples[313];
|
||||
|
||||
@@ -68,8 +67,63 @@ static const float PAL_CLOCKS_PER_SAMPLE = 5.02677579365f;
|
||||
|
||||
extern uint8 sms_gg_regs[7];
|
||||
extern int sms_bios_active;
|
||||
extern int sms_control_type[2];
|
||||
extern uint32 sms_gfxbd_data[2];
|
||||
|
||||
int sms_init(int video_system, int region) {
|
||||
static int cycles_run, cycles_to_run, scanline;
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
static void sms_frame(int);
|
||||
static void sms_scanline(void);
|
||||
static void sms_single_step(void);
|
||||
static void sms_finish_frame(void);
|
||||
static void sms_finish_scanline(void);
|
||||
#endif
|
||||
static int sms_current_scanline(void);
|
||||
static int sms_cycles_left(void);
|
||||
static void sms_set_control(int player, int control);
|
||||
|
||||
/* Console declaration... */
|
||||
sms_t sms_cons = {
|
||||
{
|
||||
CONSOLE_SMS,
|
||||
CONSOLE_SMS,
|
||||
0,
|
||||
&sms_shutdown,
|
||||
&sms_reset,
|
||||
&sms_soft_reset,
|
||||
#ifndef _arch_dreamcast
|
||||
&sms_frame,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
&sms_load_state,
|
||||
&sms_save_state,
|
||||
&sms_write_cartram_to_file,
|
||||
&sms_button_pressed,
|
||||
&sms_button_released,
|
||||
&sms_vdp_framebuffer,
|
||||
&sms_vdp_framesize,
|
||||
&sms_vdp_activeframe,
|
||||
&sms_cheat_write,
|
||||
#ifndef _arch_dreamcast
|
||||
&sms_scanline,
|
||||
&sms_single_step,
|
||||
&sms_finish_frame,
|
||||
&sms_finish_scanline,
|
||||
#else
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
#endif
|
||||
&sms_current_scanline,
|
||||
&sms_cycles_left,
|
||||
&sms_set_control
|
||||
}
|
||||
};
|
||||
|
||||
int sms_init(int video_system, int region, int borders) {
|
||||
int i;
|
||||
float tmp;
|
||||
|
||||
@@ -90,7 +144,7 @@ int sms_init(int video_system, int region) {
|
||||
|
||||
sn76489_init(&psg, NTSC_Z80_CLOCK, 44100.0f,
|
||||
SN76489_NOISE_BITS_SMS, SN76489_NOISE_TAPPED_SMS);
|
||||
YM2413Init(1, NTSC_Z80_CLOCK, 44100);
|
||||
sms_fm = ym2413_init(NTSC_Z80_CLOCK, 44100);
|
||||
}
|
||||
else {
|
||||
tmp = PAL_Z80_CLOCK / PSG_DIVISOR / PAL_FPS / PAL_LINES_PER_FRAME /
|
||||
@@ -98,7 +152,7 @@ int sms_init(int video_system, int region) {
|
||||
|
||||
for(i = 0; i < PAL_LINES_PER_FRAME; ++i) {
|
||||
psg_samples[i] = (uint32) (tmp * (i + 1)) -
|
||||
(uint32) (tmp * i);
|
||||
(uint32) (tmp * i);
|
||||
}
|
||||
|
||||
/* We need 882 samples per frame @ 44100 Hz, 50fps. */
|
||||
@@ -106,29 +160,32 @@ int sms_init(int video_system, int region) {
|
||||
|
||||
sn76489_init(&psg, PAL_Z80_CLOCK, 44100.0f,
|
||||
SN76489_NOISE_BITS_SMS, SN76489_NOISE_TAPPED_SMS);
|
||||
YM2413Init(1, PAL_Z80_CLOCK, 44100);
|
||||
sms_fm = ym2413_init(PAL_Z80_CLOCK, 44100);
|
||||
}
|
||||
|
||||
sms_region = region;
|
||||
|
||||
gui_set_console((console_t *)&sms_cons);
|
||||
|
||||
sms_cheat_init();
|
||||
|
||||
sms_mem_init();
|
||||
sms_vdp_init(video_system);
|
||||
sms_vdp_init(video_system, borders);
|
||||
sms_z80_init();
|
||||
|
||||
sound_init(2, video_system);
|
||||
|
||||
sms_sdsc_reset();
|
||||
YM2413ResetChip(0);
|
||||
ym2413_reset(sms_fm);
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
sms_initialized = 1;
|
||||
sms_cons._base.initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sms_reset() {
|
||||
if(sms_initialized == 0)
|
||||
int sms_reset(void) {
|
||||
if(sms_cons._base.initialized == 0)
|
||||
return 0;
|
||||
|
||||
if(sms_region & SMS_VIDEO_NTSC) {
|
||||
@@ -140,7 +197,7 @@ int sms_reset() {
|
||||
SN76489_NOISE_BITS_SMS, SN76489_NOISE_TAPPED_SMS);
|
||||
}
|
||||
|
||||
YM2413ResetChip(0);
|
||||
ym2413_reset(sms_fm);
|
||||
|
||||
sound_reset_buffer();
|
||||
|
||||
@@ -154,14 +211,16 @@ int sms_reset() {
|
||||
|
||||
sms_sdsc_reset();
|
||||
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sms_soft_reset() {
|
||||
if(sms_initialized == 0)
|
||||
return;
|
||||
int sms_soft_reset(void) {
|
||||
if(sms_cons._base.initialized == 0)
|
||||
return 0;
|
||||
|
||||
YM2413ResetChip(0);
|
||||
ym2413_reset(sms_fm);
|
||||
|
||||
sound_reset_buffer();
|
||||
|
||||
@@ -170,31 +229,56 @@ void sms_soft_reset() {
|
||||
sms_vdp_reset();
|
||||
|
||||
sms_sdsc_reset();
|
||||
|
||||
cycles_run = cycles_to_run = scanline = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sms_shutdown() {
|
||||
int sms_shutdown(void) {
|
||||
sms_cheat_shutdown();
|
||||
sms_mem_shutdown();
|
||||
sms_vdp_shutdown();
|
||||
sms_z80_shutdown();
|
||||
YM2413Shutdown();
|
||||
ym2413_shutdown(sms_fm);
|
||||
sound_shutdown();
|
||||
|
||||
/* Reset a few things in case we reinit later. */
|
||||
sms_pad = 0xFFFF;
|
||||
sms_initialized = 0;
|
||||
sms_cons._base.initialized = 0;
|
||||
sms_cons._base.console_type = CONSOLE_SMS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
int sms_frame(int run, int skip) {
|
||||
int16 buf[882 << 1], fmbuf[882 << 1];
|
||||
int line, total_lines, tmp, samples = 0;
|
||||
static __INLINE__ int update_sound(int16 buf[], int start, int line) {
|
||||
int16 fmbuf[16]; /* More than we'll need, but meh. */
|
||||
int16 tmp;
|
||||
uint32 i;
|
||||
|
||||
sms_cycles_run = run;
|
||||
sms_cycles_to_run = 0;
|
||||
if(sms_psg_enabled)
|
||||
sn76489_execute_samples(&psg, buf + start, psg_samples[line]);
|
||||
else
|
||||
memset(buf + start, 0, psg_samples[line] << 2);
|
||||
|
||||
if(sms_ym2413_enabled) {
|
||||
ym2413_update(sms_fm, fmbuf, psg_samples[line]);
|
||||
|
||||
/* Mix in the FM unit's samples */
|
||||
for(i = 0; i < psg_samples[line]; ++i) {
|
||||
tmp = (fmbuf[i << 1] + fmbuf[(i << 1) + 1]);
|
||||
buf[(i << 1) + start] += tmp;
|
||||
buf[(i << 1) + 1 + start] += tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return start + (psg_samples[line] << 1);
|
||||
}
|
||||
|
||||
static void sms_frame(int skip) {
|
||||
int16 buf[882 << 1];
|
||||
int samples = 0, total_lines, line;
|
||||
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
@@ -202,40 +286,159 @@ int sms_frame(int run, int skip) {
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
for(line = 0; line < total_lines; ++line) {
|
||||
sms_cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
sms_cheat_frame();
|
||||
|
||||
sms_cycles_run += sms_vdp_execute(line, skip);
|
||||
sms_cycles_run += sms_z80_run(sms_cycles_to_run - sms_cycles_run);
|
||||
cycles_run += sms_vdp_execute(line, skip);
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
if(sms_psg_enabled) {
|
||||
sn76489_execute_samples(&psg, buf + samples, psg_samples[line]);
|
||||
}
|
||||
else {
|
||||
memset(buf + samples, 0, psg_samples[line] << 2);
|
||||
}
|
||||
|
||||
if(sms_ym2413_enabled) {
|
||||
YM2413UpdateOne(0, fmbuf, psg_samples[line]);
|
||||
|
||||
/* Mix in the FM unit's samples */
|
||||
for(i = 0; i < psg_samples[line]; ++i) {
|
||||
tmp = (fmbuf[i << 1] + fmbuf[(i << 1) + 1]) / 2;
|
||||
buf[(i << 1) + samples] += tmp;
|
||||
buf[(i << 1) + 1 + samples] += tmp;
|
||||
}
|
||||
}
|
||||
|
||||
samples += psg_samples[line] << 1;
|
||||
samples = update_sound(buf, samples, line);
|
||||
}
|
||||
|
||||
#ifndef TIMING_TEST
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
static void sms_scanline(void) {
|
||||
int16 buf[882 << 1];
|
||||
int total_lines, samples = 0;
|
||||
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
sms_cheat_frame();
|
||||
|
||||
/* Run the VDP and Z80 for the whole line. */
|
||||
cycles_run += sms_vdp_execute(scanline, 0);
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
samples = update_sound(buf, 0, scanline);
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* See if we hit the end of a frame by running this scanline. */
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
if(++scanline == total_lines) {
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void sms_single_step(void) {
|
||||
int16 buf[882 << 1];
|
||||
int total_lines, run;
|
||||
|
||||
/* If we're at the start of a frame, set things up for the first line. Also,
|
||||
if we finished a line last time (or are starting a new frame), run the
|
||||
VDP for the line. */
|
||||
if(!cycles_to_run || cycles_run >= cycles_to_run) {
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
sms_cheat_frame();
|
||||
if((run = sms_vdp_execute(scanline, 0))) {
|
||||
cycles_run += run;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Run our one instruction. */
|
||||
cycles_run += sms_z80_run(1);
|
||||
|
||||
/* Did we finish a line? */
|
||||
if(cycles_run >= cycles_to_run) {
|
||||
run = update_sound(buf, 0, scanline);
|
||||
sound_update_buffer(buf, run << 1);
|
||||
|
||||
/* Was it the last line in the frame? */
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
if(++scanline == total_lines) {
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sms_finish_frame(void) {
|
||||
int16 buf[882 << 1];
|
||||
int samples = 0, total_lines, line;
|
||||
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
for(line = scanline; line < total_lines; ++line) {
|
||||
cycles_to_run += SMS_CYCLES_PER_LINE;
|
||||
sms_cheat_frame();
|
||||
|
||||
cycles_run += sms_vdp_execute(line, 0);
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
samples = update_sound(buf, samples, line);
|
||||
}
|
||||
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
|
||||
static void sms_finish_scanline(void) {
|
||||
int16 buf[882 << 1];
|
||||
int total_lines, samples = 0;
|
||||
|
||||
/* Make sure we have something to do. */
|
||||
if(cycles_run >= cycles_to_run)
|
||||
return;
|
||||
|
||||
/* Run the Z80 for the rest of the line. The VDP should've already been
|
||||
run. */
|
||||
cycles_run += sms_z80_run(cycles_to_run - cycles_run);
|
||||
|
||||
samples = update_sound(buf, 0, scanline);
|
||||
sound_update_buffer(buf, samples << 1);
|
||||
|
||||
/* See if we hit the end of a frame by finishing this line. */
|
||||
if(sms_region & SMS_VIDEO_NTSC)
|
||||
total_lines = NTSC_LINES_PER_FRAME;
|
||||
else
|
||||
total_lines = PAL_LINES_PER_FRAME;
|
||||
|
||||
if(++scanline == total_lines) {
|
||||
/* Reset the state for the next frame. */
|
||||
cycles_run -= cycles_to_run;
|
||||
cycles_to_run = 0;
|
||||
scanline = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return sms_cycles_run - sms_cycles_to_run;
|
||||
static void sms_set_control(int player, int control) {
|
||||
/* Verify what we got from the ui... */
|
||||
if(player < 0 || player > 1)
|
||||
return;
|
||||
|
||||
if(control < SMS_PADTYPE_NONE || control > SMS_PADTYPE_GFX_BOARD)
|
||||
return;
|
||||
|
||||
/* Set the control type. */
|
||||
sms_control_type[player] = control;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sms_button_pressed(int player, int button) {
|
||||
uint16 mask = 0;
|
||||
@@ -243,35 +446,57 @@ void sms_button_pressed(int player, int button) {
|
||||
if(player < 1 || player > 2)
|
||||
return;
|
||||
|
||||
if(button < SMS_UP || button > SMS_CONSOLE_RESET || button == SMS_QUIT)
|
||||
return;
|
||||
|
||||
switch(button) {
|
||||
case SMS_UP:
|
||||
case SMS_DOWN:
|
||||
case SMS_LEFT:
|
||||
case SMS_RIGHT:
|
||||
case SMS_BUTTON_1:
|
||||
case SMS_BUTTON_2:
|
||||
mask = (player == 1) ? (1 << button) : (1 << (button + 6));
|
||||
break;
|
||||
|
||||
case SMS_CONSOLE_RESET:
|
||||
mask = SMS_RESET;
|
||||
break;
|
||||
|
||||
case GAMEGEAR_START:
|
||||
if(sms_console == CONSOLE_GG) {
|
||||
sms_gg_regs[0] &= 0x7F;
|
||||
}
|
||||
else if(sms_console == CONSOLE_SMS) {
|
||||
sms_z80_nmi();
|
||||
}
|
||||
if(sms_control_type[player - 1] == SMS_PADTYPE_CONTROL_PAD) {
|
||||
if(button < SMS_UP || button > SMS_CONSOLE_RESET || button == SMS_QUIT)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the pad */
|
||||
sms_pad &= ~mask;
|
||||
switch(button) {
|
||||
case SMS_UP:
|
||||
case SMS_DOWN:
|
||||
case SMS_LEFT:
|
||||
case SMS_RIGHT:
|
||||
case SMS_BUTTON_1:
|
||||
case SMS_BUTTON_2:
|
||||
mask = (player == 1) ? (1 << button) : (1 << (button + 6));
|
||||
break;
|
||||
|
||||
case SMS_CONSOLE_RESET:
|
||||
mask = SMS_RESET;
|
||||
break;
|
||||
|
||||
case GAMEGEAR_START:
|
||||
if(sms_cons._base.console_type == CONSOLE_GG) {
|
||||
sms_gg_regs[0] &= 0x7F;
|
||||
}
|
||||
else if(sms_cons._base.console_type == CONSOLE_SMS) {
|
||||
sms_z80_nmi();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the pad */
|
||||
sms_pad &= ~mask;
|
||||
}
|
||||
else if(sms_control_type[player - 1] == SMS_PADTYPE_GFX_BOARD) {
|
||||
switch(button) {
|
||||
case SMS_GFXBD_1:
|
||||
case SMS_GFXBD_2:
|
||||
case SMS_GFXBD_3:
|
||||
sms_gfxbd_data[player - 1] &= ~(1 << button);
|
||||
break;
|
||||
|
||||
case SMS_CONSOLE_RESET:
|
||||
sms_pad &= ~SMS_RESET;
|
||||
break;
|
||||
|
||||
case GAMEGEAR_START:
|
||||
if(sms_cons._base.console_type == CONSOLE_GG)
|
||||
sms_gg_regs[0] &= 0x7F;
|
||||
else if(sms_cons._base.console_type == CONSOLE_SMS)
|
||||
sms_z80_nmi();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sms_button_released(int player, int button) {
|
||||
@@ -280,32 +505,52 @@ void sms_button_released(int player, int button) {
|
||||
if(player < 1 || player > 2)
|
||||
return;
|
||||
|
||||
if(button < SMS_UP || button > SMS_CONSOLE_RESET || button == SMS_QUIT)
|
||||
return;
|
||||
|
||||
switch(button) {
|
||||
case SMS_UP:
|
||||
case SMS_DOWN:
|
||||
case SMS_LEFT:
|
||||
case SMS_RIGHT:
|
||||
case SMS_BUTTON_1:
|
||||
case SMS_BUTTON_2:
|
||||
mask = (player == 1) ? (1 << button) : (1 << (button + 6));
|
||||
break;
|
||||
|
||||
case SMS_CONSOLE_RESET:
|
||||
mask = SMS_RESET;
|
||||
break;
|
||||
|
||||
case GAMEGEAR_START:
|
||||
if(sms_console == CONSOLE_GG) {
|
||||
sms_gg_regs[0] |= 0x80;
|
||||
}
|
||||
if(sms_control_type[player - 1] == SMS_PADTYPE_CONTROL_PAD) {
|
||||
if(button < SMS_UP || button > SMS_CONSOLE_RESET || button == SMS_QUIT)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the pad */
|
||||
sms_pad |= mask;
|
||||
switch(button) {
|
||||
case SMS_UP:
|
||||
case SMS_DOWN:
|
||||
case SMS_LEFT:
|
||||
case SMS_RIGHT:
|
||||
case SMS_BUTTON_1:
|
||||
case SMS_BUTTON_2:
|
||||
mask = (player == 1) ? (1 << button) : (1 << (button + 6));
|
||||
break;
|
||||
|
||||
case SMS_CONSOLE_RESET:
|
||||
mask = SMS_RESET;
|
||||
break;
|
||||
|
||||
case GAMEGEAR_START:
|
||||
if(sms_cons._base.console_type == CONSOLE_GG) {
|
||||
sms_gg_regs[0] |= 0x80;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the pad */
|
||||
sms_pad |= mask;
|
||||
}
|
||||
else if(sms_control_type[player - 1] == SMS_PADTYPE_GFX_BOARD) {
|
||||
switch(button) {
|
||||
case SMS_GFXBD_1:
|
||||
case SMS_GFXBD_2:
|
||||
case SMS_GFXBD_3:
|
||||
sms_gfxbd_data[player - 1] |= (1 << button);
|
||||
break;
|
||||
|
||||
case SMS_CONSOLE_RESET:
|
||||
sms_pad |= SMS_RESET;
|
||||
break;
|
||||
|
||||
case GAMEGEAR_START:
|
||||
if(sms_cons._base.console_type == CONSOLE_GG)
|
||||
sms_gg_regs[0] |= 0x80;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sms_set_console(int console) {
|
||||
@@ -342,7 +587,19 @@ void sms_set_console(int console) {
|
||||
return;
|
||||
}
|
||||
|
||||
sms_console = console;
|
||||
sms_cons._base.console_type = console;
|
||||
}
|
||||
|
||||
static int sms_current_scanline(void) {
|
||||
return scanline;
|
||||
}
|
||||
|
||||
static int sms_cycles_left(void) {
|
||||
return cycles_to_run - cycles_run;
|
||||
}
|
||||
|
||||
int sms_cycles_elapsed(void) {
|
||||
return cycles_run + sms_z80_get_cycles();
|
||||
}
|
||||
|
||||
static void sms_psg_read_context_v1(FILE *fp) {
|
||||
@@ -476,7 +733,7 @@ int sms_save_state(const char *filename)
|
||||
FILE *fp;
|
||||
uint8 data[4];
|
||||
|
||||
if(sms_initialized == 0)
|
||||
if(sms_cons._base.initialized == 0)
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
|
||||
@@ -513,10 +770,10 @@ int sms_save_state(const char *filename)
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
fwrite(data, 1, 4, fp); /* Console (0 = SMS) */
|
||||
|
||||
data[0] = sms_console; /* Console sub-type */
|
||||
data[1] = sms_region & 0x0F; /* Region code */
|
||||
data[2] = sms_region >> 4; /* Video system */
|
||||
data[3] = 0; /* Reserved */
|
||||
data[0] = sms_cons._base.console_type; /* Console sub-type */
|
||||
data[1] = sms_region & 0x0F; /* Region code */
|
||||
data[2] = sms_region >> 4; /* Video system */
|
||||
data[3] = 0; /* Reserved */
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
/* Write each block's state */
|
||||
@@ -548,7 +805,7 @@ int sms_save_state(const char *filename)
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
int sms_save_state(const char *filename) {
|
||||
char tmpfn[4096];
|
||||
@@ -703,7 +960,7 @@ static int sms_cons_read_context(const uint8 *buf) {
|
||||
if(cons != 0)
|
||||
return -1;
|
||||
|
||||
if(buf[20] != sms_console)
|
||||
if(buf[20] != sms_cons._base.console_type)
|
||||
return -1;
|
||||
|
||||
region = buf[21] == 1 ? SMS_REGION_DOMESTIC : SMS_REGION_EXPORT;
|
||||
@@ -837,7 +1094,7 @@ int sms_load_state(const char *filename)
|
||||
FILE *fp;
|
||||
char byte;
|
||||
|
||||
if(sms_initialized == 0) {
|
||||
if(sms_cons._base.initialized == 0) {
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
}
|
||||
@@ -950,3 +1207,138 @@ int sms_load_state(const char *filename __UNUSED__) {
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sms_write_state(FILE *fp)
|
||||
{
|
||||
uint8 data[4];
|
||||
|
||||
if(sms_cons._base.initialized == 0)
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
|
||||
/* Don't let users do this while the bios is running... */
|
||||
if(sms_bios_active)
|
||||
return -42;
|
||||
|
||||
if(!fp)
|
||||
return -1;
|
||||
|
||||
fprintf(fp, "CrabEmu Save State");
|
||||
|
||||
/* Write save state version */
|
||||
data[0] = 0x00;
|
||||
data[1] = 0x02;
|
||||
fwrite(data, 1, 2, fp);
|
||||
|
||||
/* Write out the Console Metadata block */
|
||||
data[0] = 'C';
|
||||
data[1] = 'O';
|
||||
data[2] = 'N';
|
||||
data[3] = 'S';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
UINT32_TO_BUF(24, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
|
||||
UINT16_TO_BUF(1, data);
|
||||
fwrite(data, 1, 2, fp); /* Version */
|
||||
fwrite(data, 1, 2, fp); /* Flags (Importance = 1) */
|
||||
|
||||
data[0] = data[1] = data[2] = data[3] = 0;
|
||||
fwrite(data, 1, 4, fp); /* Child pointer */
|
||||
fwrite(data, 1, 4, fp); /* Console (0 = SMS) */
|
||||
|
||||
data[0] = sms_cons._base.console_type; /* Console sub-type */
|
||||
data[1] = sms_region & 0x0F; /* Region code */
|
||||
data[2] = sms_region >> 4; /* Video system */
|
||||
data[3] = 0; /* Reserved */
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
/* Write each block's state */
|
||||
if(sms_game_write_context(fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
else if(sms_z80_write_context(fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
else if(sms_psg_write_context(fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
else if(sms_vdp_write_context(fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
else if(sms_mem_write_context(fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
else if(sms_ym2413_write_context(fp)) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sms_read_state(FILE *fp)
|
||||
{
|
||||
char str[19];
|
||||
char byte;
|
||||
|
||||
if(sms_cons._base.initialized == 0) {
|
||||
/* This shouldn't happen.... */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!fp)
|
||||
return -1;
|
||||
|
||||
fread(str, 18, 1, fp);
|
||||
str[18] = 0;
|
||||
if(strcmp("CrabEmu Save State", str)) {
|
||||
fclose(fp);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Read save state version */
|
||||
fread(&byte, 1, 1, fp);
|
||||
if(byte != 0x00) {
|
||||
fclose(fp);
|
||||
return -2;
|
||||
}
|
||||
|
||||
fread(&byte, 1, 1, fp);
|
||||
|
||||
if(byte == 0x01) {
|
||||
/* Read in the current Z80 context */
|
||||
sms_z80_read_context_v1(fp);
|
||||
|
||||
/* Next, read the current VDP state */
|
||||
sms_vdp_read_context_v1(fp);
|
||||
|
||||
/* Now, read the current PSG state */
|
||||
sms_psg_read_context_v1(fp);
|
||||
|
||||
/* Finally, read the current memory contents from the file */
|
||||
sms_mem_read_context_v1(fp);
|
||||
}
|
||||
else if(byte == 0x02) {
|
||||
if(sms_load_state_v2(fp))
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
/* Unknown version... */
|
||||
fclose(fp);
|
||||
return -2;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
sound_reset_buffer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+28
-14
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2007, 2009, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2007, 2009, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -21,22 +21,22 @@
|
||||
#define SMS_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
#include "console.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern int sms_init(int video_system, int region);
|
||||
extern int sms_init(int video_system, int region, int borders);
|
||||
extern int sms_reset(void);
|
||||
extern void sms_soft_reset(void);
|
||||
extern int sms_soft_reset(void);
|
||||
extern int sms_shutdown(void);
|
||||
|
||||
extern int sms_frame(int run, int skip);
|
||||
|
||||
extern void sms_button_pressed(int player, int button);
|
||||
extern void sms_button_released(int player, int button);
|
||||
|
||||
extern void sms_set_console(int console);
|
||||
extern int sms_cycles_elapsed(void);
|
||||
|
||||
extern int sms_psg_write_context(FILE *fp);
|
||||
extern int sms_psg_read_context(const uint8 *buf);
|
||||
@@ -44,6 +44,9 @@ extern int sms_psg_read_context(const uint8 *buf);
|
||||
extern int sms_save_state(const char *filename);
|
||||
extern int sms_load_state(const char *filename);
|
||||
|
||||
extern int sms_write_state(FILE *fp);
|
||||
extern int sms_read_state(FILE *fp);
|
||||
|
||||
/* Old button defines. These define the raw bits used for the data. */
|
||||
#define SMS_PAD1_UP 0x0001
|
||||
#define SMS_PAD1_DOWN 0x0002
|
||||
@@ -72,6 +75,16 @@ extern int sms_load_state(const char *filename);
|
||||
#define SMS_QUIT 7 /* This one is ignored... */
|
||||
#define SMS_CONSOLE_RESET 8
|
||||
|
||||
/* Buttons for the Graphic Board */
|
||||
#define SMS_GFXBD_1 0
|
||||
#define SMS_GFXBD_2 1
|
||||
#define SMS_GFXBD_3 2
|
||||
|
||||
/* Control pad types. */
|
||||
#define SMS_PADTYPE_NONE 0
|
||||
#define SMS_PADTYPE_CONTROL_PAD 1
|
||||
#define SMS_PADTYPE_GFX_BOARD 2
|
||||
|
||||
#define SMS_PAD1_TL SMS_PAD1_A
|
||||
#define SMS_PAD1_TR SMS_PAD1_B
|
||||
#define SMS_PAD2_TL SMS_PAD2_A
|
||||
@@ -81,15 +94,9 @@ extern int sms_load_state(const char *filename);
|
||||
|
||||
#define SMS_TH_MASK 0xC000
|
||||
|
||||
/* Console types */
|
||||
#define CONSOLE_SMS 1
|
||||
#define CONSOLE_GG 2
|
||||
#define CONSOLE_SG1000 3
|
||||
#define CONSOLE_SC3000 4
|
||||
|
||||
/* Region types */
|
||||
#define SMS_REGION_DOMESTIC 0x01
|
||||
#define SMS_REGION_EXPORT 0x02
|
||||
#define SMS_REGION_DOMESTIC REGION_JAPAN
|
||||
#define SMS_REGION_EXPORT REGION_US
|
||||
|
||||
/* Video Standards */
|
||||
#define SMS_VIDEO_NTSC VIDEO_NTSC
|
||||
@@ -97,6 +104,13 @@ extern int sms_load_state(const char *filename);
|
||||
|
||||
#define SMS_CYCLES_PER_LINE 228
|
||||
|
||||
/* Console definition. */
|
||||
typedef struct crabemu_sms {
|
||||
console_t _base;
|
||||
} sms_t;
|
||||
|
||||
extern sms_t sms_cons;
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* !SMS_H */
|
||||
|
||||
@@ -96,10 +96,20 @@ void sms_gg_port_write(uint16 port, uint8 data) {
|
||||
port &= 0xFF;
|
||||
|
||||
if(port < 0x07) {
|
||||
sms_gg_regs[port] = data;
|
||||
switch(port) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
sms_gg_regs[port] = data;
|
||||
break;
|
||||
|
||||
if(port == 0x06) {
|
||||
sn76489_set_output_channels(&psg, data);
|
||||
case 5:
|
||||
sms_gg_regs[5] = data & 0xF8;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
sn76489_set_output_channels(&psg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(port < 0x40) {
|
||||
@@ -144,7 +154,25 @@ uint8 sms_gg_port_read(uint16 port) {
|
||||
port &= 0xFF;
|
||||
|
||||
if(port < 0x07) {
|
||||
return sms_gg_regs[port];
|
||||
switch(port) {
|
||||
case 0:
|
||||
return sms_gg_regs[0] & 0xE0;
|
||||
|
||||
case 1:
|
||||
return 0;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
return sms_gg_regs[port];
|
||||
|
||||
case 6:
|
||||
return 0xFF;
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
else if(port < 0x40) {
|
||||
return 0xFF;
|
||||
|
||||
+204
-71
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
|
||||
2015, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -79,9 +80,9 @@ static uint32 mapper;
|
||||
extern uint16 sms_pad;
|
||||
extern int sms_region;
|
||||
extern sn76489_t psg;
|
||||
extern int sms_console;
|
||||
extern eeprom93c46_t e93c46;
|
||||
extern int sms_ym2413_enabled;
|
||||
extern YM2413 *sms_fm;
|
||||
|
||||
uint8 sms_paging_regs[4];
|
||||
uint8 *sms_rom_page0;
|
||||
@@ -103,10 +104,13 @@ uint8 sms_dummy_arear[256];
|
||||
uint8 sms_dummy_areaw[256];
|
||||
uint8 sms_cart_ram[0x8000];
|
||||
int sms_bios_active = 0;
|
||||
int sms_control_type[2] = { SMS_PADTYPE_CONTROL_PAD, SMS_PADTYPE_CONTROL_PAD };
|
||||
static uint8 sms_fm_detect = 0;
|
||||
static uint8 sms_ym2413_regs[0x41] = { 0 };
|
||||
static int sms_ym2413_in_use = 0;
|
||||
static uint32 rom_crc, rom_adler;
|
||||
static int gfx_board_nibble[2] = { 0, 0 };
|
||||
uint32 sms_gfxbd_data[2] = { 0x0000000F, 0x0000000F };
|
||||
|
||||
typedef void (*remap_page_func)();
|
||||
remap_page_func sms_mem_remap_page[4];
|
||||
@@ -201,9 +205,17 @@ static void remap_page0_sms_bios() {
|
||||
sms_write_map[2] = sms_dummy_areaw;
|
||||
sms_write_map[3] = sms_dummy_areaw;
|
||||
|
||||
for(i = 0x04; i < 0x40; ++i) {
|
||||
sms_read_map[i] = sms_rom_page0 + (i << 8);
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
if(sms_bios_len >= 0x4000) {
|
||||
for(i = 0x04; i < 0x40; ++i) {
|
||||
sms_read_map[i] = sms_rom_page0 + (i << 8);
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(i = 0x04; i < 0x20; ++i) {
|
||||
sms_read_map[i] = sms_read_map[i + 0x20] = sms_rom_page0 + (i << 8);
|
||||
sms_write_map[i] = sms_write_map[i + 0x20] = sms_dummy_areaw;
|
||||
}
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(sms_read_map);
|
||||
@@ -255,9 +267,18 @@ static void remap_page1_sms_bios() {
|
||||
sms_rom_page1 = sms_bios_rom;
|
||||
}
|
||||
|
||||
for(i = 0x40; i < 0x80; ++i) {
|
||||
sms_read_map[i] = sms_rom_page1 + ((i & 0x3F) << 8);
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
if(sms_bios_len >= 0x4000) {
|
||||
for(i = 0x40; i < 0x80; ++i) {
|
||||
sms_read_map[i] = sms_rom_page1 + ((i & 0x3F) << 8);
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(i = 0x40; i < 0x60; ++i) {
|
||||
sms_read_map[i] = sms_read_map[i + 0x20] =
|
||||
sms_rom_page1 + ((i & 0x3F) << 8);
|
||||
sms_write_map[i] = sms_write_map[i + 0x20] = sms_dummy_areaw;
|
||||
}
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(sms_read_map);
|
||||
@@ -330,9 +351,18 @@ static void remap_page2_sms_bios() {
|
||||
sms_rom_page2 = sms_bios_rom;
|
||||
}
|
||||
|
||||
for(i = 0x80; i < 0xC0; ++i) {
|
||||
sms_read_map[i] = sms_rom_page2 + ((i & 0x3F) << 8);
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
if(sms_bios_len >= 0x4000) {
|
||||
for(i = 0x80; i < 0xC0; ++i) {
|
||||
sms_read_map[i] = sms_rom_page2 + ((i & 0x3F) << 8);
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(i = 0x80; i < 0xA0; ++i) {
|
||||
sms_read_map[i] = sms_read_map[i + 0x20] =
|
||||
sms_rom_page2 + ((i & 0x3F) << 8);
|
||||
sms_write_map[i] = sms_write_map[i + 0x20] = sms_dummy_areaw;
|
||||
}
|
||||
}
|
||||
|
||||
sms_z80_set_readmap(sms_read_map);
|
||||
@@ -372,7 +402,7 @@ static void setup_mapper() {
|
||||
int codemasters, sega, korean, korean_msx;
|
||||
uint8 *ptr;
|
||||
|
||||
if(sms_cart_len < 0x8000) {
|
||||
if(sms_cart_len <= 0x8000) {
|
||||
/* The cartridge contains less than 32KB of data, so there probably
|
||||
isn't even a mapper in it, but just to be safe, in case it has
|
||||
SRAM for some reason, we'll just assume the Sega Mapper. */
|
||||
@@ -398,7 +428,7 @@ static void setup_mapper() {
|
||||
++codemasters;
|
||||
else if(value == 0xA000)
|
||||
++korean;
|
||||
else if(value < 0x0004)
|
||||
else if(value == 0x0002 || value == 0x0003 || value == 0x0004)
|
||||
++korean_msx;
|
||||
}
|
||||
}
|
||||
@@ -417,10 +447,12 @@ void sms_mem_handle_memctl(uint8 data) {
|
||||
if(sms_memctl == data)
|
||||
return;
|
||||
|
||||
if(sms_console != CONSOLE_SMS && sms_console != CONSOLE_GG)
|
||||
if(sms_cons._base.console_type != CONSOLE_SMS &&
|
||||
sms_cons._base.console_type != CONSOLE_GG)
|
||||
return;
|
||||
|
||||
if(!(data & SMS_MEMCTL_BIOS) && sms_console == CONSOLE_SMS &&
|
||||
if(!(data & SMS_MEMCTL_BIOS) &&
|
||||
sms_cons._base.console_type == CONSOLE_SMS &&
|
||||
sms_bios_rom != NULL) {
|
||||
sms_mem_remap_page[0] = &remap_page2_sms_bios;
|
||||
sms_mem_remap_page[1] = &remap_page0_sms_bios;
|
||||
@@ -432,7 +464,8 @@ void sms_mem_handle_memctl(uint8 data) {
|
||||
sms_z80_set_mwrite16(&sms_mem_sega_mwrite16);
|
||||
sms_bios_active = 1;
|
||||
}
|
||||
else if(!(data & SMS_MEMCTL_CART) || sms_bios_rom == NULL) {
|
||||
else if((!(data & SMS_MEMCTL_CART) || sms_bios_rom == NULL) &&
|
||||
sms_cart_rom) {
|
||||
sms_bios_active = 0;
|
||||
|
||||
switch(mapper) {
|
||||
@@ -538,13 +571,16 @@ void sms_mem_handle_memctl(uint8 data) {
|
||||
|
||||
void sms_mem_handle_ioctl(uint8 data) {
|
||||
int old, new;
|
||||
uint8 tmp;
|
||||
|
||||
/* Make sure we're emulating an export SMS, the Japanese SMS (and earlier
|
||||
hardware) did not have the I/O Control Register functionality. */
|
||||
if((sms_console == CONSOLE_SMS && (sms_region & SMS_REGION_DOMESTIC)) ||
|
||||
sms_console == CONSOLE_SG1000)
|
||||
if((sms_cons._base.console_type == CONSOLE_SMS &&
|
||||
(sms_region & SMS_REGION_DOMESTIC)) ||
|
||||
sms_cons._base.console_type == CONSOLE_SG1000)
|
||||
return;
|
||||
|
||||
tmp = sms_ioctl;
|
||||
sms_ioctl = data;
|
||||
old = ((sms_pad & sms_ioctl_input_mask) |
|
||||
(sms_ioctl_output_bits & sms_ioctl_output_mask)) ^ SMS_TH_MASK;
|
||||
@@ -567,9 +603,71 @@ void sms_mem_handle_ioctl(uint8 data) {
|
||||
new = ((sms_pad & sms_ioctl_input_mask) |
|
||||
(sms_ioctl_output_bits & sms_ioctl_output_mask)) & SMS_TH_MASK;
|
||||
|
||||
if(old & new) {
|
||||
sms_vdp_hcnt_latch();
|
||||
}
|
||||
if(old & new)
|
||||
sms_vdp_hcnt_latch(sms_cycles_elapsed() % SMS_CYCLES_PER_LINE);
|
||||
|
||||
/* Handle any graphics boards hooked up. Information from the SMS Power
|
||||
forums provided by Maxim and Bock here:
|
||||
http://www.smspower.org/forums/viewtopic.php?p=81920#81920 */
|
||||
if(sms_control_type[0] == SMS_PADTYPE_GFX_BOARD) {
|
||||
if((tmp ^ data) & SMS_IOCTL_TH_A_LEVEL)
|
||||
gfx_board_nibble[0] = (gfx_board_nibble[0] + 1) & 7;
|
||||
if(data & SMS_IOCTL_TR_A_LEVEL)
|
||||
gfx_board_nibble[0] = 0;
|
||||
}
|
||||
|
||||
if(sms_control_type[1] == SMS_PADTYPE_GFX_BOARD) {
|
||||
if((tmp ^ data) & SMS_IOCTL_TH_B_LEVEL)
|
||||
gfx_board_nibble[1] = (gfx_board_nibble[1] + 1) & 7;
|
||||
if(data & SMS_IOCTL_TR_B_LEVEL)
|
||||
gfx_board_nibble[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8 sms_read_controls(uint16 port) {
|
||||
uint16 output = sms_ioctl_output_bits & sms_ioctl_output_mask;
|
||||
uint16 input = sms_pad;
|
||||
|
||||
/* The information here came from the forum thread linked to above... */
|
||||
if(sms_control_type[0] == SMS_PADTYPE_GFX_BOARD) {
|
||||
if(sms_ioctl & SMS_IOCTL_TR_A_LEVEL) {
|
||||
input &= 0xFFE0;
|
||||
}
|
||||
else {
|
||||
input = (input & 0xFFF0) |
|
||||
((sms_gfxbd_data[0] >> (gfx_board_nibble[0] << 2)) & 0x0F);
|
||||
|
||||
if(gfx_board_nibble[0] == 0)
|
||||
input &= ~SMS_PAD1_TL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is totally a guess until someone actually tests this out, but it
|
||||
seems logical, at least. */
|
||||
if(sms_control_type[1] == SMS_PADTYPE_GFX_BOARD) {
|
||||
if(sms_ioctl & SMS_IOCTL_TR_B_LEVEL) {
|
||||
input &= 0xF83F;
|
||||
}
|
||||
else {
|
||||
input = (input & 0xFC3F) |
|
||||
(((sms_gfxbd_data[1] >> (gfx_board_nibble[1] << 2)) &
|
||||
0x0F) <<6);
|
||||
|
||||
if(gfx_board_nibble[1] == 0)
|
||||
input &= ~SMS_PAD2_TL;
|
||||
}
|
||||
}
|
||||
|
||||
input &= sms_ioctl_input_mask;
|
||||
|
||||
if(port & 0x01) {
|
||||
/* I/O port B/misc register */
|
||||
return ((input | output) >> 8) & 0xFF;
|
||||
}
|
||||
else {
|
||||
/* I/O port A/B register */
|
||||
return (input | output) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void sms_port_write(uint16 port, uint8 data) {
|
||||
@@ -603,12 +701,14 @@ void sms_port_write(uint16 port, uint8 data) {
|
||||
if(sms_ym2413_enabled) {
|
||||
if(port == 0xF0) {
|
||||
sms_ym2413_regs[0x40] = data;
|
||||
YM2413Write(0, 0, data);
|
||||
//YM2413Write(0, 0, data);
|
||||
ym2413_write(sms_fm, 0, data);
|
||||
sms_ym2413_in_use = 1;
|
||||
}
|
||||
else if(port == 0xF1) {
|
||||
sms_ym2413_regs[sms_ym2413_regs[0x40]] = data;
|
||||
YM2413Write(0, 1, data);
|
||||
//YM2413Write(0, 1, data);
|
||||
ym2413_write(sms_fm, 1, data);
|
||||
sms_ym2413_in_use = 1;
|
||||
}
|
||||
else if(port == 0xF2) {
|
||||
@@ -653,24 +753,17 @@ uint8 sms_port_read(uint16 port) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!(sms_memctl & SMS_MEMCTL_IO)) {
|
||||
if(port & 0x01) {
|
||||
/* I/O port B/misc register */
|
||||
return (((sms_pad & sms_ioctl_input_mask) |
|
||||
(sms_ioctl_output_bits &
|
||||
sms_ioctl_output_mask)) >> 8) & 0xFF;
|
||||
}
|
||||
else {
|
||||
/* I/O port A/B register */
|
||||
return ((sms_pad & sms_ioctl_input_mask) |
|
||||
(sms_ioctl_output_bits &
|
||||
sms_ioctl_output_mask)) & 0xFF;
|
||||
}
|
||||
}
|
||||
else if(sms_ym2413_enabled && port == 0xF2) {
|
||||
/* Uguu~~ Some games apparently don't disable the I/O chip properly when
|
||||
trying to detect the FM unit... In theory this would potentially give
|
||||
some fun interference on the bus and allow for false positives, but
|
||||
whatever. */
|
||||
if(sms_ym2413_enabled && port == 0xF2) {
|
||||
sms_ym2413_in_use = 1;
|
||||
return sms_fm_detect;
|
||||
}
|
||||
else if(!(sms_memctl & SMS_MEMCTL_IO)) {
|
||||
return sms_read_controls(port);
|
||||
}
|
||||
else {
|
||||
return 0xFF;
|
||||
}
|
||||
@@ -947,7 +1040,7 @@ static void finalize_load(const char *fn) {
|
||||
&rom_adler)) == (uint32)-1) {
|
||||
mapper = SMS_MAPPER_SEGA;
|
||||
|
||||
if(sms_console != CONSOLE_SG1000) {
|
||||
if(sms_cons._base.console_type != CONSOLE_SG1000) {
|
||||
setup_mapper();
|
||||
|
||||
if(mapper == SMS_MAPPER_SEGA) {
|
||||
@@ -979,7 +1072,12 @@ static void finalize_load(const char *fn) {
|
||||
}
|
||||
}
|
||||
|
||||
if((sms_console == CONSOLE_SG1000 || sms_console == CONSOLE_SC3000) &&
|
||||
#ifdef DEBUG
|
||||
printf("Detected Mapper %d\n", mapper);
|
||||
#endif
|
||||
|
||||
if((sms_cons._base.console_type == CONSOLE_SG1000 ||
|
||||
sms_cons._base.console_type == CONSOLE_SC3000) &&
|
||||
mapper != SMS_MAPPER_TW_MSX_TYPE_B) {
|
||||
for(i = 0; i < 0x08; ++i) {
|
||||
sms_read_map[i + 0xC8] = ram + (i << 8);
|
||||
@@ -1007,11 +1105,11 @@ static void finalize_load(const char *fn) {
|
||||
}
|
||||
}
|
||||
|
||||
if((sms_console != CONSOLE_SMS || sms_bios_rom == NULL) &&
|
||||
(sms_console != CONSOLE_GG || gg_bios_rom == NULL)) {
|
||||
if((sms_cons._base.console_type != CONSOLE_SMS || sms_bios_rom == NULL) &&
|
||||
(sms_cons._base.console_type != CONSOLE_GG || gg_bios_rom == NULL)) {
|
||||
switch(mapper) {
|
||||
case SMS_MAPPER_SEGA:
|
||||
if(sms_console != CONSOLE_SG1000) {
|
||||
if(sms_cons._base.console_type != CONSOLE_SG1000) {
|
||||
sms_z80_set_mread(&sms_mem_sega_mread);
|
||||
sms_z80_set_mread16(&sms_mem_sega_mread16);
|
||||
sms_z80_set_mwrite(&sms_mem_sega_mwrite);
|
||||
@@ -1146,7 +1244,7 @@ static void finalize_load(const char *fn) {
|
||||
/* Give a sane default for the SP since we don't have a BIOS. */
|
||||
sms_z80_write_reg(SMS_Z80_REG_SP, 0xDFF0);
|
||||
}
|
||||
else if(sms_console == CONSOLE_SMS) {
|
||||
else if(sms_cons._base.console_type == CONSOLE_SMS) {
|
||||
sms_mem_remap_page[0] = &remap_page2_sms_bios;
|
||||
sms_mem_remap_page[1] = &remap_page0_sms_bios;
|
||||
sms_mem_remap_page[2] = &remap_page1_sms_bios;
|
||||
@@ -1160,7 +1258,7 @@ static void finalize_load(const char *fn) {
|
||||
sms_mem_janggun_init();
|
||||
}
|
||||
}
|
||||
else if(sms_console == CONSOLE_GG) {
|
||||
else if(sms_cons._base.console_type == CONSOLE_GG) {
|
||||
sms_mem_remap_page[0] = &remap_page2;
|
||||
sms_mem_remap_page[1] = &sms_mem_remap_page0_gg_bios;
|
||||
sms_mem_remap_page[2] = &remap_page1;
|
||||
@@ -1184,6 +1282,32 @@ static void finalize_load(const char *fn) {
|
||||
}
|
||||
}
|
||||
|
||||
int sms_mem_run_bios(int console) {
|
||||
/* Clear cartram, although it shouldn't be relevant... */
|
||||
memset(sms_cart_ram, 0, 0x8000);
|
||||
cartram_enabled = 0;
|
||||
|
||||
sms_set_console(console);
|
||||
|
||||
sms_cart_rom = NULL;
|
||||
sms_cart_len = 0;
|
||||
|
||||
/* Set up the mapping functions. */
|
||||
sms_mem_remap_page[0] = &remap_page2_sms_bios;
|
||||
sms_mem_remap_page[1] = &remap_page0_sms_bios;
|
||||
sms_mem_remap_page[2] = &remap_page1_sms_bios;
|
||||
sms_mem_remap_page[3] = &remap_page2_sms_bios;
|
||||
sms_z80_set_mread(&sms_mem_sega_mread);
|
||||
sms_z80_set_mread16(&sms_mem_sega_mread16);
|
||||
sms_z80_set_mwrite(&sms_mem_sega_mwrite);
|
||||
sms_z80_set_mwrite16(&sms_mem_sega_mwrite16);
|
||||
|
||||
reorganize_pages();
|
||||
gui_set_title("CrabEmu");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef NO_ZLIB
|
||||
static int load_gz_rom(const char *fn) {
|
||||
int len;
|
||||
@@ -1502,7 +1626,7 @@ int sms_ym2413_write_context(FILE *fp) {
|
||||
return 0;
|
||||
|
||||
/* Also, this isn't possible on Game Gear */
|
||||
if(sms_console == CONSOLE_GG)
|
||||
if(sms_cons._base.console_type == CONSOLE_GG)
|
||||
return 0;
|
||||
|
||||
/* Finally, if its not being used, don't bother */
|
||||
@@ -1555,30 +1679,30 @@ int sms_ym2413_read_context(const uint8 *buf) {
|
||||
memcpy(sms_ym2413_regs, buf + 16, 65);
|
||||
|
||||
/* This is based on how SMS Plus handles things... */
|
||||
YM2413Write(0, 0, 0x0E);
|
||||
YM2413Write(0, 1, sms_ym2413_regs[0x0E]);
|
||||
ym2413_write(sms_fm, 0, 0x0E);
|
||||
ym2413_write(sms_fm, 1, sms_ym2413_regs[0x0E]);
|
||||
|
||||
for(i = 0x00; i <= 0x07; ++i) {
|
||||
YM2413Write(0, 0, i);
|
||||
YM2413Write(0, 1, sms_ym2413_regs[i]);
|
||||
ym2413_write(sms_fm, 0, i);
|
||||
ym2413_write(sms_fm, 1, sms_ym2413_regs[i]);
|
||||
}
|
||||
|
||||
for(i = 0x10; i <= 0x18; ++i) {
|
||||
YM2413Write(0, 0, i);
|
||||
YM2413Write(0, 1, sms_ym2413_regs[i]);
|
||||
ym2413_write(sms_fm, 0, i);
|
||||
ym2413_write(sms_fm, 1, sms_ym2413_regs[i]);
|
||||
}
|
||||
|
||||
for(i = 0x20; i <= 0x28; ++i) {
|
||||
YM2413Write(0, 0, i);
|
||||
YM2413Write(0, 1, sms_ym2413_regs[i]);
|
||||
ym2413_write(sms_fm, 0, i);
|
||||
ym2413_write(sms_fm, 1, sms_ym2413_regs[i]);
|
||||
}
|
||||
|
||||
for(i = 0x30; i <= 0x38; ++i) {
|
||||
YM2413Write(0, 0, i);
|
||||
YM2413Write(0, 1, sms_ym2413_regs[i]);
|
||||
ym2413_write(sms_fm, 0, i);
|
||||
ym2413_write(sms_fm, 1, sms_ym2413_regs[i]);
|
||||
}
|
||||
|
||||
YM2413Write(0, 0, sms_ym2413_regs[64]);
|
||||
ym2413_write(sms_fm, 0, sms_ym2413_regs[64]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1664,7 +1788,7 @@ int sms_mem_write_context(FILE *fp) {
|
||||
data[3] = 'M';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
switch(sms_console) {
|
||||
switch(sms_cons._base.console_type) {
|
||||
case CONSOLE_SMS:
|
||||
case CONSOLE_GG:
|
||||
memlen = 0x2000;
|
||||
@@ -1717,7 +1841,7 @@ int sms_mem_write_context(FILE *fp) {
|
||||
fwrite(data, 1, 4, fp);
|
||||
|
||||
/* Write the GG Registers block, if appropriate */
|
||||
if(sms_console == CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type == CONSOLE_GG) {
|
||||
data[0] = 'G';
|
||||
data[1] = 'G';
|
||||
data[2] = 'R';
|
||||
@@ -1807,7 +1931,7 @@ int sms_mem_read_context(const uint8 *buf) {
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
switch(sms_console) {
|
||||
switch(sms_cons._base.console_type) {
|
||||
case CONSOLE_SMS:
|
||||
case CONSOLE_GG:
|
||||
if(len != 0x2010)
|
||||
@@ -1952,6 +2076,8 @@ int sms_mapper_read_context(const uint8 *buf) {
|
||||
ptr += clen;
|
||||
}
|
||||
|
||||
reorganize_pages();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1990,6 +2116,7 @@ int sms_mem_init(void) {
|
||||
sms_gg_regs[6] = 0xFF;
|
||||
|
||||
for(i = 0x00; i < 0xC0; ++i) {
|
||||
sms_read_map[i] = sms_dummy_arear;
|
||||
sms_write_map[i] = sms_dummy_areaw;
|
||||
}
|
||||
|
||||
@@ -2011,16 +2138,19 @@ int sms_mem_init(void) {
|
||||
sms_ioctl_output_mask = 0x0000;
|
||||
sms_ioctl_output_bits = 0x0000;
|
||||
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_CART | SMS_MEMCTL_RAM) ^ 0xFF;
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_CART | SMS_MEMCTL_RAM) ^ 0xFC;
|
||||
sms_ioctl = SMS_IOCTL_TR_A_DIRECTION | SMS_IOCTL_TH_A_DIRECTION |
|
||||
SMS_IOCTL_TR_B_DIRECTION | SMS_IOCTL_TH_B_DIRECTION;
|
||||
|
||||
gfx_board_nibble[0] = gfx_board_nibble[1] = 0;
|
||||
sms_gfxbd_data[0] = sms_gfxbd_data[1] = 0x0000000F;
|
||||
|
||||
memset(ram, 0xF0, 8 * 1024);
|
||||
|
||||
/* Set the memctl value at address 0xC000 of the SMS' memory. */
|
||||
ram[0] = sms_memctl;
|
||||
sms_bios_active = 0;
|
||||
|
||||
memset(ram, 0xF0, 8 * 1024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2057,38 +2187,41 @@ void sms_mem_reset(void) {
|
||||
sms_gg_regs[5] = 0x00;
|
||||
sms_gg_regs[6] = 0xFF;
|
||||
|
||||
if(sms_console == CONSOLE_SMS && sms_bios_rom != NULL) {
|
||||
if(sms_cons._base.console_type == CONSOLE_SMS && sms_bios_rom != NULL) {
|
||||
sms_mem_remap_page[0] = &remap_page2_sms_bios;
|
||||
sms_mem_remap_page[1] = &remap_page0_sms_bios;
|
||||
sms_mem_remap_page[2] = &remap_page1_sms_bios;
|
||||
sms_mem_remap_page[3] = &remap_page2_sms_bios;
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_BIOS | SMS_MEMCTL_RAM) ^ 0xFF;
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_BIOS | SMS_MEMCTL_RAM) ^ 0xFC;
|
||||
sms_bios_active = 1;
|
||||
}
|
||||
else if(sms_console == CONSOLE_GG && gg_bios_rom != NULL) {
|
||||
else if(sms_cons._base.console_type == CONSOLE_GG && gg_bios_rom != NULL) {
|
||||
sms_mem_remap_page[0] = &remap_page2;
|
||||
sms_mem_remap_page[1] = &sms_mem_remap_page0_gg_bios;
|
||||
sms_mem_remap_page[2] = &remap_page1;
|
||||
sms_mem_remap_page[3] = &remap_page2;
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_BIOS | SMS_MEMCTL_RAM) ^ 0xFF;
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_BIOS | SMS_MEMCTL_RAM) ^ 0xFC;
|
||||
sms_bios_active = 1;
|
||||
}
|
||||
else {
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_CART | SMS_MEMCTL_RAM) ^ 0xFF;
|
||||
sms_memctl = (SMS_MEMCTL_IO | SMS_MEMCTL_CART | SMS_MEMCTL_RAM) ^ 0xFC;
|
||||
sms_bios_active = 0;
|
||||
}
|
||||
|
||||
/* Set the memctl value at address 0xC000 in the SMS' memory. */
|
||||
ram[0] = sms_memctl;
|
||||
|
||||
sms_ioctl_input_mask = 0xFFFF;
|
||||
sms_ioctl_output_mask = 0x0000;
|
||||
sms_ioctl_output_bits = 0x0000;
|
||||
sms_ioctl = SMS_IOCTL_TR_A_DIRECTION | SMS_IOCTL_TH_A_DIRECTION |
|
||||
SMS_IOCTL_TR_B_DIRECTION | SMS_IOCTL_TH_B_DIRECTION;
|
||||
|
||||
gfx_board_nibble[0] = gfx_board_nibble[1] = 0;
|
||||
sms_gfxbd_data[0] = sms_gfxbd_data[1] = 0x0000000F;
|
||||
|
||||
memset(ram, 0xF0, 8 * 1024);
|
||||
|
||||
/* Set the memctl value at address 0xC000 in the SMS' memory. */
|
||||
ram[0] = sms_memctl;
|
||||
|
||||
reorganize_pages();
|
||||
sms_mem_janggun_reset();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2007, 2008, 2009, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2007, 2008, 2009, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -63,6 +63,8 @@ extern void sms_mem_handle_ioctl(uint8 data);
|
||||
extern int sms_mem_load_bios(const char *fn);
|
||||
extern int sms_mem_load_rom(const char *fn, int console);
|
||||
|
||||
extern int sms_mem_run_bios(int console);
|
||||
|
||||
extern int sms_mem_init(void);
|
||||
extern int sms_mem_shutdown(void);
|
||||
extern void sms_mem_reset(void);
|
||||
|
||||
+300
-47
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2012, 2013, 2014, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -30,9 +30,14 @@
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
extern int sms_console;
|
||||
sms_vdp_t smsvdp;
|
||||
uint32 lut[256];
|
||||
static uint32 lut[256];
|
||||
|
||||
/* Size of the top border for each video mode, in scanlines. */
|
||||
static const int top_border[6] = { 27, 11, 0, 54, 38, 30 };
|
||||
|
||||
static const int kyoukai1[] = { 216, 232, 240, 240, 256, 264 };
|
||||
static const int kyoukai2[] = { 235, 251, 262, 259, 275, 283 };
|
||||
|
||||
#ifdef CRABEMU_32BIT_COLOR
|
||||
static void update_local_pal_sms(int num) {
|
||||
@@ -102,20 +107,19 @@ void sms_vdp_update_cache(int pat) {
|
||||
if(pat >= 512 || !smsvdp.pattern[pat].dirty)
|
||||
return;
|
||||
|
||||
/* Determine where we should start converting */
|
||||
/* Determine where we should start converting from and to... */
|
||||
bitplane = smsvdp.vram + (pat << 5);
|
||||
|
||||
tmp1 = *bitplane++;
|
||||
tmp2 = *bitplane++;
|
||||
tmp3 = *bitplane++;
|
||||
tmp4 = *bitplane++;
|
||||
|
||||
tex1 = smsvdp.pattern[pat].texture[0] + 7;
|
||||
tex2 = smsvdp.pattern[pat].texture[1];
|
||||
tex3 = smsvdp.pattern[pat].texture[2] + 63;
|
||||
tex4 = smsvdp.pattern[pat].texture[3] + 56;
|
||||
|
||||
for(i = 0; i < 8; ++i) {
|
||||
tmp1 = *bitplane++;
|
||||
tmp2 = *bitplane++;
|
||||
tmp3 = *bitplane++;
|
||||
tmp4 = *bitplane++;
|
||||
|
||||
/* lutval = 8x4bpp pixels */
|
||||
lutval = (lut[tmp1]) | (lut[tmp2] << 1) | (lut[tmp3] << 2) |
|
||||
(lut[tmp4] << 3);
|
||||
@@ -134,11 +138,6 @@ void sms_vdp_update_cache(int pat) {
|
||||
|
||||
tex1 += 16;
|
||||
tex4 -= 16;
|
||||
|
||||
tmp1 = *bitplane++;
|
||||
tmp2 = *bitplane++;
|
||||
tmp3 = *bitplane++;
|
||||
tmp4 = *bitplane++;
|
||||
}
|
||||
|
||||
smsvdp.pattern[pat].dirty = 0;
|
||||
@@ -293,6 +292,10 @@ static void sms_vdp_m4_draw_spr(int line, pixel_t *px) {
|
||||
|
||||
y = sat[i] + 1;
|
||||
|
||||
/* sat[i] = 255 implies that the sprite should start on line 0. */
|
||||
if(y == 256)
|
||||
y = 0;
|
||||
|
||||
/* Check the position of this sprite */
|
||||
if(line >= y && line <= y + height - 1) {
|
||||
/* If its on this line, make sure that we haven't already rendered
|
||||
@@ -495,7 +498,39 @@ uint32 sms_vdp_execute(int line, int skip) {
|
||||
/* Draw only if the display is enabled */
|
||||
if(smsvdp.regs[1] & 0x40 && line < smsvdp.lines) {
|
||||
if(!skip) {
|
||||
pixel_t *px = (smsvdp.framebuffer) + (line << 8);
|
||||
pixel_t *px;
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
if(smsvdp.borders)
|
||||
px = (smsvdp.framebuffer_base) + (line << smsvdp.fb_x);
|
||||
else
|
||||
#endif /* !_arch_dreamcast */
|
||||
px = (smsvdp.framebuffer) + (line << smsvdp.fb_x);
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
/* Fill in the left border, if we're bothering to emulate them.
|
||||
This will also, conveniently, reposition the px pointer to beyond
|
||||
the border for us. */
|
||||
if(smsvdp.borders) {
|
||||
int tmp = (smsvdp.regs[7] & 0x0F) | 0x10;
|
||||
pixel_t col = smsvdp.pal[tmp];
|
||||
|
||||
/* The left border is 13 pixels in size. */
|
||||
*px++ = col; /* 1 */
|
||||
*px++ = col; /* 2 */
|
||||
*px++ = col; /* 3 */
|
||||
*px++ = col; /* 4 */
|
||||
*px++ = col; /* 5 */
|
||||
*px++ = col; /* 6 */
|
||||
*px++ = col; /* 7 */
|
||||
*px++ = col; /* 8 */
|
||||
*px++ = col; /* 9 */
|
||||
*px++ = col; /* 10 */
|
||||
*px++ = col; /* 11 */
|
||||
*px++ = col; /* 12 */
|
||||
*px++ = col; /* 13 */
|
||||
}
|
||||
#endif /* !_arch_dreamcast */
|
||||
|
||||
bg_draw(line, px);
|
||||
spr_draw(line, px);
|
||||
@@ -515,6 +550,33 @@ uint32 sms_vdp_execute(int line, int skip) {
|
||||
*px++ = col; /* 7 */
|
||||
*px++ = col; /* 8 */
|
||||
}
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
/* Fill in the right border, if we're bothering to emulate them. */
|
||||
if(smsvdp.borders) {
|
||||
int tmp = (smsvdp.regs[7] & 0x0F) | 0x10;
|
||||
pixel_t col = smsvdp.pal[tmp];
|
||||
px = (smsvdp.framebuffer_base) + (line << smsvdp.fb_x) + 256 +
|
||||
13;
|
||||
|
||||
/* The right border is 15 pixels in size. */
|
||||
*px++ = col; /* 1 */
|
||||
*px++ = col; /* 2 */
|
||||
*px++ = col; /* 3 */
|
||||
*px++ = col; /* 4 */
|
||||
*px++ = col; /* 5 */
|
||||
*px++ = col; /* 6 */
|
||||
*px++ = col; /* 7 */
|
||||
*px++ = col; /* 8 */
|
||||
*px++ = col; /* 9 */
|
||||
*px++ = col; /* 10 */
|
||||
*px++ = col; /* 11 */
|
||||
*px++ = col; /* 12 */
|
||||
*px++ = col; /* 13 */
|
||||
*px++ = col; /* 14 */
|
||||
*px++ = col; /* 15 */
|
||||
}
|
||||
#endif /* !_arch_dreamcast */
|
||||
}
|
||||
else {
|
||||
/* Backgrounds can't actually affect anything status-wise, so there
|
||||
@@ -524,7 +586,13 @@ uint32 sms_vdp_execute(int line, int skip) {
|
||||
}
|
||||
}
|
||||
else if(line < smsvdp.lines && !skip) {
|
||||
pixel_t *px = (smsvdp.framebuffer) + (line << 8);
|
||||
pixel_t *px;
|
||||
#ifndef _arch_dreamcast
|
||||
if(smsvdp.borders)
|
||||
px = (smsvdp.framebuffer_base) + (line << smsvdp.fb_x);
|
||||
else
|
||||
#endif /* !_arch_dreamcast */
|
||||
px = (smsvdp.framebuffer) + (line << smsvdp.fb_x);
|
||||
|
||||
/* Blank the whole scanline. */
|
||||
int tmp = (smsvdp.regs[7] & 0x0F) | 0x10;
|
||||
@@ -533,7 +601,79 @@ uint32 sms_vdp_execute(int line, int skip) {
|
||||
for(i = 0; i < 256; ++i) {
|
||||
*px++ = col;
|
||||
}
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
/* Blank the rest of the scanline as well. */
|
||||
if(smsvdp.borders) {
|
||||
for(i = 0; i < 28; ++i) {
|
||||
*px++ = col;
|
||||
}
|
||||
}
|
||||
#endif /* !_arch_dreamcast */
|
||||
}
|
||||
#ifndef _arch_dreamcast
|
||||
/* If we're emulating borders, then we might have work to do outside the
|
||||
active display period. */
|
||||
else if(smsvdp.borders) {
|
||||
int bb = 0, tb = 512;
|
||||
|
||||
if(smsvdp.vidmode == SMS_VIDEO_NTSC) {
|
||||
switch(smsvdp.lines) {
|
||||
case 192:
|
||||
bb = kyoukai1[0];
|
||||
tb = kyoukai2[0];
|
||||
break;
|
||||
|
||||
case 224:
|
||||
bb = kyoukai1[1];
|
||||
tb = kyoukai2[1];
|
||||
break;
|
||||
|
||||
case 240:
|
||||
bb = kyoukai1[2];
|
||||
tb = kyoukai2[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(smsvdp.lines) {
|
||||
case 192:
|
||||
bb = kyoukai1[3];
|
||||
tb = kyoukai2[3];
|
||||
break;
|
||||
|
||||
case 224:
|
||||
bb = kyoukai1[4];
|
||||
tb = kyoukai2[4];
|
||||
break;
|
||||
|
||||
case 240:
|
||||
bb = kyoukai1[5];
|
||||
tb = kyoukai2[5];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(line < bb) {
|
||||
pixel_t *px = (smsvdp.framebuffer_base) + (line << smsvdp.fb_x);
|
||||
int tmp = (smsvdp.regs[7] & 0x0F) | 0x10;
|
||||
pixel_t col = smsvdp.pal[tmp];
|
||||
|
||||
for(i = 0; i < 284; ++i) {
|
||||
*px++ = col;
|
||||
}
|
||||
}
|
||||
else if(line >= tb) {
|
||||
pixel_t *px = (smsvdp.framebuffer) + ((line - tb) << smsvdp.fb_x);
|
||||
int tmp = (smsvdp.regs[7] & 0x0F) | 0x10;
|
||||
pixel_t col = smsvdp.pal[tmp];
|
||||
|
||||
for(i = 0; i < 284; ++i) {
|
||||
*px++ = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !_arch_dreamcast */
|
||||
|
||||
if(line <= smsvdp.lines) {
|
||||
if(smsvdp.linecnt == 0) {
|
||||
@@ -587,7 +727,14 @@ uint32 tms9918a_vdp_execute(int line, void (*irqfunc)(), int skip) {
|
||||
/* Draw only if the display is enabled */
|
||||
if((smsvdp.regs[1] & 0x40) && line < 192) {
|
||||
if(!skip) {
|
||||
pixel_t *px = (smsvdp.framebuffer) + (line << 8);
|
||||
pixel_t *px;
|
||||
#ifndef _arch_dreamcast
|
||||
if(smsvdp.borders)
|
||||
px = (smsvdp.framebuffer_base) + (line << smsvdp.fb_x);
|
||||
else
|
||||
#endif /* !_arch_dreamcast */
|
||||
px = (smsvdp.framebuffer) + (line << smsvdp.fb_x);
|
||||
|
||||
|
||||
bg_draw(line, px);
|
||||
spr_draw(line, px);
|
||||
@@ -600,7 +747,7 @@ uint32 tms9918a_vdp_execute(int line, void (*irqfunc)(), int skip) {
|
||||
}
|
||||
}
|
||||
else if(line < 192 && !skip) {
|
||||
pixel_t *px = (smsvdp.framebuffer) + (line << 8);
|
||||
pixel_t *px = (smsvdp.framebuffer_base) + (line << smsvdp.fb_x);
|
||||
|
||||
/* Blank the whole scanline. */
|
||||
int tmp = (smsvdp.regs[7] & 0x0F);
|
||||
@@ -643,7 +790,7 @@ void sms_vdp_data_write(uint8 data) {
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
if(sms_console != CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type != CONSOLE_GG) {
|
||||
smsvdp.cram[smsvdp.addr & 0x1F] = data;
|
||||
update_local_pal_sms(smsvdp.addr & 0x1F);
|
||||
}
|
||||
@@ -744,6 +891,40 @@ void sms_vdp_ctl_write(uint8 data) {
|
||||
}
|
||||
}
|
||||
|
||||
void *sms_vdp_framebuffer(void) {
|
||||
return smsvdp.framebuffer;
|
||||
}
|
||||
|
||||
void sms_vdp_framesize(uint32_t *x, uint32_t *y) {
|
||||
*x = 1 << smsvdp.fb_x;
|
||||
*y = 1 << smsvdp.fb_y;
|
||||
}
|
||||
|
||||
void sms_vdp_activeframe(uint32_t *x, uint32_t *y, uint32_t *w, uint32_t *h) {
|
||||
if(sms_cons._base.console_type == CONSOLE_GG) {
|
||||
*x = 48;
|
||||
*y = 24;
|
||||
*w = 160;
|
||||
*h = 144;
|
||||
}
|
||||
else {
|
||||
if(!smsvdp.borders) {
|
||||
*x = *y = 0;
|
||||
*w = 256;
|
||||
*h = smsvdp.lines;
|
||||
}
|
||||
else {
|
||||
*x = *y = 0;
|
||||
*w = 284;
|
||||
|
||||
if(smsvdp.vidmode == SMS_VIDEO_NTSC)
|
||||
*h = 243;
|
||||
else
|
||||
*h = 288;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 sms_vdp_vcnt_read(void) {
|
||||
return vcnt_tab[smsvdp.line];
|
||||
}
|
||||
@@ -788,16 +969,17 @@ uint8 sms_vdp_status_read(void) {
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void sms_vdp_hcnt_latch(void) {
|
||||
extern int sms_cycles_run, sms_cycles_to_run;
|
||||
|
||||
smsvdp.hcnt = sms_hcnt[(sms_cycles_run + sms_z80_get_cycles()) -
|
||||
(sms_cycles_to_run - SMS_CYCLES_PER_LINE)];
|
||||
void sms_vdp_hcnt_latch(int cycles) {
|
||||
smsvdp.hcnt = sms_hcnt[cycles];
|
||||
}
|
||||
|
||||
int sms_vdp_init(int mode) {
|
||||
int sms_vdp_init(int mode, int borders) {
|
||||
int i, tmp;
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
(void)borders;
|
||||
#endif
|
||||
|
||||
/* Initialize the VDP emulation to a sane state */
|
||||
smsvdp.code = 0;
|
||||
smsvdp.addr = 0;
|
||||
@@ -807,6 +989,7 @@ int sms_vdp_init(int mode) {
|
||||
smsvdp.linecnt = 0xFF;
|
||||
smsvdp.pal_latch = 0;
|
||||
smsvdp.hcnt = 0;
|
||||
smsvdp.borders = 0;
|
||||
|
||||
/* Set some sane register values */
|
||||
smsvdp.regs[0x0] = 0x04;
|
||||
@@ -865,9 +1048,24 @@ int sms_vdp_init(int mode) {
|
||||
}
|
||||
|
||||
#ifndef _arch_dreamcast
|
||||
smsvdp.framebuffer = (pixel_t *)malloc(256 * 256 * sizeof(pixel_t));
|
||||
if(!borders) {
|
||||
smsvdp.framebuffer = (pixel_t *)malloc(256 * 256 * sizeof(pixel_t));
|
||||
#else
|
||||
smsvdp.framebuffer = (pixel_t *)memalign(32, 256 * 256 * sizeof(pixel_t));
|
||||
smsvdp.framebuffer =
|
||||
(pixel_t *)memalign(32, 256 * 256 * sizeof(pixel_t));
|
||||
#endif
|
||||
smsvdp.fb_x = 8;
|
||||
smsvdp.fb_y = 8;
|
||||
smsvdp.borders = 0;
|
||||
#ifndef _arch_dreamcast
|
||||
}
|
||||
else {
|
||||
smsvdp.framebuffer = (pixel_t *)malloc(512 * 512 * sizeof(pixel_t));
|
||||
|
||||
smsvdp.fb_x = 9;
|
||||
smsvdp.fb_y = 9;
|
||||
smsvdp.borders = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(smsvdp.framebuffer == NULL) {
|
||||
@@ -1002,9 +1200,18 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
case 0x0C:
|
||||
case 0x0E:
|
||||
case 0x0F:
|
||||
bg_draw = &sms_vdp_m4_draw_bg;
|
||||
spr_draw = &sms_vdp_m4_draw_spr;
|
||||
spr_skip = &sms_vdp_m4_skip_spr;
|
||||
if(machine != SMS_VDP_MACHINE_TMS9918A) {
|
||||
bg_draw = &sms_vdp_m4_draw_bg;
|
||||
spr_draw = &sms_vdp_m4_draw_spr;
|
||||
spr_skip = &sms_vdp_m4_skip_spr;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "sms_vdp_set_vidmode: Unsupported mode for "
|
||||
"TMS9918A VDP: 0x%02x.\n", vdp_mode);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1031,16 +1238,22 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
192 line mode */
|
||||
vcnt_tab = vcnt_ntsc_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[0] << smsvdp.fb_x);
|
||||
}
|
||||
else if(smsvdp.regs[1] & 0x10) {
|
||||
/* M1 is set: 224 line mode */
|
||||
vcnt_tab = vcnt_ntsc_224;
|
||||
smsvdp.lines = 224;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[1] << smsvdp.fb_x);
|
||||
}
|
||||
else if(smsvdp.regs[1] & 0x08) {
|
||||
/* M3 is set: 240 line mode */
|
||||
vcnt_tab = vcnt_ntsc_240;
|
||||
smsvdp.lines = 240;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[2] << smsvdp.fb_x);
|
||||
}
|
||||
else {
|
||||
/* Invalid text mode.... */
|
||||
@@ -1055,15 +1268,19 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
/* M1 is not set: 192 line mode */
|
||||
vcnt_tab = vcnt_ntsc_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[0] << smsvdp.fb_x);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* M4 is not set, use TMS9918 modes */
|
||||
vcnt_tab = vcnt_ntsc_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[0] << smsvdp.fb_x);
|
||||
}
|
||||
|
||||
if(sms_console != CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type != CONSOLE_GG) {
|
||||
if(smsvdp.lines == 192) {
|
||||
gui_set_aspect(4.0f, 3.0f);
|
||||
}
|
||||
@@ -1080,6 +1297,13 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
}
|
||||
else if(machine == SMS_VDP_MACHINE_SMS1) {
|
||||
}
|
||||
else if(machine == SMS_VDP_MACHINE_TMS9918A) {
|
||||
vcnt_tab = vcnt_ntsc_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[0] << smsvdp.fb_x);
|
||||
gui_set_aspect(4.0f, 3.0f);
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "sms_vdp_set_vidmode: Invalid machine: %d\n",
|
||||
@@ -1103,16 +1327,22 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
192 line mode */
|
||||
vcnt_tab = vcnt_pal_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[3] << smsvdp.fb_x);
|
||||
}
|
||||
else if(smsvdp.regs[1] & 0x10) {
|
||||
/* M1 is set: 224 line mode */
|
||||
vcnt_tab = vcnt_pal_224;
|
||||
smsvdp.lines = 224;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[4] << smsvdp.fb_x);
|
||||
}
|
||||
else if(smsvdp.regs[1] & 0x08) {
|
||||
/* M3 is set: 240 line mode */
|
||||
vcnt_tab = vcnt_pal_240;
|
||||
smsvdp.lines = 240;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[5] << smsvdp.fb_x);
|
||||
}
|
||||
else {
|
||||
/* Invalid text mode.... */
|
||||
@@ -1127,15 +1357,19 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
/* M1 is not set: 192 line mode */
|
||||
vcnt_tab = vcnt_pal_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[3] << smsvdp.fb_x);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* M4 is not set, use TMS9918 modes */
|
||||
vcnt_tab = vcnt_pal_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[3] << smsvdp.fb_x);
|
||||
}
|
||||
|
||||
if(sms_console != CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type != CONSOLE_GG) {
|
||||
if(smsvdp.lines == 192) {
|
||||
gui_set_aspect(4.0f, 3.0f);
|
||||
}
|
||||
@@ -1152,6 +1386,13 @@ void sms_vdp_set_vidmode(int mode, int machine) {
|
||||
}
|
||||
else if(machine == SMS_VDP_MACHINE_SMS1) {
|
||||
}
|
||||
else if(machine == SMS_VDP_MACHINE_TMS9918A) {
|
||||
vcnt_tab = vcnt_pal_192;
|
||||
smsvdp.lines = 192;
|
||||
smsvdp.framebuffer_base =
|
||||
smsvdp.framebuffer + (top_border[0] << smsvdp.fb_x);
|
||||
gui_set_aspect(4.0f, 3.0f);
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "sms_vdp_set_vidmode: Invalid machine: %d\n",
|
||||
@@ -1199,7 +1440,7 @@ static int sms_vdp_read_vram_context(const uint8 *buf) {
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len < 16 || len > 0x4010)
|
||||
if(len != 0x4010)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
@@ -1212,7 +1453,7 @@ static int sms_vdp_read_vram_context(const uint8 *buf) {
|
||||
return -1;
|
||||
|
||||
memset(smsvdp.vram, 0, 0x4000);
|
||||
memcpy(smsvdp.vram, buf + 16, len - 16);
|
||||
memcpy(smsvdp.vram, buf + 16, 0x4000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1220,9 +1461,8 @@ static int sms_vdp_write_cram_context(FILE *fp) {
|
||||
uint8 data[4];
|
||||
|
||||
/* Don't write anything for SG-1000, SC-3000, or ColecoVision. */
|
||||
if(sms_console > CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type > CONSOLE_GG)
|
||||
return 0;
|
||||
}
|
||||
|
||||
data[0] = 'C';
|
||||
data[1] = 'R';
|
||||
@@ -1250,7 +1490,7 @@ static int sms_vdp_read_cram_context(const uint8 *buf) {
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
if(len < 16 || len > 80)
|
||||
if(len != 80)
|
||||
return -1;
|
||||
|
||||
/* Check the version number */
|
||||
@@ -1263,7 +1503,7 @@ static int sms_vdp_read_cram_context(const uint8 *buf) {
|
||||
return -1;
|
||||
|
||||
memset(smsvdp.cram, 0, 64);
|
||||
memcpy(smsvdp.cram, buf + 16, len - 16);
|
||||
memcpy(smsvdp.cram, buf + 16, 64);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1277,12 +1517,10 @@ int sms_vdp_write_context(FILE *fp) {
|
||||
data[3] = '8';
|
||||
fwrite(data, 1, 4, fp); /* Block ID */
|
||||
|
||||
if(sms_console < CONSOLE_SG1000) {
|
||||
if(sms_cons._base.console_type < CONSOLE_SG1000)
|
||||
len = 0x4010 + 80 + 48; /* VRAM + CRAM + this block */
|
||||
}
|
||||
else {
|
||||
else
|
||||
len = 0x4010 + 48; /* VRAM + this block */
|
||||
}
|
||||
|
||||
UINT32_TO_BUF(len, data);
|
||||
fwrite(data, 1, 4, fp); /* Length */
|
||||
@@ -1327,7 +1565,7 @@ int sms_vdp_read_context(const uint8 *buf) {
|
||||
uint32 len, child, clen;
|
||||
uint16 ver;
|
||||
const uint8 *ptr;
|
||||
int rv, i;
|
||||
int rv, i, tmp;
|
||||
|
||||
/* Check the size */
|
||||
BUF_TO_UINT32(buf + 4, len);
|
||||
@@ -1346,7 +1584,7 @@ int sms_vdp_read_context(const uint8 *buf) {
|
||||
|
||||
/* Read the VDP state */
|
||||
for(i = 0; i < 16; ++i) {
|
||||
sms_vdp_reg_write(i, buf[16 + i]);
|
||||
smsvdp.regs[i] = buf[16 + i];
|
||||
}
|
||||
|
||||
BUF_TO_UINT16(buf + 32, smsvdp.addr);
|
||||
@@ -1398,7 +1636,7 @@ int sms_vdp_read_context(const uint8 *buf) {
|
||||
ptr += clen;
|
||||
}
|
||||
|
||||
if(sms_console != CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type != CONSOLE_GG) {
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
update_local_pal_sms(i);
|
||||
}
|
||||
@@ -1409,6 +1647,21 @@ int sms_vdp_read_context(const uint8 *buf) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with the updated register values... */
|
||||
sms_z80_clear_irq();
|
||||
smsvdp.sat = smsvdp.vram + ((smsvdp.regs[5] & 0x7E) << 7);
|
||||
tmp = (smsvdp.regs[8] & 0xF8) >> 3;
|
||||
smsvdp.xscroll_coarse = (32 - tmp) & 0x1F;
|
||||
smsvdp.xscroll_fine = smsvdp.regs[8] & 0x07;
|
||||
smsvdp.yscroll_fine = smsvdp.regs[9] & 0x07;
|
||||
sms_vdp_set_vidmode(smsvdp.vidmode, smsvdp.machine);
|
||||
readjust_name_table();
|
||||
|
||||
/* Assert the irq line if we need to. */
|
||||
if((smsvdp.status & 0x80) && (smsvdp.regs[1] & 0x20)) {
|
||||
sms_z80_assert_irq();
|
||||
}
|
||||
|
||||
/* Mark all patterns as dirty */
|
||||
for(i = 0; i < 512; ++i) {
|
||||
smsvdp.pattern[i].dirty = 1;
|
||||
@@ -1448,7 +1701,7 @@ void sms_vdp_read_context_v1(FILE *fp) {
|
||||
overwritten before they're used, anyway */
|
||||
}
|
||||
|
||||
if(sms_console != CONSOLE_GG) {
|
||||
if(sms_cons._base.console_type != CONSOLE_GG) {
|
||||
for(i = 0; i < 0x20; ++i) {
|
||||
update_local_pal_sms(i);
|
||||
}
|
||||
|
||||
+18
-6
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -73,8 +73,9 @@ typedef struct smsvdp_s {
|
||||
/* Background priority levels */
|
||||
uint8 bg_prio[32];
|
||||
|
||||
/* Internal Framebuffer - 256x256 pixels 32bpp or 16bpp */
|
||||
/* Internal Framebuffer - 256x256 or 512x512 pixels 32bpp or 16bpp */
|
||||
pixel_t *framebuffer;
|
||||
pixel_t *framebuffer_base;
|
||||
|
||||
/* Background alpha levels, used with the priority to determine if
|
||||
a given pixel is invisible or not */
|
||||
@@ -106,6 +107,11 @@ typedef struct smsvdp_s {
|
||||
int xscroll_coarse;
|
||||
int xscroll_fine;
|
||||
int yscroll_fine;
|
||||
|
||||
/* Framebuffer size, in lg(pixels) */
|
||||
int borders;
|
||||
int fb_x;
|
||||
int fb_y;
|
||||
} sms_vdp_t;
|
||||
|
||||
#define SMS_VDP_FLAG_BYTES_WRITTEN 0x00000001
|
||||
@@ -117,23 +123,29 @@ extern void sms_vdp_update_cache(int pat);
|
||||
extern void sms_vdp_data_write(uint8 data);
|
||||
extern void sms_vdp_ctl_write(uint8 data);
|
||||
|
||||
extern void *sms_vdp_framebuffer(void);
|
||||
extern void sms_vdp_framesize(uint32_t *x, uint32_t *y);
|
||||
extern void sms_vdp_activeframe(uint32_t *x, uint32_t *y, uint32_t *w,
|
||||
uint32_t *h);
|
||||
|
||||
extern uint8 sms_vdp_vcnt_read(void);
|
||||
extern uint8 sms_vdp_hcnt_read(void);
|
||||
extern uint8 sms_vdp_data_read(void);
|
||||
extern uint8 sms_vdp_status_read(void);
|
||||
|
||||
extern void sms_vdp_hcnt_latch(void);
|
||||
extern void sms_vdp_hcnt_latch(int cycles);
|
||||
|
||||
extern uint32 sms_vdp_execute(int line, int skip);
|
||||
extern uint32 tms9918a_vdp_execute(int line, void (*irqfunc)(), int skip);
|
||||
extern void sms_vdp_dump_vram(const char *fn);
|
||||
|
||||
extern int sms_vdp_init(int mode);
|
||||
extern int sms_vdp_init(int mode, int borders);
|
||||
extern int sms_vdp_reset(void);
|
||||
extern int sms_vdp_shutdown(void);
|
||||
|
||||
#define SMS_VDP_MACHINE_SMS1 1
|
||||
#define SMS_VDP_MACHINE_SMS2 2
|
||||
#define SMS_VDP_MACHINE_SMS1 1
|
||||
#define SMS_VDP_MACHINE_SMS2 2
|
||||
#define SMS_VDP_MACHINE_TMS9918A 3
|
||||
extern void sms_vdp_set_vidmode(int mode, int machine);
|
||||
|
||||
extern int sms_vdp_write_context(FILE *fp);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2009 Lawrence Sebald
|
||||
Copyright (C) 2009, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -198,6 +198,77 @@ uint16 sms_z80_read_reg(int reg) {
|
||||
}
|
||||
}
|
||||
|
||||
void sms_z80_write_reg(int reg, uint16 value) {
|
||||
switch(reg) {
|
||||
case SMS_Z80_REG_B:
|
||||
CZ80.BC.B.H = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_C:
|
||||
CZ80.BC.B.L = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_D:
|
||||
CZ80.DE.B.H = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_E:
|
||||
CZ80.DE.B.L = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_H:
|
||||
CZ80.HL.B.H = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_L:
|
||||
CZ80.HL.B.L = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_F:
|
||||
CZ80.FA.B.H = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_A:
|
||||
CZ80.FA.B.L = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_PC:
|
||||
Cz80_Set_PC(&CZ80, value);
|
||||
break;
|
||||
case SMS_Z80_REG_SP:
|
||||
CZ80.SP.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_IX:
|
||||
CZ80.IX.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_IY:
|
||||
CZ80.IY.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_BC:
|
||||
CZ80.BC.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_DE:
|
||||
CZ80.DE.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_HL:
|
||||
CZ80.HL.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_AF:
|
||||
CZ80.FA.W = (value >> 8) | ((value & 0xFF) << 8);
|
||||
break;
|
||||
case SMS_Z80_REG_R:
|
||||
CZ80.R.B.L = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_I:
|
||||
CZ80.I = (uint8)value;
|
||||
break;
|
||||
case SMS_Z80_REG_BCp:
|
||||
CZ80.BC2.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_DEp:
|
||||
CZ80.DE2.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_HLp:
|
||||
CZ80.HL2.W = value;
|
||||
break;
|
||||
case SMS_Z80_REG_AFp:
|
||||
CZ80.FA2.W = (value >> 8) | ((value & 0xFF) << 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int sms_z80_write_context(FILE *fp __UNUSED__) {
|
||||
/* XXXX */
|
||||
return -1;
|
||||
@@ -206,3 +277,8 @@ int sms_z80_write_context(FILE *fp __UNUSED__) {
|
||||
void sms_z80_read_context_v1(FILE *fp __UNUSED__) {
|
||||
/* XXXX */
|
||||
}
|
||||
|
||||
int sms_z80_read_context(const uint8 *buf __UNUSED__) {
|
||||
/* XXXX */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2008 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* This file contains the z80 core debugger that I wrote
|
||||
to compare how CrabZ80 worked versus the Z80 core from
|
||||
MAME. The code isn't pretty, but its here, in case
|
||||
anyone wants to compile their own binary with it.
|
||||
(These binaries cannot be released due to licensing
|
||||
restrictions on the MAME core that contradict the GPL).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "smsz80.h"
|
||||
|
||||
#include "CrabZ80d.h"
|
||||
|
||||
#include "../../cpu/mamez80/z80.h"
|
||||
|
||||
typedef struct {
|
||||
uint16 addr;
|
||||
uint8 val;
|
||||
uint8 write;
|
||||
} debug_memory_ent;
|
||||
|
||||
CrabZ80_t *cpuz80;
|
||||
extern Z80_Regs Z80;
|
||||
|
||||
debug_memory_ent crabmem[10];
|
||||
debug_memory_ent mz80mem[10];
|
||||
|
||||
debug_memory_ent crabport[10];
|
||||
debug_memory_ent mz80port[10];
|
||||
|
||||
debug_memory_ent portread[10];
|
||||
debug_memory_ent memread[200];
|
||||
|
||||
int debug_crab_ents = 0;
|
||||
int debug_mz80_ents = 0;
|
||||
int debug_crab_ports = 0;
|
||||
int debug_mz80_ports = 0;
|
||||
int debug_port_reads1 = 0;
|
||||
int debug_port_reads2 = 0;
|
||||
int debug_mem_reads1 = 0;
|
||||
int debug_mem_reads2 = 0;
|
||||
|
||||
uint8 (*mread8)(uint16) = NULL;
|
||||
void (*mwrite8)(uint16, uint8) = NULL;
|
||||
|
||||
static void sms_debug_memwrite(uint16 addr, uint8 val) {
|
||||
crabmem[debug_crab_ents].addr = addr;
|
||||
crabmem[debug_crab_ents].write = 1;
|
||||
crabmem[debug_crab_ents++].val = val;
|
||||
|
||||
mwrite8(addr, val);
|
||||
}
|
||||
|
||||
static uint8 sms_debug_portread(uint16 port) {
|
||||
uint8 res = sms_port_read(port);
|
||||
|
||||
portread[debug_port_reads1].addr = port;
|
||||
portread[debug_port_reads1].val = res;
|
||||
portread[debug_port_reads1++].write = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sms_debug_portwrite(uint16 port, uint8 data) {
|
||||
crabport[debug_crab_ports].addr = port & 0xFF;
|
||||
crabport[debug_crab_ports].write = 1;
|
||||
crabport[debug_crab_ports++].val = data;
|
||||
|
||||
sms_port_write(port, data);
|
||||
}
|
||||
|
||||
static uint8 sms_debug_memread(uint16 addr) {
|
||||
uint8 res = mread8(addr);
|
||||
|
||||
memread[debug_mem_reads1].addr = addr;
|
||||
memread[debug_mem_reads1].write = 0;
|
||||
memread[debug_mem_reads1++].val = res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Glue code for mz80 */
|
||||
void program_write_byte_8(UINT32 addr, UINT8 val) {
|
||||
mz80mem[debug_mz80_ents].addr = addr;
|
||||
mz80mem[debug_mz80_ents].write = 1;
|
||||
mz80mem[debug_mz80_ents++].val = val;
|
||||
}
|
||||
|
||||
UINT8 program_read_byte_8(UINT32 addr) {
|
||||
int i;
|
||||
|
||||
for(i = 0; i < debug_mem_reads1; ++i) {
|
||||
if(memread[i].addr == (addr & 0xFFFF))
|
||||
return memread[i].val;
|
||||
}
|
||||
|
||||
printf("reading other addr: %04x %04X\n", addr & 0xFFFF, Z80.pc.w.l);
|
||||
return mread8(addr);
|
||||
}
|
||||
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
void io_write_byte_8(UINT32 port, UINT8 val) {
|
||||
mz80port[debug_mz80_ports].addr = port & 0xFF;
|
||||
mz80port[debug_mz80_ports].write = 1;
|
||||
mz80port[debug_mz80_ports++].val = val;
|
||||
}
|
||||
|
||||
UINT8 io_read_byte_8(UINT32 port) {
|
||||
int i;
|
||||
|
||||
for(i = 0; i < debug_port_reads1; ++i) {
|
||||
if(portread[i].addr == (port & 0xFF))
|
||||
return portread[i].val;
|
||||
}
|
||||
|
||||
printf("reading other port: %04x %04X\n", port & 0xFF, Z80.pc.w.l);
|
||||
return sms_port_read(port & 0xFF);
|
||||
}
|
||||
|
||||
static int _compare_registers() {
|
||||
int f = 0;
|
||||
if(cpuz80->af.b.h != Z80.af.b.h) {
|
||||
printf("A does not match, %x v %x\n", cpuz80->af.b.h, Z80.af.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->af.b.l != Z80.af.b.l) {
|
||||
printf("F does not match, %02X v %02X\n", cpuz80->af.b.l, Z80.af.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->bc.b.h != Z80.bc.b.h) {
|
||||
printf("B does not match, %x v %x\n", cpuz80->bc.b.h, Z80.bc.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->bc.b.l != Z80.bc.b.l) {
|
||||
printf("C does not match, %x v %x\n", cpuz80->bc.b.l, Z80.bc.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->de.b.h != Z80.de.b.h) {
|
||||
printf("D does not match, %x v %x\n", cpuz80->de.b.h, Z80.de.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->de.b.l != Z80.de.b.l) {
|
||||
printf("E does not match, %x v %x\n", cpuz80->de.b.l, Z80.de.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->hl.b.h != Z80.hl.b.h) {
|
||||
printf("H does not match, %x v %x\n", cpuz80->hl.b.h, Z80.hl.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->hl.b.l != Z80.hl.b.l) {
|
||||
printf("L does not match, %x v %x\n", cpuz80->hl.b.l, Z80.hl.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->afp.b.h != Z80.af2.b.h) {
|
||||
printf("Ap does not match, %x v %x\n", cpuz80->afp.b.h, Z80.af2.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->afp.b.l != Z80.af2.b.l) {
|
||||
printf("Fp does not match, %x v %x\n", cpuz80->afp.b.l, Z80.af2.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->bcp.b.h != Z80.bc2.b.h) {
|
||||
printf("Bp does not match, %x v %x\n", cpuz80->bcp.b.h, Z80.bc2.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->bcp.b.l != Z80.bc2.b.l) {
|
||||
printf("Cp does not match, %x v %x\n", cpuz80->bcp.b.l, Z80.bc2.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->dep.b.h != Z80.de2.b.h) {
|
||||
printf("Dp does not match, %x v %x\n", cpuz80->dep.b.h, Z80.de2.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->dep.b.l != Z80.de2.b.l) {
|
||||
printf("Ep does not match, %x v %x\n", cpuz80->dep.b.l, Z80.de2.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->hlp.b.h != Z80.hl2.b.h) {
|
||||
printf("Hp does not match, %x v %x\n", cpuz80->hlp.b.h, Z80.hl2.b.h);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->hlp.b.l != Z80.hl2.b.l) {
|
||||
printf("Lp does not match, %x v %x\n", cpuz80->hlp.b.l, Z80.hl2.b.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->pc.w != Z80.pc.w.l) {
|
||||
printf("PC does not match, %x v %x\n", cpuz80->pc.w, Z80.pc.w.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->ix.w != Z80.ix.w.l) {
|
||||
printf("IX does not match %x v %x\n", cpuz80->ix.w, Z80.iy.w.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->iy.w != Z80.iy.w.l) {
|
||||
printf("IY does not match %x v %x\n", cpuz80->iy.w, Z80.ix.w.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->sp.w != Z80.sp.w.l) {
|
||||
printf("SP does not match %x v %x\n", cpuz80->sp.w, Z80.sp.w.l);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->ir.b.h != Z80.i) {
|
||||
printf("I does not match %x v %x\n", cpuz80->ir.b.h, Z80.i);
|
||||
f++;
|
||||
}
|
||||
if(cpuz80->ir.b.l != Z80.r) {
|
||||
printf("R does not match %x v %x\n", cpuz80->ir.b.l, Z80.r);
|
||||
f++;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static int _compare_memory() {
|
||||
int i;
|
||||
int f = 0;
|
||||
|
||||
for(i = 0; i < debug_crab_ents; ++i) {
|
||||
if(crabmem[i].addr != mz80mem[i].addr) {
|
||||
printf("Mismatched addresses (write=%d)! %02x v %02x\n", crabmem[i].write, crabmem[i].addr, mz80mem[i].addr);
|
||||
f++;
|
||||
}
|
||||
else if(crabmem[i].val != mz80mem[i].val) {
|
||||
printf("mismatched bytes (write=%d)! %02x v %02x\n", crabmem[i].write, crabmem[i].val, mz80mem[i].val);
|
||||
f++;
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static int _compare_ports() {
|
||||
int i;
|
||||
int f = 0;
|
||||
|
||||
for(i = 0; i < debug_crab_ports; ++i) {
|
||||
if(crabport[i].addr != mz80port[i].addr) {
|
||||
printf("Mismatched ports (write=%d)! %02x v %02x\n", crabport[i].write, crabport[i].addr, mz80port[i].addr);
|
||||
f++;
|
||||
}
|
||||
else if(crabport[i].val != mz80port[i].val) {
|
||||
printf("mismatched pbytes (write=%d)! %02x v %02x\n", crabport[i].write, crabport[i].val, mz80port[i].val);
|
||||
f++;
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
int sms_z80_init(void) {
|
||||
cpuz80 = (CrabZ80_t *)malloc(sizeof(CrabZ80_t));
|
||||
|
||||
if(cpuz80 == NULL) {
|
||||
fprintf(stderr, "Out of memory while initializing Z80 in debug mode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize CrabZ80 */
|
||||
CrabZ80_init(cpuz80);
|
||||
CrabZ80_reset(cpuz80);
|
||||
|
||||
CrabZ80_set_memwrite(cpuz80, sms_debug_memwrite);
|
||||
CrabZ80_set_memread(cpuz80, sms_debug_memread);
|
||||
CrabZ80_set_portwrite(cpuz80, sms_debug_portwrite);
|
||||
CrabZ80_set_portread(cpuz80, sms_debug_portread);
|
||||
|
||||
z80_init();
|
||||
z80_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sms_z80_shutdown(void) {
|
||||
free(cpuz80);
|
||||
z80_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sms_z80_reset(void) {
|
||||
CrabZ80_reset(cpuz80);
|
||||
z80_reset();
|
||||
}
|
||||
|
||||
#define INPUT_LINE_NMI 127
|
||||
|
||||
void sms_z80_assert_irq(void) {
|
||||
CrabZ80_assert_irq(cpuz80, 0xFFFFFFFF);
|
||||
z80_set_irq_line(1, 1);
|
||||
}
|
||||
|
||||
void sms_z80_clear_irq(void) {
|
||||
CrabZ80_clear_irq(cpuz80);
|
||||
z80_set_irq_line(1, 0);
|
||||
}
|
||||
|
||||
void sms_z80_nmi(void) {
|
||||
CrabZ80_pulse_nmi(cpuz80);
|
||||
z80_set_irq_line(INPUT_LINE_NMI, 1);
|
||||
}
|
||||
|
||||
uint32 sms_z80_run(uint32 cycles) {
|
||||
uint32 cyclesdone = 0;
|
||||
uint32 opcode;
|
||||
uint16 pc;
|
||||
int tmp;
|
||||
static char last_str[256];
|
||||
char str[256];
|
||||
static uint16 last_pc = 0;
|
||||
uint32 c1, c2;
|
||||
|
||||
while(cyclesdone < cycles) {
|
||||
debug_crab_ents = debug_mz80_ents = 0;
|
||||
debug_port_reads1 = debug_port_reads2 = 0;
|
||||
debug_mem_reads1 = debug_mem_reads2 = 0;
|
||||
debug_crab_ports = debug_mz80_ports = 0;
|
||||
|
||||
pc = cpuz80->pc.w;
|
||||
|
||||
opcode = mread8(cpuz80->pc.w);
|
||||
|
||||
if(opcode == 0xED || opcode == 0xCB || opcode == 0xDD || opcode == 0xFD) {
|
||||
opcode |= (mread8(cpuz80->pc.w + 1) << 8);
|
||||
}
|
||||
if((opcode & 0xFF00) == 0xCB00) {
|
||||
opcode |= (mread8(cpuz80->pc.w + 2) << 16) | (mread8(cpuz80->pc.w + 3) << 24);
|
||||
}
|
||||
|
||||
c1 = CrabZ80_execute(cpuz80, 1);
|
||||
c2 = z80_execute(1);
|
||||
cyclesdone += c1;
|
||||
|
||||
tmp = _compare_registers() + _compare_memory() + _compare_ports();
|
||||
CrabZ80_disassemble(str, cpuz80, pc);
|
||||
|
||||
if(tmp) {
|
||||
printf("Cycles done: %d %d\n", (int)c1, (int)c2);
|
||||
printf("%d errors at: PC = 0x%04X, old_pc = 0x%04X Opcode = 0x%02X",
|
||||
tmp, pc, last_pc, opcode & 0xFF);
|
||||
switch(opcode & 0xFF) {
|
||||
case 0xED:
|
||||
case 0xCB:
|
||||
printf("%02X (%s -- %s)\n", (opcode & 0xFF00) >> 8, str,
|
||||
last_str);
|
||||
break;
|
||||
case 0xDD:
|
||||
case 0xFD:
|
||||
if((opcode & 0xFF00) == 0xCB00)
|
||||
printf("%02X%02X%02X (%s -- %s)\n",
|
||||
(opcode & 0xFF00) >> 8,
|
||||
(opcode & 0xFF0000) >> 16,
|
||||
(opcode & 0xFF000000) >> 24, str, last_str);
|
||||
else
|
||||
printf("%02X (%s -- %s)\n", (opcode & 0xFF00) >> 8, str,
|
||||
last_str);
|
||||
break;
|
||||
default:
|
||||
printf("(%s -- %s)\n", str, last_str);
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(last_str, str);
|
||||
last_pc = pc;
|
||||
}
|
||||
|
||||
return cyclesdone;
|
||||
}
|
||||
|
||||
uint16 sms_z80_get_pc(void) {
|
||||
return cpuz80->pc.w;
|
||||
}
|
||||
|
||||
void sms_z80_set_mread(uint8 (*mread)(uint16)) {
|
||||
mread8 = mread;
|
||||
}
|
||||
|
||||
void sms_z80_set_mwrite(void (*mwrite)(uint16, uint8)) {
|
||||
mwrite8 = mwrite;
|
||||
}
|
||||
|
||||
void sms_z80_set_mread16(uint16 (*mread)(uint16) UNUSED) {
|
||||
/* Disabled for debugging */
|
||||
}
|
||||
|
||||
void sms_z80_set_mwrite16(void (*mwrite)(uint16, uint16) UNUSED) {
|
||||
/* Disabled for debugging */
|
||||
}
|
||||
|
||||
void sms_z80_set_readmap(uint8 *readmap[256] UNUSED) {
|
||||
/* Disabled for debugging */
|
||||
}
|
||||
|
||||
int sms_z80_write_context(FILE *fp) {
|
||||
/* XXXX */
|
||||
return -1;
|
||||
}
|
||||
|
||||
void sms_z80_read_context_v1(FILE *fp) {
|
||||
/* XXXX */
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -34,7 +34,7 @@ int sms_z80_init(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CrabZ80_init(cpuz80);
|
||||
CrabZ80_init(cpuz80, CRABZ80_CPU_Z80);
|
||||
CrabZ80_reset(cpuz80);
|
||||
|
||||
CrabZ80_set_portwrite(cpuz80, sms_port_write);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2008 Lawrence Sebald
|
||||
Copyright (C) 2008, 2013 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -37,8 +37,8 @@ void terebi_update(int x, int y, int pressed) {
|
||||
|
||||
if(x < 0)
|
||||
x = 0;
|
||||
else if(x > 255)
|
||||
x = 255;
|
||||
else if(x > 251)
|
||||
x = 251;
|
||||
|
||||
if(y < 0)
|
||||
y = 0;
|
||||
@@ -63,12 +63,7 @@ uint8 terebi_mread(uint16 addr) {
|
||||
return (terebi_flags & TEREBI_OEKAKI_PRESSED) ? 0 : 1;
|
||||
}
|
||||
else if(addr == 0xA000) {
|
||||
if((terebi_flags & TEREBI_OEKAKI_PRESSED)) {
|
||||
return (terebi_flags & TEREBI_OEKAKI_AXIS_Y) ? terebi_x : terebi_y;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
return (terebi_flags & TEREBI_OEKAKI_AXIS_Y) ? terebi_x : terebi_y;
|
||||
}
|
||||
|
||||
return sms_read_map[addr >> 8][addr & 0xFF];
|
||||
|
||||
+117
-10
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "sms.h"
|
||||
#include "smsz80.h"
|
||||
#include "smsvdp.h"
|
||||
#include "tms9918a.h"
|
||||
|
||||
@@ -364,19 +365,15 @@ void tms9918a_m023_draw_spr(int line, pixel_t *px) {
|
||||
for(i = 0; i < 32 && count < 5; ++i) {
|
||||
y = sat[i << 2] + 1;
|
||||
|
||||
if(y == 0xD1) {
|
||||
if(y == 0xD1)
|
||||
/* End of list marker */
|
||||
break;
|
||||
}
|
||||
else if(line < y) {
|
||||
else if(line < y)
|
||||
continue;
|
||||
}
|
||||
else if(y + (pattern_size << size_shift) - 1 < line) {
|
||||
else if(y + (pattern_size << size_shift) - 1 < line)
|
||||
continue;
|
||||
}
|
||||
else if(++count == 5) {
|
||||
else if(++count == 5)
|
||||
break;
|
||||
}
|
||||
|
||||
x = sat[(i << 2) + 1];
|
||||
pattern = sat[(i << 2) + 2];
|
||||
@@ -601,7 +598,7 @@ void tms9918a_m023_skip_spr(int line) {
|
||||
|
||||
if(y == 0xD1)
|
||||
/* End of list marker */
|
||||
return;
|
||||
break;
|
||||
else if(line < y)
|
||||
continue;
|
||||
else if(y + (pattern_size << size_shift) - 1 < line)
|
||||
@@ -710,6 +707,7 @@ void tms9918a_m023_skip_spr(int line) {
|
||||
}
|
||||
else if(y == 0xD1) {
|
||||
/* Set the fifth sprite bits to the last sprite displayed */
|
||||
smsvdp.status &= 0xE0;
|
||||
smsvdp.status |= (i & 0x1F);
|
||||
}
|
||||
else {
|
||||
@@ -719,5 +717,114 @@ void tms9918a_m023_skip_spr(int line) {
|
||||
}
|
||||
}
|
||||
|
||||
void tms9918a_vdp_data_write(uint8 data) {
|
||||
/* Clear the bytes written flag */
|
||||
smsvdp.flags &= (~SMS_VDP_FLAG_BYTES_WRITTEN);
|
||||
|
||||
/* First, update the read buffer */
|
||||
smsvdp.read_buf = data;
|
||||
|
||||
/* Write the byte to the RAM */
|
||||
smsvdp.vram[smsvdp.addr] = data;
|
||||
|
||||
/* Update the address register, and wrap, if needed */
|
||||
smsvdp.addr = (smsvdp.addr + 1) & 0x3FFF;
|
||||
}
|
||||
|
||||
static void tms9918a_vdp_reg_write(int reg, uint8 data) {
|
||||
if(reg > 7)
|
||||
return;
|
||||
|
||||
switch(reg) {
|
||||
case 0:
|
||||
smsvdp.regs[0] = data & 0x03;
|
||||
sms_vdp_set_vidmode(smsvdp.vidmode, SMS_VDP_MACHINE_TMS9918A);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
smsvdp.regs[1] = data & 0xfb;
|
||||
sms_vdp_set_vidmode(smsvdp.vidmode, SMS_VDP_MACHINE_TMS9918A);
|
||||
|
||||
if((smsvdp.status & 0x80) && (smsvdp.regs[1] & 0x20))
|
||||
sms_z80_assert_irq();
|
||||
break;
|
||||
|
||||
default:
|
||||
smsvdp.regs[reg] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void tms9918a_vdp_ctl_write(uint8 data) {
|
||||
/* First, update the code/address registers */
|
||||
if(!(smsvdp.flags & SMS_VDP_FLAG_BYTES_WRITTEN)) {
|
||||
/* A multiple of 2 bytes written */
|
||||
smsvdp.addr = (smsvdp.addr & 0x3F00) | (data);
|
||||
smsvdp.addr_latch = data;
|
||||
smsvdp.flags |= SMS_VDP_FLAG_BYTES_WRITTEN;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* One byte written, write the last one */
|
||||
smsvdp.addr = (data & 0x3F) << 8 | (smsvdp.addr_latch & 0xFF);
|
||||
smsvdp.code = (data & 0xC0) >> 6;
|
||||
smsvdp.flags &= (~SMS_VDP_FLAG_BYTES_WRITTEN);
|
||||
|
||||
/* Code 0 - Read a byte of vram, put it in the buffer, and increment
|
||||
the address (that last part is important!) */
|
||||
if(smsvdp.code == 0x00) {
|
||||
smsvdp.read_buf = smsvdp.vram[smsvdp.addr];
|
||||
smsvdp.addr = (smsvdp.addr + 1) & 0x3FFF;
|
||||
}
|
||||
/* Code 2 - Register Write */
|
||||
else if(smsvdp.code == 0x02) {
|
||||
int reg = data & 0x0F;
|
||||
tms9918a_vdp_reg_write(reg, smsvdp.addr & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 tms9918a_vdp_data_read(void) {
|
||||
uint8 tmp;
|
||||
|
||||
/* Clear the bytes written flag */
|
||||
smsvdp.flags &= (~SMS_VDP_FLAG_BYTES_WRITTEN);
|
||||
|
||||
/* Fetch what is already in the buffer */
|
||||
tmp = smsvdp.read_buf;
|
||||
|
||||
/* Fetch a new byte for the buffer */
|
||||
smsvdp.read_buf = smsvdp.vram[smsvdp.addr];
|
||||
|
||||
/* And finally, increment the address */
|
||||
smsvdp.addr = (smsvdp.addr + 1) & 0x3FFF;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
uint8 tms9918a_vdp_status_read(void) {
|
||||
uint8 tmp;
|
||||
|
||||
/* Check the interrupt flags */
|
||||
if((smsvdp.status & 0x80))
|
||||
sms_z80_clear_irq();
|
||||
|
||||
/* Clear the bytes written flag and line interrupt pending flag */
|
||||
smsvdp.flags &= ~SMS_VDP_FLAG_BYTES_WRITTEN;
|
||||
|
||||
/* Fetch our flags, so that we can clear stale ones */
|
||||
tmp = smsvdp.status;
|
||||
smsvdp.status &= (~0xE0);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#undef DRAW_PIXEL
|
||||
#undef CHECK_PIXEL
|
||||
|
||||
void tms9918a_vdp_activeframe(uint32_t *x, uint32_t *y, uint32_t *w,
|
||||
uint32_t *h) {
|
||||
*x = *y = 0;
|
||||
*w = 256;
|
||||
*h = 192;
|
||||
}
|
||||
|
||||
+10
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
@@ -72,7 +72,15 @@ extern void tms9918a_m1_draw_bg(int line, pixel_t *px);
|
||||
extern void tms9918a_m2_draw_bg(int line, pixel_t *px);
|
||||
extern void tms9918a_m3_draw_bg(int line, pixel_t *px);
|
||||
extern void tms9918a_m023_draw_spr(int line, pixel_t *px);
|
||||
void tms9918a_m023_skip_spr(int line);
|
||||
extern void tms9918a_m023_skip_spr(int line);
|
||||
|
||||
extern void tms9918a_vdp_data_write(uint8 data);
|
||||
extern void tms9918a_vdp_ctl_write(uint8 data);
|
||||
extern uint8 tms9918a_vdp_data_read(void);
|
||||
extern uint8 tms9918a_vdp_status_read(void);
|
||||
|
||||
extern void tms9918a_vdp_activeframe(uint32_t *x, uint32_t *y, uint32_t *w,
|
||||
uint32_t *h);
|
||||
|
||||
ENDCLINK
|
||||
|
||||
|
||||
+78
-17
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012 Lawrence Sebald
|
||||
Copyright (C) 2012, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -42,9 +42,34 @@ ADCMOP:
|
||||
FETCH_BYTE(_addr, _tmp);
|
||||
ADCOP:
|
||||
#ifndef CRAB6502_NO_DECIMAL
|
||||
/* Decimal mode is... ugly. But it should work. */
|
||||
if(cpu->p & 0x08) {
|
||||
/* XXXX: Support BCD mode */
|
||||
assert(0);
|
||||
/* Calculate the two halves as well as the Z flag. */
|
||||
uint8 _al = (cpu->a & 0x0F) + (_tmp & 0x0F) + (cpu->p & 0x01);
|
||||
_tmp32 = (cpu->a >> 4) + (_tmp >> 4);
|
||||
cpu->p = (cpu->p & 0x3C) |
|
||||
(ZNtable[(uint8)(cpu->a + _tmp + (cpu->p & 0x01))] & 0x02);
|
||||
|
||||
/* Adjust the lower half, if needed, adding the half carry to the
|
||||
upper half. */
|
||||
if(_al > 9) {
|
||||
_al += 6;
|
||||
++_tmp32;
|
||||
}
|
||||
|
||||
/* Set the N flag if the top bit of the upper half is set, as well
|
||||
as the V flag, if needed. */
|
||||
cpu->p |= (_tmp32 & 0x08) << 4 |
|
||||
(((_tmp ^ cpu->a ^ 0x80) & (_tmp ^ (_tmp32 << 4)) & 0x80) >> 1);
|
||||
|
||||
/* Adjust the upper half and set the C flag, if needed. */
|
||||
if(_tmp32 > 9) {
|
||||
_tmp32 += 6;
|
||||
cpu->p |= 0x01;
|
||||
}
|
||||
|
||||
/* Store the final result. */
|
||||
cpu->a = (uint8)((_tmp32 << 4) | (_al & 0x0F));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -193,14 +218,35 @@ ANDOP:
|
||||
case 0x6B: /* (U) ARR imm */
|
||||
FETCH_ARG8(_tmp);
|
||||
#ifndef CRAB6502_NO_DECIMAL
|
||||
/* Yup... this makes about... zero sense. */
|
||||
if(cpu->p & 0x08) {
|
||||
/* XXXX: Support BCD mode */
|
||||
assert(0);
|
||||
uint8 _al, _ah;
|
||||
|
||||
_tmp32 = _tmp = cpu->a & _tmp;
|
||||
_ah = _tmp >> 4;
|
||||
_al = _tmp & 0x0F;
|
||||
_tmp = (_tmp >> 1) | ((cpu->p & 0x01) << 7);
|
||||
|
||||
/* Set all the flags except for C. */
|
||||
cpu->p = (cpu->p & 0x3C) | ZNtable[_tmp] | ((_tmp32 ^ _tmp) & 0x40);
|
||||
|
||||
/* BCD adjust (sorta) the low order bits. */
|
||||
if(_al + (_al & 0x01) > 5)
|
||||
_tmp = (_tmp & 0xf0) | ((_tmp + 6) & 0x0F);
|
||||
|
||||
/* BCD adjust (sorta) the high order bits, and set C if needed. */
|
||||
if(_ah + (_ah & 0x01) > 5) {
|
||||
cpu->p |= 0x01;
|
||||
_tmp = (_tmp + 0x60);
|
||||
}
|
||||
|
||||
cpu->a = _tmp;
|
||||
cycles_done += 2;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
cpu->a = ((cpu->a & _tmp) >> 1) | (cpu->p << 7);
|
||||
cpu->p = (cpu->p & 0x3C) | ((cpu->a >> 6) & 0x01) |
|
||||
cpu->p = (cpu->p & 0x3C) | ((cpu->a >> 6) & 0x01) |
|
||||
(((cpu->a << 1) ^ cpu->a) & 0x40) | ZNtable[cpu->a];
|
||||
cycles_done += 2;
|
||||
break;
|
||||
@@ -443,13 +489,13 @@ CMPOP:
|
||||
inst = cpu->y;
|
||||
cycles_done += 2;
|
||||
goto CMPOP;
|
||||
|
||||
|
||||
case 0xC4: /* CPY zpg */
|
||||
FETCH_ARG8(_addr);
|
||||
inst = cpu->y;
|
||||
cycles_done += 3;
|
||||
goto CMPMOP;
|
||||
|
||||
|
||||
case 0xCC: /* CPY abs */
|
||||
FETCH_ARG16(_addr);
|
||||
inst = cpu->y;
|
||||
@@ -793,7 +839,7 @@ LDAMOP:
|
||||
FETCH_BYTE(_addr, cpu->a);
|
||||
LDAOP:
|
||||
cpu->p = (cpu->p & 0x7D) | ZNtable[cpu->a];
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case 0xB5: /* LDA zpg, X */
|
||||
@@ -1295,17 +1341,32 @@ SAXOP:
|
||||
SBCMOP:
|
||||
FETCH_BYTE(_addr, _tmp);
|
||||
SBCOP:
|
||||
#ifndef CRAB6502_NO_DECIMAL
|
||||
if(cpu->p & 0x08) {
|
||||
/* XXXX: Support BCD mode */
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
/* Flag calculation is the same regardless of whether or not we are in
|
||||
decimal mode. */
|
||||
_tmp32 = cpu->a - _tmp - ((cpu->p & 0x01) ^ 0x01);
|
||||
inst = cpu->p;
|
||||
cpu->p = (cpu->p & 0x3C) | ZNtable[(uint8)_tmp32] |
|
||||
(((~_tmp32) >> 8) & 0x01) |
|
||||
(((_tmp ^ cpu->a) & (cpu->a ^ _tmp32) & 0x80) >> 1);
|
||||
|
||||
#ifndef CRAB6502_NO_DECIMAL
|
||||
/* If we're in decimal mode, calculate the real value that we want in
|
||||
the accumulator after all is said and done. */
|
||||
if(cpu->p & 0x08) {
|
||||
uint8 _al = (cpu->a & 0x0F) - (_tmp & 0x0F) -
|
||||
((inst & 0x01) ^ 0x01);
|
||||
|
||||
if(_al & 0xF0)
|
||||
_al = (_al - 6) | 0x10;
|
||||
|
||||
_tmp = (cpu->a >> 4) - (_tmp >> 4) - ((_al & 0x10) >> 4);
|
||||
if(_tmp & 0xF0)
|
||||
_tmp -= 6;
|
||||
|
||||
_tmp32 = (_tmp << 4) | (_al & 0x0F);
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu->a = (uint8)_tmp32;
|
||||
break;
|
||||
|
||||
|
||||
+142
-32
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011, 2012 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2011, 2012, 2015,
|
||||
2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@@ -17,14 +18,16 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "CrabZ80.h"
|
||||
#include "CrabZ80_tables.h"
|
||||
#include "CrabZ80_macros.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
static CrabZ80_t *cpu = NULL;
|
||||
#include "CrabZ80.h"
|
||||
#include "CrabZ80_tables.h"
|
||||
#include "CrabZ80_macros.h"
|
||||
#include "CrabZ80_gbmacros.h"
|
||||
|
||||
static Z80 *cpu = NULL;
|
||||
|
||||
#ifndef CRABZ80_NO_READMAP_FALLBACK
|
||||
|
||||
@@ -38,9 +41,9 @@ static CrabZ80_t *cpu = NULL;
|
||||
uint16 pc2 = cpu->pc.w + 1; \
|
||||
uint8 pc_s = cpu->pc.w >> 8; \
|
||||
uint8 pc2_s = pc2 >> 8; \
|
||||
name = cpu->readmap[pc_s] ? \
|
||||
name = (cpu->readmap[pc_s] ? \
|
||||
cpu->readmap[pc_s][(uint8)cpu->pc.w] : \
|
||||
cpu->mread(cpu->pc.w) | \
|
||||
cpu->mread(cpu->pc.w)) | \
|
||||
((cpu->readmap[pc2_s] ? \
|
||||
cpu->readmap[pc2_s][(uint8)pc2] : \
|
||||
cpu->mread(pc2)) << 8); \
|
||||
@@ -62,6 +65,9 @@ static CrabZ80_t *cpu = NULL;
|
||||
|
||||
#endif
|
||||
|
||||
static uint32 CrabZ80_exec_z80(Z80 *cpuin, uint32 cycles);
|
||||
static uint32 CrabZ80_exec_lr35902(Z80 *cpuin, uint32 cycles);
|
||||
|
||||
static uint8 CrabZ80_dummy_read(uint16 addr) {
|
||||
(void)addr;
|
||||
return 0;
|
||||
@@ -95,57 +101,57 @@ static void CrabZ80_default_mwrite16(uint16 addr, uint16 data) {
|
||||
cpu->mwrite(addr, data & 0xFF);
|
||||
}
|
||||
|
||||
void CrabZ80_set_portread(CrabZ80_t *cpuz80, uint8 (*pread)(uint16 port)) {
|
||||
void CRABZ80_FUNC(set_portread)(Z80 *cpuz80, uint8 (*pread)(uint16 port)) {
|
||||
if(pread == NULL)
|
||||
cpuz80->pread = CrabZ80_dummy_read;
|
||||
else
|
||||
cpuz80->pread = pread;
|
||||
}
|
||||
|
||||
void CrabZ80_set_memread(CrabZ80_t *cpuz80, uint8 (*mread)(uint16 addr)) {
|
||||
void CRABZ80_FUNC(set_memread)(Z80 *cpuz80, uint8 (*mread)(uint16 addr)) {
|
||||
if(mread == NULL)
|
||||
cpuz80->mread = CrabZ80_dummy_read;
|
||||
else
|
||||
cpuz80->mread = mread;
|
||||
}
|
||||
|
||||
void CrabZ80_set_portwrite(CrabZ80_t *cpuz80,
|
||||
void (*pwrite)(uint16 port, uint8 data)) {
|
||||
void CRABZ80_FUNC(set_portwrite)(Z80 *cpuz80,
|
||||
void (*pwrite)(uint16 port, uint8 data)) {
|
||||
if(pwrite == NULL)
|
||||
cpuz80->pwrite = CrabZ80_dummy_write;
|
||||
else
|
||||
cpuz80->pwrite = pwrite;
|
||||
}
|
||||
|
||||
void CrabZ80_set_memwrite(CrabZ80_t *cpuz80,
|
||||
void (*mwrite)(uint16 addr, uint8 data)) {
|
||||
void CRABZ80_FUNC(set_memwrite)(Z80 *cpuz80,
|
||||
void (*mwrite)(uint16 addr, uint8 data)) {
|
||||
if(mwrite == NULL)
|
||||
cpuz80->mwrite = CrabZ80_dummy_write;
|
||||
else
|
||||
cpuz80->mwrite = mwrite;
|
||||
}
|
||||
|
||||
void CrabZ80_set_memread16(CrabZ80_t *cpuz80,
|
||||
uint16 (*mread16)(uint16 addr)) {
|
||||
void CRABZ80_FUNC(set_memread16)(Z80 *cpuz80,
|
||||
uint16 (*mread16)(uint16 addr)) {
|
||||
if(mread16 == NULL)
|
||||
cpuz80->mread16 = CrabZ80_default_mread16;
|
||||
else
|
||||
cpuz80->mread16 = mread16;
|
||||
}
|
||||
|
||||
void CrabZ80_set_memwrite16(CrabZ80_t *cpuz80,
|
||||
void (*mwrite16)(uint16 addr, uint16 data)) {
|
||||
void CRABZ80_FUNC(set_memwrite16)(Z80 *cpuz80,
|
||||
void (*mwrite16)(uint16 addr, uint16 data)) {
|
||||
if(mwrite16 == NULL)
|
||||
cpuz80->mwrite16 = CrabZ80_default_mwrite16;
|
||||
else
|
||||
cpuz80->mwrite16 = mwrite16;
|
||||
}
|
||||
|
||||
void CrabZ80_set_readmap(CrabZ80_t *cpuz80, uint8 *readmap[256]) {
|
||||
void CRABZ80_FUNC(set_readmap)(Z80 *cpuz80, uint8 *readmap[256]) {
|
||||
memcpy(cpuz80->readmap, readmap, 256 * sizeof(uint8 *));
|
||||
}
|
||||
|
||||
void CrabZ80_init(CrabZ80_t *cpuz80) {
|
||||
void CRABZ80_FUNC(init)(Z80 *cpuz80, int model) {
|
||||
cpuz80->pread = CrabZ80_dummy_read;
|
||||
cpuz80->mread = CrabZ80_dummy_read;
|
||||
cpuz80->pwrite = CrabZ80_dummy_write;
|
||||
@@ -154,9 +160,24 @@ void CrabZ80_init(CrabZ80_t *cpuz80) {
|
||||
cpuz80->mwrite16 = CrabZ80_default_mwrite16;
|
||||
|
||||
memset(cpuz80->readmap, 0, 256 * sizeof(uint8 *));
|
||||
|
||||
switch(model) {
|
||||
case CRABZ80_CPU_Z80:
|
||||
cpuz80->exec = &CrabZ80_exec_z80;
|
||||
break;
|
||||
|
||||
case CRABZ80_CPU_LR35902:
|
||||
cpuz80->exec = &CrabZ80_exec_lr35902;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown CPU type (%d), selecting Z80.\n", model);
|
||||
cpuz80->exec = &CrabZ80_exec_z80;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CrabZ80_reset(CrabZ80_t *cpuz80) {
|
||||
void CRABZ80_FUNC(reset)(Z80 *cpuz80) {
|
||||
cpuz80->pc.w = 0x0000;
|
||||
cpuz80->iff1 = 0;
|
||||
cpuz80->iff2 = 0;
|
||||
@@ -181,7 +202,7 @@ void CrabZ80_reset(CrabZ80_t *cpuz80) {
|
||||
cpuz80->irq_pending = 0;
|
||||
}
|
||||
|
||||
static uint32 CrabZ80_take_nmi(CrabZ80_t *cpuz80) {
|
||||
static uint32 CrabZ80_take_nmi(Z80 *cpuz80) {
|
||||
if(cpuz80->halt) {
|
||||
cpuz80->pc.w++;
|
||||
cpuz80->halt = 0;
|
||||
@@ -201,7 +222,7 @@ static uint32 CrabZ80_take_nmi(CrabZ80_t *cpuz80) {
|
||||
return 11;
|
||||
}
|
||||
|
||||
static uint32 CrabZ80_take_irq(CrabZ80_t *cpuz80) {
|
||||
static uint32 CrabZ80_take_irq(Z80 *cpuz80) {
|
||||
if(cpuz80->halt) {
|
||||
cpuz80->pc.w++;
|
||||
cpuz80->halt = 0;
|
||||
@@ -223,7 +244,7 @@ static uint32 CrabZ80_take_irq(CrabZ80_t *cpuz80) {
|
||||
case 0:
|
||||
case 1:
|
||||
cpuz80->sp.w -= 2;
|
||||
cpuz80->mwrite16(cpu->sp.w, cpu->pc.w);
|
||||
cpuz80->mwrite16(cpuz80->sp.w, cpuz80->pc.w);
|
||||
cpuz80->pc.w = 0x0038;
|
||||
return 13;
|
||||
|
||||
@@ -231,8 +252,8 @@ static uint32 CrabZ80_take_irq(CrabZ80_t *cpuz80) {
|
||||
{
|
||||
uint16 tmp = (cpuz80->ir.b.h << 8) + (cpuz80->irq_vector & 0xFF);
|
||||
cpuz80->sp.w -= 2;
|
||||
cpuz80->mwrite16(cpu->sp.w, cpu->pc.w);
|
||||
cpuz80->pc.w = cpu->mread16(tmp);
|
||||
cpuz80->mwrite16(cpuz80->sp.w, cpuz80->pc.w);
|
||||
cpuz80->pc.w = cpuz80->mread16(tmp);
|
||||
return 19;
|
||||
}
|
||||
|
||||
@@ -246,24 +267,37 @@ static uint32 CrabZ80_take_irq(CrabZ80_t *cpuz80) {
|
||||
}
|
||||
}
|
||||
|
||||
void CrabZ80_pulse_nmi(CrabZ80_t *cpuz80) {
|
||||
void CRABZ80_FUNC(pulse_nmi)(Z80 *cpuz80) {
|
||||
cpuz80->irq_pending |= 2;
|
||||
}
|
||||
|
||||
void CrabZ80_assert_irq(CrabZ80_t *cpuz80, uint32 vector) {
|
||||
void CRABZ80_FUNC(assert_irq)(Z80 *cpuz80, uint32 vector) {
|
||||
cpuz80->irq_pending |= 1;
|
||||
cpuz80->irq_vector = vector;
|
||||
}
|
||||
|
||||
void CrabZ80_clear_irq(CrabZ80_t *cpuz80) {
|
||||
void CRABZ80_FUNC(clear_irq)(Z80 *cpuz80) {
|
||||
cpuz80->irq_pending &= 2;
|
||||
}
|
||||
|
||||
void CrabZ80_release_cycles(CrabZ80_t *cpuz80) {
|
||||
void CRABZ80_FUNC(set_irqflag_lr35902)(Z80 *cpuz80, uint8 irqs) {
|
||||
cpuz80->irq_pending = irqs;
|
||||
}
|
||||
|
||||
void CRABZ80_FUNC(set_irqen_lr35902)(Z80 *cpuz80, uint8 irqs) {
|
||||
/* Reuse IFF2, since it doesn't exist on the LR35902. */
|
||||
cpuz80->iff2 = irqs;
|
||||
}
|
||||
|
||||
void CRABZ80_FUNC(release_cycles)(Z80 *cpuz80) {
|
||||
cpuz80->cycles_in = 0;
|
||||
}
|
||||
|
||||
uint32 CrabZ80_execute(CrabZ80_t *cpuin, uint32 cycles) {
|
||||
uint32 CRABZ80_FUNC(execute)(Z80 *cpuin, uint32 cycles) {
|
||||
return cpuin->exec(cpuin, cycles);
|
||||
}
|
||||
|
||||
static uint32 CrabZ80_exec_z80(Z80 *cpuin, uint32 cycles) {
|
||||
register uint32 cycles_done = 0;
|
||||
CrabZ80_t *oldcpu = NULL;
|
||||
uint32 oldcyclesin = 0, oldcycles = 0;
|
||||
@@ -311,3 +345,79 @@ out:
|
||||
|
||||
return cycles_done;
|
||||
}
|
||||
|
||||
static uint32 CrabZ80_exec_lr35902(Z80 *cpuin, uint32 cycles) {
|
||||
register uint32 cycles_done = 0;
|
||||
CrabZ80_t *oldcpu = NULL;
|
||||
uint32 oldcyclesin = 0, oldcycles = 0;
|
||||
|
||||
if(cpu) {
|
||||
oldcyclesin = cpu->cycles_in;
|
||||
oldcycles = cpu->cycles;
|
||||
oldcpu = cpu;
|
||||
}
|
||||
|
||||
cpu = cpuin;
|
||||
|
||||
cycles_done = 0;
|
||||
cpuin->cycles_in = cycles;
|
||||
cpuin->cycles = 0;
|
||||
|
||||
while(cycles_done < cpuin->cycles_in) {
|
||||
/* If interrupts are enabled, then check if we have any waiting. */
|
||||
if(!cpuin->ei && cpuin->iff1) {
|
||||
uint8 irqs;
|
||||
|
||||
/* Do we actually have any IRQs pending? */
|
||||
irqs = cpuin->irq_pending & cpuin->iff2;
|
||||
if(irqs) {
|
||||
uint8 i;
|
||||
|
||||
/* Un-halt the CPU if it is halted. */
|
||||
if(cpuin->halt) {
|
||||
cpuin->pc.w++;
|
||||
cpuin->halt = 0;
|
||||
}
|
||||
|
||||
/* Look through all interrupts being asserted (that are enabled)
|
||||
until we find the highest priority one being asserted. */
|
||||
for(i = 0; i < 5; ++i) {
|
||||
if((irqs & (1 << i))) {
|
||||
/* Clear the IRQ pending flag and disable interrupts. */
|
||||
cpuin->irq_pending &= ~(1 << i);
|
||||
cpuin->iff1 = 0;
|
||||
|
||||
/* Push the PC onto the stack and set the new PC. */
|
||||
cpuin->sp.w -= 2;
|
||||
cpuin->mwrite16(cpuin->sp.w, cpuin->pc.w);
|
||||
cpuin->pc.w = 0x40 + (i << 3);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cpuin->ei = 0;
|
||||
{
|
||||
uint8 inst;
|
||||
FETCH_ARG8(inst);
|
||||
++cpuin->ir.b.l;
|
||||
#define INSIDE_CRABZ80_GBEXECUTE
|
||||
#include "CrabZ80gbops.h"
|
||||
#undef INSIDE_CRABZ80_GBEXECUTE
|
||||
}
|
||||
|
||||
out:
|
||||
cpuin->cycles = cycles_done;
|
||||
}
|
||||
|
||||
cpu = oldcpu;
|
||||
|
||||
if(cpu) {
|
||||
cpu->cycles = oldcycles;
|
||||
cpu->cycles_in = oldcyclesin;
|
||||
}
|
||||
|
||||
return cycles_done;
|
||||
}
|
||||
|
||||
+81
-37
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009 Lawrence Sebald
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -52,8 +52,38 @@ typedef uint32_t int32;
|
||||
#endif /* CRABEMU_TYPEDEFS */
|
||||
#endif /* IN_CRABEMU */
|
||||
|
||||
/******************************************************************************
|
||||
DANGER WILL ROBINSON!
|
||||
|
||||
No User-serviceable parts below this block.
|
||||
|
||||
DANGER WILL ROBINSON!
|
||||
******************************************************************************/
|
||||
CLINKAGE
|
||||
|
||||
#ifndef CRABZ80_PREFIX
|
||||
#define CRABZ80_PREFIX CrabZ80
|
||||
#endif
|
||||
|
||||
/* Because any of this makes sense... */
|
||||
#define CRABZ80_SYM_3(x, y) x ## _ ## y
|
||||
#define CRABZ80_SYM_2(x, y) CRABZ80_SYM_3(x, y)
|
||||
#define CRABZ80_SYM(x) CRABZ80_SYM_2(CRABZ80_PREFIX, x)
|
||||
|
||||
#define CRABZ80_FUNC(fn) CRABZ80_SYM(fn)
|
||||
#define CRABZ80_CPU CRABZ80_SYM(t)
|
||||
#define Z80 CRABZ80_CPU
|
||||
|
||||
/* CPU Types.
|
||||
|
||||
These control various parts of the code to implement emulation for different
|
||||
Z80-like CPUs. */
|
||||
#define CRABZ80_CPU_Z80 0
|
||||
#define CRABZ80_CPU_LR35902 1
|
||||
#define CRABZ80_CPU_GB CRABZ80_CPU_LR35902
|
||||
|
||||
#define CRABZ80_CPU_MAX 1 /* Don't use this one... */
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
#ifdef __BIG_ENDIAN__
|
||||
@@ -66,7 +96,7 @@ typedef union {
|
||||
} b;
|
||||
uint8 bytes[2];
|
||||
uint16 w;
|
||||
} CrabZ80_reg_t;
|
||||
} CRABZ80_SYM(reg_t);
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
@@ -79,7 +109,7 @@ typedef union {
|
||||
#endif
|
||||
} b;
|
||||
uint16 w;
|
||||
} CrabZ80_regAF_t;
|
||||
} CRABZ80_SYM(regAF_t);
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
#define REG8(x) cpu->regs8[(x) & 0x07]
|
||||
@@ -91,28 +121,28 @@ typedef union {
|
||||
|
||||
#define REG16(x) cpu->regs16[(x) & 0x03]
|
||||
|
||||
typedef struct CrabZ80_struct {
|
||||
typedef struct CRABZ80_SYM(struct) {
|
||||
union {
|
||||
uint8 regs8[8];
|
||||
uint16 regs16[4];
|
||||
struct {
|
||||
CrabZ80_reg_t bc;
|
||||
CrabZ80_reg_t de;
|
||||
CrabZ80_reg_t hl;
|
||||
CrabZ80_regAF_t af;
|
||||
CRABZ80_SYM(reg_t) bc;
|
||||
CRABZ80_SYM(reg_t) de;
|
||||
CRABZ80_SYM(reg_t) hl;
|
||||
CRABZ80_SYM(regAF_t) af;
|
||||
};
|
||||
};
|
||||
CrabZ80_reg_t ix;
|
||||
CrabZ80_reg_t iy;
|
||||
CrabZ80_reg_t pc;
|
||||
CrabZ80_reg_t sp;
|
||||
CrabZ80_reg_t ir;
|
||||
CrabZ80_regAF_t afp;
|
||||
CrabZ80_reg_t bcp;
|
||||
CrabZ80_reg_t dep;
|
||||
CrabZ80_reg_t hlp;
|
||||
CRABZ80_SYM(reg_t) ix;
|
||||
CRABZ80_SYM(reg_t) iy;
|
||||
CRABZ80_SYM(reg_t) pc;
|
||||
CRABZ80_SYM(reg_t) sp;
|
||||
CRABZ80_SYM(reg_t) ir;
|
||||
CRABZ80_SYM(regAF_t) afp;
|
||||
CRABZ80_SYM(reg_t) bcp;
|
||||
CRABZ80_SYM(reg_t) dep;
|
||||
CRABZ80_SYM(reg_t) hlp;
|
||||
|
||||
CrabZ80_reg_t *offset;
|
||||
CRABZ80_SYM(reg_t) *offset;
|
||||
|
||||
uint8 iff1;
|
||||
uint8 iff2;
|
||||
@@ -137,8 +167,10 @@ typedef struct CrabZ80_struct {
|
||||
uint16 (*mread16)(uint16 addr);
|
||||
void (*mwrite16)(uint16 addr, uint16 data);
|
||||
|
||||
uint32 (*exec)(struct CRABZ80_SYM(struct) *, uint32);
|
||||
|
||||
uint8 *readmap[256];
|
||||
} CrabZ80_t;
|
||||
} CRABZ80_SYM(t);
|
||||
|
||||
/* Flag definitions */
|
||||
#define CRABZ80_CF 0
|
||||
@@ -150,35 +182,47 @@ typedef struct CrabZ80_struct {
|
||||
#define CRABZ80_ZF 6
|
||||
#define CRABZ80_SF 7
|
||||
|
||||
/* Flags when emulating an LR35902. Other flag bits are all 0. */
|
||||
#define CRABZ80_LR35902_CF 4
|
||||
#define CRABZ80_LR35902_HF 5
|
||||
#define CRABZ80_LR35902_NF 6
|
||||
#define CRABZ80_LR35902_ZF 7
|
||||
|
||||
/* Flag setting macros */
|
||||
#define CRABZ80_SET_FLAG(z80, n) (z80)->af.b.l |= (1 << (n))
|
||||
#define CRABZ80_CLEAR_FLAG(z80, n) (z80)->af.b.l &= (~(1 << (n)))
|
||||
#define CRABZ80_GET_FLAG(z80, n) ((z80)->af.b.l >> (n)) & 1
|
||||
|
||||
/* Function definitions */
|
||||
void CrabZ80_init(CrabZ80_t *cpu);
|
||||
void CrabZ80_reset(CrabZ80_t *cpu);
|
||||
void CRABZ80_FUNC(init)(Z80 *cpu, int model);
|
||||
void CRABZ80_FUNC(reset)(Z80 *cpu);
|
||||
|
||||
void CrabZ80_pulse_nmi(CrabZ80_t *cpu);
|
||||
void CrabZ80_assert_irq(CrabZ80_t *cpu, uint32 vector);
|
||||
void CrabZ80_clear_irq(CrabZ80_t *cpu);
|
||||
/* Z80 IRQs. */
|
||||
void CRABZ80_FUNC(pulse_nmi)(Z80 *cpu);
|
||||
void CRABZ80_FUNC(assert_irq)(Z80 *cpu, uint32 vector);
|
||||
void CRABZ80_FUNC(clear_irq)(Z80 *cpu);
|
||||
|
||||
uint32 CrabZ80_execute(CrabZ80_t *cpu, uint32 cycles);
|
||||
/* LR35902 IRQs. */
|
||||
void CRABZ80_FUNC(set_irqflag_lr35902)(Z80 *cpuz80, uint8 irqs);
|
||||
void CRABZ80_FUNC(set_irqen_lr35902)(Z80 *cpuz80, uint8 irqs);
|
||||
|
||||
void CrabZ80_release_cycles();
|
||||
uint32 CRABZ80_FUNC(execute)(Z80 *cpu, uint32 cycles);
|
||||
|
||||
void CrabZ80_set_portread(CrabZ80_t *cpu, uint8 (*pread)(uint16 port));
|
||||
void CrabZ80_set_memread(CrabZ80_t *cpu, uint8 (*mread)(uint16 addr));
|
||||
void CrabZ80_set_portwrite(CrabZ80_t *cpu,
|
||||
void (*pwrite)(uint16 port, uint8 data));
|
||||
void CrabZ80_set_memwrite(CrabZ80_t *cpu,
|
||||
void (*mwrite)(uint16 addr, uint8 data));
|
||||
void CRABZ80_FUNC(release_cycles)(Z80 *cpu);
|
||||
|
||||
void CrabZ80_set_memread16(CrabZ80_t *cpu, uint16 (*mread16)(uint16 addr));
|
||||
void CrabZ80_set_memwrite16(CrabZ80_t *cpu,
|
||||
void (*mwrite16)(uint16 addr, uint16 data));
|
||||
void CRABZ80_FUNC(set_portread)(Z80 *cpu, uint8 (*pread)(uint16 port));
|
||||
void CRABZ80_FUNC(set_memread)(Z80 *cpu, uint8 (*mread)(uint16 addr));
|
||||
void CRABZ80_FUNC(set_portwrite)(Z80 *cpu,
|
||||
void (*pwrite)(uint16 port, uint8 data));
|
||||
void CRABZ80_FUNC(set_memwrite)(Z80 *cpu,
|
||||
void (*mwrite)(uint16 addr, uint8 data));
|
||||
|
||||
void CrabZ80_set_readmap(CrabZ80_t *cpuz80, uint8 *readmap[256]);
|
||||
void CRABZ80_FUNC(set_memread16)(Z80 *cpu,
|
||||
uint16 (*mread16)(uint16 addr));
|
||||
void CRABZ80_FUNC(set_memwrite16)(Z80 *cpu,
|
||||
void (*mwrite16)(uint16 addr, uint16 data));
|
||||
|
||||
void CRABZ80_FUNC(set_readmap)(Z80 *cpu, uint8 *readmap[256]);
|
||||
|
||||
ENDCLINK
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2015, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define GB_INC8(val) { \
|
||||
uint32 _tmp = (val) + 1; \
|
||||
cpu->af.b.l = ((!_tmp) << 7) | \
|
||||
((_tmp & 0x0F) ? 0x00 : 0x20) | \
|
||||
(cpu->af.b.l & 0x10); \
|
||||
(val) = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_DEC8(val) { \
|
||||
uint32 _tmp = (val) - 1; \
|
||||
cpu->af.b.l = ((!_tmp) << 7) | \
|
||||
(cpu->af.b.l & 0x01) | \
|
||||
((((val) ^ _tmp) & 0x10) << 1) | \
|
||||
0x40; \
|
||||
(val) = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_RLCA() { \
|
||||
cpu->af.b.h = (cpu->af.b.h << 1) | (cpu->af.b.h >> 7); \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0xEF) | ((cpu->af.b.h & 0x01) << 4); \
|
||||
}
|
||||
|
||||
#define GB_RRCA() { \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0xEF) | ((cpu->af.b.h & 0x01) << 4); \
|
||||
cpu->af.b.h = (cpu->af.b.h >> 1) | (cpu->af.b.h << 7); \
|
||||
}
|
||||
|
||||
#define GB_RLA() { \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0xEF) | ((cpu->af.b.h >> 3) & 0x10); \
|
||||
cpu->af.b.h = (cpu->af.b.h << 1) | (cpu->af.b.l & 0x01); \
|
||||
}
|
||||
|
||||
#define GB_RRA() { \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0xEF) | ((cpu->af.b.h & 0x01) << 4); \
|
||||
cpu->af.b.h = (cpu->af.b.h >> 1) | (cpu->af.b.l << 7); \
|
||||
}
|
||||
|
||||
#define GB_CPL() { \
|
||||
cpu->af.b.h ^= 0xFF; \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0x9F) | 0x60; \
|
||||
}
|
||||
|
||||
#define GB_SCF() { \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0x80) | 0x10; \
|
||||
}
|
||||
|
||||
#define GB_CCF() { \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0x80) | ((cpu->af.b.l ^ 0x10) & 0x10); \
|
||||
}
|
||||
|
||||
#define GB_ADD() { \
|
||||
uint32 _tmp = cpu->af.b.h + _value; \
|
||||
cpu->af.b.l = ((!((uint8)_tmp)) << 7) | \
|
||||
(((cpu->af.b.h ^ _tmp ^ _value) & 0x10) << 1) | \
|
||||
((_tmp >> 4) & 0x10); \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_ADC() { \
|
||||
uint32 _tmp = cpu->af.b.h + _value + (cpu->af.b.l & 0x01); \
|
||||
cpu->af.b.l = ((!((uint8)_tmp)) << 7) | \
|
||||
(((cpu->af.b.h ^ _tmp ^ _value) & 0x10) << 1) | \
|
||||
((_tmp >> 4) & 0x10); \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_SUB() { \
|
||||
uint32 _tmp = cpu->af.b.h - _value; \
|
||||
cpu->af.b.l = ((!((uint8)_tmp)) << 7) | \
|
||||
(((cpu->af.b.h ^ _tmp ^ _value) & 0x10) << 1) | \
|
||||
0x40 | ((_tmp >> 4) & 0x10); \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_SBC() { \
|
||||
uint32 _tmp = cpu->af.b.h - (cpu->af.b.l & 0x01) - _value; \
|
||||
cpu->af.b.l = ((!((uint8)_tmp)) << 7) | \
|
||||
(((cpu->af.b.h ^ _tmp ^ _value) & 0x10) << 1) | \
|
||||
0x40 | ((_tmp >> 4) & 0x10); \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_AND() { \
|
||||
uint32 _tmp = cpu->af.b.h & _value; \
|
||||
cpu->af.b.l = ((!_tmp) << 7) | 0x10; \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_XOR() { \
|
||||
uint32 _tmp = cpu->af.b.h ^ _value; \
|
||||
cpu->af.b.l = ((!_tmp) << 7); \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_OR() { \
|
||||
uint32 _tmp = cpu->af.b.h | _value; \
|
||||
cpu->af.b.l = ((!_tmp) << 7); \
|
||||
cpu->af.b.h = _tmp; \
|
||||
}
|
||||
|
||||
#define GB_CP() { \
|
||||
uint32 _tmp = cpu->af.b.h - _value; \
|
||||
cpu->af.b.l = ((!((uint8)_tmp)) << 7) | \
|
||||
(((cpu->af.b.h ^ _tmp ^ _value) & 0x10) << 1) | \
|
||||
0x40 | ((_tmp >> 4) & 0x10); \
|
||||
}
|
||||
|
||||
#define GB_ADDHL() { \
|
||||
uint32 _tmp = cpu->hl.w + _value; \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0x8F) | \
|
||||
(((cpu->hl.w ^ _tmp ^ _value) >> 7) & 0x20) | \
|
||||
((_tmp >> 12) & 0x10); \
|
||||
cpu->hl.w = _tmp; \
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
Copyright (C) 2005, 2006, 2007, 2008 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
#define OP_ADDIx() { \
|
||||
uint32 _tmp = cpu->offset->w + _value; \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0xC4) | \
|
||||
cpu->af.b.l = (cpu->af.b.l & 0xC4) | \
|
||||
(((cpu->offset->w ^ _tmp ^ _value) >> 8) & 0x10) | \
|
||||
((_tmp >> 8) & 0x28) | \
|
||||
((_tmp >> 16) & 0x01); \
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Copyright (C) 2005, 2006 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
|
||||
@@ -0,0 +1,877 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2015, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef INSIDE_CRABZ80_GBEXECUTE
|
||||
#error This file can only be compiled inside of CrabZ80.c. Do not try to include
|
||||
#error this file in other files.
|
||||
#endif
|
||||
|
||||
{
|
||||
uint32 _value;
|
||||
uint32 _tmp;
|
||||
|
||||
switch(inst) {
|
||||
case 0x00: /* NOP */ //d
|
||||
case 0x40: /* LD B, B */ //d
|
||||
case 0x49: /* LD C, C */ //d
|
||||
case 0x52: /* LD D, D */ //d
|
||||
case 0x5B: /* LD E, E */ //d
|
||||
case 0x64: /* LD H, H */ //d
|
||||
case 0x6D: /* LD L, L */ //d
|
||||
case 0x7F: /* LD A, A */ //d
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x01: /* LD BC, nn */ //d
|
||||
case 0x11: /* LD DE, nn */ //d
|
||||
case 0x21: /* LD HL, nn */ //d
|
||||
FETCH_ARG16(_value);
|
||||
REG16(inst >> 4) = _value;
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0x31: /* LD SP, nn */ //d
|
||||
FETCH_ARG16(_value);
|
||||
cpu->sp.w = _value;
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0x02: /* LD (BC), A */ //d
|
||||
case 0x12: /* LD (DE), A */ //d
|
||||
cpu->mwrite(REG16(inst >> 4), cpu->af.b.h);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x03: /* INC BC */ //d
|
||||
++cpu->bc.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x13: /* INC DE */ //d
|
||||
++cpu->de.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x23: /* INC HL */ //d
|
||||
++cpu->hl.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x33: /* INC SP */ //d
|
||||
++cpu->sp.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x04: /* INC B */ //d
|
||||
case 0x0C: /* INC C */ //d
|
||||
case 0x14: /* INC D */ //d
|
||||
case 0x1C: /* INC E */ //d
|
||||
case 0x24: /* INC H */ //d
|
||||
case 0x2C: /* INC L */ //d
|
||||
case 0x3C: /* INC A */ //d
|
||||
_value = REG8(inst >> 3);
|
||||
GB_INC8(_value);
|
||||
REG8(inst >> 3) = _value;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x34: /* INC (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
GB_INC8(_value);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0x05: /* DEC B */ //d
|
||||
case 0x0D: /* DEC C */ //d
|
||||
case 0x15: /* DEC D */ //d
|
||||
case 0x1D: /* DEC E */ //d
|
||||
case 0x25: /* DEC H */ //d
|
||||
case 0x2D: /* DEC L */ //d
|
||||
case 0x3D: /* DEC A */ //d
|
||||
_value = REG8(inst >> 3);
|
||||
GB_DEC8(_value);
|
||||
REG8(inst >> 3) = _value;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x35: /* DEC (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
GB_DEC8(_value);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0x06: /* LD B, n */ //d
|
||||
case 0x0E: /* LD C, n */ //d
|
||||
case 0x16: /* LD D, n */ //d
|
||||
case 0x1E: /* LD E, n */ //d
|
||||
case 0x26: /* LD H, n */ //d
|
||||
case 0x2E: /* LD L, n */ //d
|
||||
case 0x3E: /* LD A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
REG8(inst >> 3) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x36: /* LD (HL), n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0x07: /* RLCA */ //d
|
||||
GB_RLCA();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x09: /* ADD HL, BC */ //d
|
||||
case 0x19: /* ADD HL, DE */ //d
|
||||
case 0x29: /* ADD HL, HL */ //d
|
||||
_value = REG16(inst >> 4);
|
||||
|
||||
ADDHLOP:
|
||||
cpu->internal_reg = cpu->hl.b.h;
|
||||
GB_ADDHL();
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x39: /* ADD HL, SP */ //d
|
||||
_value = cpu->sp.w;
|
||||
goto ADDHLOP;
|
||||
|
||||
case 0x0A: /* LD A, (BC) */ //d
|
||||
case 0x1A: /* LD A, (DE) */ //d
|
||||
cpu->af.b.h = cpu->mread(REG16(inst >> 4));
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x0B: /* DEC BC */ //d
|
||||
--cpu->bc.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x1B: /* DEC DE */ //d
|
||||
--cpu->de.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x2B: /* DEC HL */ //d
|
||||
--cpu->hl.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x3B: /* DEC SP */ //d
|
||||
--cpu->sp.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x0F: /* RRCA */ //d
|
||||
GB_RRCA();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x18: /* JR e */ //d
|
||||
JROP:
|
||||
FETCH_ARG8(_value);
|
||||
cpu->pc.w += (int8)_value;
|
||||
cycles_done += 12;
|
||||
cpu->internal_reg = cpu->pc.b.h;
|
||||
goto out;
|
||||
|
||||
case 0x20: /* JR NZ, e */ //d
|
||||
if(!(cpu->af.b.l & 0x80)) {
|
||||
goto JROP;
|
||||
}
|
||||
|
||||
++cpu->pc.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x28: /* JR Z, e */ //d
|
||||
if(cpu->af.b.l & 0x80) {
|
||||
goto JROP;
|
||||
}
|
||||
|
||||
++cpu->pc.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x30: /* JR NC, e */ //d
|
||||
if(!(cpu->af.b.l & 0x10)) {
|
||||
goto JROP;
|
||||
}
|
||||
|
||||
++cpu->pc.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x38: /* JR C, e */ //d
|
||||
if(cpu->af.b.l & 0x10) {
|
||||
goto JROP;
|
||||
}
|
||||
|
||||
++cpu->pc.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x17: /* RLA */ //d
|
||||
GB_RLA();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x1F: /* RRA */ //d
|
||||
GB_RRA();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x27: /* DAA */ //d
|
||||
{
|
||||
int low = cpu->af.b.h & 0x0F;
|
||||
int high = cpu->af.b.h >> 4;
|
||||
int cf = cpu->af.b.l & 0x10;
|
||||
int hf = cpu->af.b.l & 0x20;
|
||||
int nf = cpu->af.b.l & 0x40;
|
||||
|
||||
if(cf) {
|
||||
_value = (low < 0x0A && !hf) ? 0x60 : 0x66;
|
||||
}
|
||||
else {
|
||||
if(low < 0x0A) {
|
||||
if(high < 0x0A) {
|
||||
_value = (hf) ? 0x06 : 0x00;
|
||||
}
|
||||
else {
|
||||
_value = (hf) ? 0x66 : 0x60;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_value = (high < 0x09) ? 0x06 : 0x66;
|
||||
}
|
||||
}
|
||||
|
||||
if(nf) {
|
||||
cpu->af.b.h -= _value;
|
||||
}
|
||||
else {
|
||||
cpu->af.b.h += _value;
|
||||
}
|
||||
|
||||
cpu->af.b.l = ((!cpu->af.b.h) << 7) | (nf);
|
||||
|
||||
if(_value >= 0x60)
|
||||
cpu->af.b.l |= 0x01;
|
||||
|
||||
if(nf) {
|
||||
if(hf && low < 0x06) {
|
||||
cpu->af.b.l |= 0x10;
|
||||
}
|
||||
}
|
||||
else if(low >= 10) {
|
||||
cpu->af.b.l |= 0x10;
|
||||
}
|
||||
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
}
|
||||
|
||||
case 0x2F: /* CPL */ //d
|
||||
GB_CPL();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x37: /* SCF */ //d
|
||||
GB_SCF();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x3F: /* CCF */ //d
|
||||
GB_CCF();
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x41: /* LD B, C */ //d
|
||||
case 0x42: /* LD B, D */ //d
|
||||
case 0x43: /* LD B, E */ //d
|
||||
case 0x44: /* LD B, H */ //d
|
||||
case 0x45: /* LD B, L */ //d
|
||||
case 0x47: /* LD B, A */ //d
|
||||
case 0x48: /* LD C, B */ //d
|
||||
case 0x4A: /* LD C, D */ //d
|
||||
case 0x4B: /* LD C, E */ //d
|
||||
case 0x4C: /* LD C, H */ //d
|
||||
case 0x4D: /* LD C, L */ //d
|
||||
case 0x4F: /* LD C, A */ //d
|
||||
case 0x50: /* LD D, B */ //d
|
||||
case 0x51: /* LD D, C */ //d
|
||||
case 0x53: /* LD D, E */ //d
|
||||
case 0x54: /* LD D, H */ //d
|
||||
case 0x55: /* LD D, L */ //d
|
||||
case 0x57: /* LD D, A */ //d
|
||||
case 0x58: /* LD E, B */ //d
|
||||
case 0x59: /* LD E, C */ //d
|
||||
case 0x5A: /* LD E, D */ //d
|
||||
case 0x5C: /* LD E, H */ //d
|
||||
case 0x5D: /* LD E, L */ //d
|
||||
case 0x5F: /* LD E, A */ //d
|
||||
case 0x60: /* LD H, B */ //d
|
||||
case 0x61: /* LD H, C */ //d
|
||||
case 0x62: /* LD H, D */ //d
|
||||
case 0x63: /* LD H, E */ //d
|
||||
case 0x65: /* LD H, L */ //d
|
||||
case 0x67: /* LD H, A */ //d
|
||||
case 0x68: /* LD L, B */ //d
|
||||
case 0x69: /* LD L, C */ //d
|
||||
case 0x6A: /* LD L, D */ //d
|
||||
case 0x6B: /* LD L, E */ //d
|
||||
case 0x6C: /* LD L, H */ //d
|
||||
case 0x6F: /* LD L, A */ //d
|
||||
case 0x78: /* LD A, B */ //d
|
||||
case 0x79: /* LD A, C */ //d
|
||||
case 0x7A: /* LD A, D */ //d
|
||||
case 0x7B: /* LD A, E */ //d
|
||||
case 0x7C: /* LD A, H */ //d
|
||||
case 0x7D: /* LD A, L */ //d
|
||||
REG8(inst >> 3) = REG8(inst);
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x46: /* LD B, (HL) */ //d
|
||||
case 0x4E: /* LD C, (HL) */ //d
|
||||
case 0x56: /* LD D, (HL) */ //d
|
||||
case 0x5E: /* LD E, (HL) */ //d
|
||||
case 0x66: /* LD H, (HL) */ //d
|
||||
case 0x6E: /* LD L, (HL) */ //d
|
||||
case 0x7E: /* LD A, (HL) */ //d
|
||||
REG8(inst >> 3) = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x70: /* LD (HL), B */ //d
|
||||
case 0x71: /* LD (HL), C */ //d
|
||||
case 0x72: /* LD (HL), D */ //d
|
||||
case 0x73: /* LD (HL), E */ //d
|
||||
case 0x74: /* LD (HL), H */ //d
|
||||
case 0x75: /* LD (HL), L */ //d
|
||||
case 0x77: /* LD (HL), A */ //d
|
||||
cpu->mwrite(cpu->hl.w, REG8(inst));
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x76: /* HALT */ //d
|
||||
--cpu->pc.w;
|
||||
cpu->halt = 1;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x80: /* ADD A, B */ //d
|
||||
case 0x81: /* ADD A, C */ //d
|
||||
case 0x82: /* ADD A, D */ //d
|
||||
case 0x83: /* ADD A, E */ //d
|
||||
case 0x84: /* ADD A, H */ //d
|
||||
case 0x85: /* ADD A, L */ //d
|
||||
case 0x87: /* ADD A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
ADDOP:
|
||||
GB_ADD();
|
||||
goto out;
|
||||
|
||||
case 0x86: /* ADD A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto ADDOP;
|
||||
|
||||
case 0xC6: /* ADD A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto ADDOP;
|
||||
|
||||
case 0x88: /* ADC A, B */ //d
|
||||
case 0x89: /* ADC A, C */ //d
|
||||
case 0x8A: /* ADC A, D */ //d
|
||||
case 0x8B: /* ADC A, E */ //d
|
||||
case 0x8C: /* ADC A, H */ //d
|
||||
case 0x8D: /* ADC A, L */ //d
|
||||
case 0x8F: /* ADC A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
ADCOP:
|
||||
GB_ADC();
|
||||
goto out;
|
||||
|
||||
case 0x8E: /* ADC A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto ADCOP;
|
||||
|
||||
case 0xCE: /* ADC A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto ADCOP;
|
||||
|
||||
case 0x90: /* SUB A, B */ //d
|
||||
case 0x91: /* SUB A, C */ //d
|
||||
case 0x92: /* SUB A, D */ //d
|
||||
case 0x93: /* SUB A, E */ //d
|
||||
case 0x94: /* SUB A, H */ //d
|
||||
case 0x95: /* SUB A, L */ //d
|
||||
case 0x97: /* SUB A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
SUBOP:
|
||||
GB_SUB();
|
||||
goto out;
|
||||
|
||||
case 0x96: /* SUB A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto SUBOP;
|
||||
|
||||
case 0xD6: /* SUB A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto SUBOP;
|
||||
|
||||
case 0x98: /* SBC A, B */ //d
|
||||
case 0x99: /* SBC A, C */ //d
|
||||
case 0x9A: /* SBC A, D */ //d
|
||||
case 0x9B: /* SBC A, E */ //d
|
||||
case 0x9C: /* SBC A, H */ //d
|
||||
case 0x9D: /* SBC A, L */ //d
|
||||
case 0x9F: /* SBC A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
SBCOP:
|
||||
GB_SBC();
|
||||
goto out;
|
||||
|
||||
case 0x9E: /* SBC A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto SBCOP;
|
||||
|
||||
case 0xDE: /* SBC A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto SBCOP;
|
||||
|
||||
case 0xA0: /* AND A, B */ //d
|
||||
case 0xA1: /* AND A, C */ //d
|
||||
case 0xA2: /* AND A, D */ //d
|
||||
case 0xA3: /* AND A, E */ //d
|
||||
case 0xA4: /* AND A, H */ //d
|
||||
case 0xA5: /* AND A, L */ //d
|
||||
case 0xA7: /* AND A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
ANDOP:
|
||||
GB_AND();
|
||||
goto out;
|
||||
|
||||
case 0xA6: /* AND A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto ANDOP;
|
||||
|
||||
case 0xE6: /* AND A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto ANDOP;
|
||||
|
||||
case 0xA8: /* XOR A, B */ //d
|
||||
case 0xA9: /* XOR A, C */ //d
|
||||
case 0xAA: /* XOR A, D */ //d
|
||||
case 0xAB: /* XOR A, E */ //d
|
||||
case 0xAC: /* XOR A, H */ //d
|
||||
case 0xAD: /* XOR A, L */ //d
|
||||
case 0xAF: /* XOR A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
XOROP:
|
||||
GB_XOR();
|
||||
goto out;
|
||||
|
||||
case 0xAE: /* XOR A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto XOROP;
|
||||
|
||||
case 0xEE: /* XOR A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto XOROP;
|
||||
|
||||
case 0xB0: /* OR A, B */ //d
|
||||
case 0xB1: /* OR A, C */ //d
|
||||
case 0xB2: /* OR A, D */ //d
|
||||
case 0xB3: /* OR A, E */ //d
|
||||
case 0xB4: /* OR A, H */ //d
|
||||
case 0xB5: /* OR A, L */ //d
|
||||
case 0xB7: /* OR A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
OROP:
|
||||
GB_OR();
|
||||
goto out;
|
||||
|
||||
case 0xB6: /* OR A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto OROP;
|
||||
|
||||
case 0xF6: /* OR A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto OROP;
|
||||
|
||||
case 0xB8: /* CP A, B */ //d
|
||||
case 0xB9: /* CP A, C */ //d
|
||||
case 0xBA: /* CP A, D */ //d
|
||||
case 0xBB: /* CP A, E */ //d
|
||||
case 0xBC: /* CP A, H */ //d
|
||||
case 0xBD: /* CP A, L */ //d
|
||||
case 0xBF: /* CP A, A */ //d
|
||||
_value = REG8(inst);
|
||||
cycles_done += 4;
|
||||
|
||||
CPOP:
|
||||
GB_CP();
|
||||
goto out;
|
||||
|
||||
case 0xBE: /* CP A, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cycles_done += 8;
|
||||
goto CPOP;
|
||||
|
||||
case 0xFE: /* CP A, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cycles_done += 8;
|
||||
goto CPOP;
|
||||
|
||||
case 0xC0: /* RET NZ */ //d
|
||||
if(!(cpu->af.b.l & 0x80)) {
|
||||
cycles_done += 4;
|
||||
goto RETOP;
|
||||
}
|
||||
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xC8: /* RET Z */ //d
|
||||
if(!(cpu->af.b.l & 0x80)) {
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cycles_done += 4;
|
||||
/* Fall through... */
|
||||
|
||||
case 0xC9: /* RET */ //d
|
||||
RETOP:
|
||||
cpu->pc.w = cpu->mread16(cpu->sp.w);
|
||||
cpu->sp.w += 2;
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xD0: /* RET NC */ //d
|
||||
if(!(cpu->af.b.l & 0x10)) {
|
||||
cycles_done += 4;
|
||||
goto RETOP;
|
||||
}
|
||||
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xD8: /* RET C */ //d
|
||||
if(cpu->af.b.l & 0x10) {
|
||||
cycles_done += 4;
|
||||
goto RETOP;
|
||||
}
|
||||
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xC1: /* POP BC */ //d
|
||||
case 0xD1: /* POP DE */ //d
|
||||
case 0xE1: /* POP HL */ //d
|
||||
REG16(inst >> 4) = cpu->mread16(cpu->sp.w);
|
||||
cpu->sp.w += 2;
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0xF1: /* POP AF */ //d
|
||||
cpu->af.b.l = cpu->mread(cpu->sp.w++);
|
||||
cpu->af.b.h = cpu->mread(cpu->sp.w++);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0xC2: /* JP NZ, ee */ //d
|
||||
if(cpu->af.b.l & 0x80)
|
||||
goto out_nocondjump;
|
||||
|
||||
cycles_done += 4;
|
||||
/* Fall through... */
|
||||
|
||||
case 0xC3: /* JP ee */ //d
|
||||
JPOP:
|
||||
FETCH_ARG16(_value);
|
||||
cpu->pc.w = _value;
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xCA: /* JP Z, ee */ //d
|
||||
if(cpu->af.b.l & 0x80) {
|
||||
cycles_done += 4;
|
||||
goto JPOP;
|
||||
}
|
||||
|
||||
goto out_nocondjump;
|
||||
|
||||
case 0xD2: /* JP NC, ee */ //d
|
||||
if(!(cpu->af.b.l & 0x10)) {
|
||||
cycles_done += 4;
|
||||
goto JPOP;
|
||||
}
|
||||
|
||||
goto out_nocondjump;
|
||||
|
||||
case 0xDA: /* JP C, ee */ //d
|
||||
if(cpu->af.b.l & 0x10) {
|
||||
cycles_done += 4;
|
||||
goto JPOP;
|
||||
}
|
||||
|
||||
goto out_nocondjump;
|
||||
|
||||
case 0xC4: /* CALL NZ, ee */ //d
|
||||
if(!(cpu->af.b.l & 0x80))
|
||||
goto CALLOP;
|
||||
|
||||
goto out_nocondjump;
|
||||
|
||||
case 0xCC: /* CALL Z, ee */ //d
|
||||
if(!(cpu->af.b.l & 0x80))
|
||||
goto out_nocondjump;
|
||||
|
||||
/* Fall through... */
|
||||
|
||||
case 0xCD: /* CALL ee */ //d
|
||||
CALLOP:
|
||||
FETCH_ARG16(_value);
|
||||
cpu->sp.w -= 2;
|
||||
cpu->mwrite16(cpu->sp.w, cpu->pc.w);
|
||||
cpu->pc.w = _value;
|
||||
cycles_done += 24;
|
||||
goto out;
|
||||
|
||||
case 0xD4: /* CALL NC, ee */ //d
|
||||
if(!(cpu->af.b.l & 0x10))
|
||||
goto CALLOP;
|
||||
|
||||
goto out_nocondjump;
|
||||
|
||||
case 0xDC: /* CALL C, ee */ //d
|
||||
if(cpu->af.b.l & 0x10)
|
||||
goto CALLOP;
|
||||
|
||||
goto out_nocondjump;
|
||||
|
||||
case 0xC5: /* PUSH BC */ //d
|
||||
case 0xD5: /* PUSH DE */ //d
|
||||
case 0xE5: /* PUSH HL */ //d
|
||||
cpu->sp.w -= 2;
|
||||
cpu->mwrite16(cpu->sp.w, REG16(inst >> 4));
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xF5: /* PUSH AF */ //d
|
||||
cpu->mwrite(--cpu->sp.w, cpu->af.b.h);
|
||||
cpu->mwrite(--cpu->sp.w, cpu->af.b.l);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xC7: /* RST 0h */ //d
|
||||
case 0xCF: /* RST 8h */ //d
|
||||
case 0xD7: /* RST 10h */ //d
|
||||
case 0xDF: /* RST 18h */ //d
|
||||
case 0xE7: /* RST 20h */ //d
|
||||
case 0xEF: /* RST 28h */ //d
|
||||
case 0xF7: /* RST 30h */ //d
|
||||
case 0xFF: /* RST 38h */ //d
|
||||
cpu->sp.w -= 2;
|
||||
cpu->mwrite16(cpu->sp.w, cpu->pc.w);
|
||||
cpu->pc.w = inst & 0x38;
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xE9: /* JP (HL) */ //d
|
||||
cpu->pc.w = cpu->hl.w;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0xF3: /* DI */ //d
|
||||
cpu->iff1 = 0;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0xF9: /* LD SP, HL */ //d
|
||||
cpu->sp.w = cpu->hl.w;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xFB: /* EI */ //d
|
||||
cpu->iff1 = cpu->ei = 1;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
/* Weird instructions that don't match Z80. */
|
||||
case 0x08: /* LD (nn), SP */ //d
|
||||
FETCH_ARG16(_value)
|
||||
cpu->mwrite16(_value, cpu->sp.w);
|
||||
cycles_done += 20;
|
||||
goto out;
|
||||
|
||||
case 0x10: /* STOP */ //d, sorta.
|
||||
// XXXX
|
||||
--cpu->pc.w;
|
||||
cpu->halt = 1;
|
||||
cycles_done += 4;
|
||||
goto out;
|
||||
|
||||
case 0x22: /* LD (HL+), A */ //d
|
||||
cpu->mwrite(cpu->hl.w++, cpu->af.b.h);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x2A: /* LD A, (HL+) */ //d
|
||||
cpu->af.b.l = cpu->mread(cpu->hl.w++);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x32: /* LD (HL-), A */ //d
|
||||
cpu->mwrite(cpu->hl.w--, cpu->af.b.h);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x3A: /* LD A, (HL-) */ //d
|
||||
cpu->af.b.l = cpu->mread(cpu->hl.w--);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xD9: /* RETI */ //d
|
||||
cpu->pc.w = cpu->mread16(cpu->sp.w);
|
||||
cpu->sp.w += 2;
|
||||
cpu->iff1 = 1;
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xE0: /* LD (0xFF00 + n), A */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cpu->mwrite(0xFF00 + _value, cpu->af.b.h);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0xE2: /* LD (0xFF00 + C), A */ //d
|
||||
cpu->mwrite(0xFF00 + cpu->bc.b.l, cpu->af.b.h);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xE8: /* ADD SP, n */ //d
|
||||
FETCH_ARG8(_value);
|
||||
_tmp = cpu->sp.b.l + ((int8)_value);
|
||||
/* ... Why? ... */
|
||||
cpu->af.b.l = (((cpu->sp.b.l ^ _tmp ^ _value) & 0x10) << 1) |
|
||||
((_tmp >> 4) & 0x10);
|
||||
cpu->sp.w += (int8)_value;
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xEA: /* LD (nn), A */ //d
|
||||
FETCH_ARG16(_value);
|
||||
cpu->mwrite(_value, cpu->af.b.h);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xF0: /* LD A, (0xFF00 + n) */ //d
|
||||
FETCH_ARG8(_value);
|
||||
cpu->af.b.h = cpu->mread(0xFF00 + _value);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0xF2: /* LD A, (0xFF00 + C) */ //d
|
||||
cpu->af.b.h = cpu->mread(0xFF00 + cpu->bc.b.l);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xF8: /* LD HL, SP + nn */ //d
|
||||
FETCH_ARG8(_value);
|
||||
_tmp = cpu->sp.b.l + ((int8)_value);
|
||||
/* ... Why? ... */
|
||||
cpu->af.b.l = (((cpu->sp.b.l ^ _tmp ^ _value) & 0x10) << 1) |
|
||||
((_tmp >> 4) & 0x10);
|
||||
cpu->hl.w = cpu->sp.w + ((int8)_value);
|
||||
cycles_done += 12;
|
||||
goto out;
|
||||
|
||||
case 0xFA: /* LD A, (nn) */ //d
|
||||
FETCH_ARG16(_value);
|
||||
cpu->af.b.h = cpu->mread(_value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
/* Illegal opcodes.... */
|
||||
case 0xD3:
|
||||
case 0xDB:
|
||||
case 0xDD:
|
||||
case 0xE3:
|
||||
case 0xE4:
|
||||
case 0xEB:
|
||||
case 0xEC:
|
||||
case 0xED:
|
||||
case 0xF4:
|
||||
case 0xFC:
|
||||
case 0xFD:
|
||||
/* XXXX */
|
||||
goto out;
|
||||
|
||||
case 0xCB: /* CB-prefix */
|
||||
goto execCB;
|
||||
}
|
||||
|
||||
execCB:
|
||||
#include "CrabZ80gbopsCB.h"
|
||||
/* We shouldn't get here. */
|
||||
|
||||
/* Conditional JP and CALL instructions that don't end up jumping end up
|
||||
coming here instead. This falls through back to CrabZ80.c (essentially to the
|
||||
same place that goto out would put us). */
|
||||
out_nocondjump: //d
|
||||
cycles_done += 12;
|
||||
cpu->pc.w += 2;
|
||||
|
||||
/* Fall through... */
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2005, 2006, 2007, 2008, 2016 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabEmu; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef INSIDE_CRABZ80_GBEXECUTE
|
||||
#error This file can only be compiled inside of CrabZ80.c. Do not try to include
|
||||
#error this file in other files.
|
||||
#endif
|
||||
|
||||
++cpu->ir.b.l;
|
||||
FETCH_ARG8(inst);
|
||||
|
||||
switch(inst) {
|
||||
case 0x00: /* RLC B */ //d
|
||||
case 0x01: /* RLC C */ //d
|
||||
case 0x02: /* RLC D */ //d
|
||||
case 0x03: /* RLC E */ //d
|
||||
case 0x04: /* RLC H */ //d
|
||||
case 0x05: /* RLC L */ //d
|
||||
case 0x07: /* RLC A */ //d
|
||||
_value = REG8(inst) = (uint8)((REG8(inst) << 1) | (REG8(inst) >> 7));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_value & 0x01) << 4);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x06: /* RLC (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)((_value << 1) | (_value >> 7));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_value & 0x01) << 4);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x08: /* RRC B */ //d
|
||||
case 0x09: /* RRC C */ //d
|
||||
case 0x0A: /* RRC D */ //d
|
||||
case 0x0B: /* RRC E */ //d
|
||||
case 0x0C: /* RRC H */ //d
|
||||
case 0x0D: /* RRC L */ //d
|
||||
case 0x0F: /* RRC A */ //d
|
||||
_value = (uint8)((REG8(inst) >> 1) | (REG8(inst) << 7));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((REG8(inst) & 0x01) << 4);
|
||||
REG8(inst) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x0E: /* RRC (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)((_tmp >> 1) | (_tmp << 7));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_tmp & 0x01) << 4);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x10: /* RL B */ //d
|
||||
case 0x11: /* RL C */ //d
|
||||
case 0x12: /* RL D */ //d
|
||||
case 0x13: /* RL E */ //d
|
||||
case 0x14: /* RL H */ //d
|
||||
case 0x15: /* RL L */ //d
|
||||
case 0x17: /* RL A */ //d
|
||||
_value = (uint8)((REG8(inst) << 1) | ((cpu->af.b.l & 0x10) >> 4));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((REG8(inst) & 0x80) >> 3);
|
||||
REG8(inst) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x16: /* RL (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)(_tmp << 1) | ((cpu->af.b.l & 0x10) >> 4);
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_tmp & 0x80) >> 3);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x18: /* RR B */ //d
|
||||
case 0x19: /* RR C */ //d
|
||||
case 0x1A: /* RR D */ //d
|
||||
case 0x1B: /* RR E */ //d
|
||||
case 0x1C: /* RR H */ //d
|
||||
case 0x1D: /* RR L */ //d
|
||||
case 0x1F: /* RR A */ //d
|
||||
_value = (uint8)((REG8(inst) >> 1) | ((cpu->af.b.l & 0x10) << 3));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((REG8(inst) & 0x01) << 4);
|
||||
REG8(inst) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x1E: /* RR (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)(_tmp >> 1) | ((cpu->af.b.l & 0x10) << 3);
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_tmp & 0x01) << 4);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x20: /* SLA B */ //d
|
||||
case 0x21: /* SLA C */ //d
|
||||
case 0x22: /* SLA D */ //d
|
||||
case 0x23: /* SLA E */ //d
|
||||
case 0x24: /* SLA H */ //d
|
||||
case 0x25: /* SLA L */ //d
|
||||
case 0x27: /* SLA A */ //d
|
||||
_value = (uint8)(REG8(inst) << 1);
|
||||
cpu->af.b.l = ((!_value) << 7) | ((REG8(inst) & 0x80) >> 3);
|
||||
REG8(inst) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x26: /* SLA (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)(_tmp << 1);
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_tmp & 0x80) >> 3);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x28: /* SRA B */ //d
|
||||
case 0x29: /* SRA C */ //d
|
||||
case 0x2A: /* SRA D */ //d
|
||||
case 0x2B: /* SRA E */ //d
|
||||
case 0x2C: /* SRA H */ //d
|
||||
case 0x2D: /* SRA L */ //d
|
||||
case 0x2F: /* SRA A */ //d
|
||||
_value = (uint8)((REG8(inst) >> 1) | (REG8(inst) & 0x80));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((REG8(inst) & 0x01) << 4);
|
||||
REG8(inst) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x2E: /* SRA (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)((_tmp >> 1) | (_tmp & 0x80));
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_tmp & 0x01) << 4);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
/* The SWAP instructions are unique to the LR35902. On the Z80, these are
|
||||
the undocumented SLL instructions. */
|
||||
case 0x30: /* SWAP B */ //d
|
||||
case 0x31: /* SWAP C */ //d
|
||||
case 0x32: /* SWAP D */ //d
|
||||
case 0x33: /* SWAP E */ //d
|
||||
case 0x34: /* SWAP H */ //d
|
||||
case 0x35: /* SWAP L */ //d
|
||||
case 0x37: /* SWAP A */ //d
|
||||
_value = REG8(inst);
|
||||
/* Doesn't matter if we swap yet, because if it is zero after swapping,
|
||||
it was zero before too (and that's the only bit that matters). */
|
||||
cpu->af.b.l = (!_value) << 7;
|
||||
REG8(inst) = (_value >> 4) | (_value << 4);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x36: /* SWAP (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
/* Doesn't matter if we swap yet, because if it is zero after swapping,
|
||||
it was zero before too (and that's the only bit that matters). */
|
||||
cpu->af.b.l = (!_value) << 7;
|
||||
cpu->mwrite(cpu->hl.w, (_value >> 4) | (_value << 4));
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x38: /* SRL B */ //d
|
||||
case 0x39: /* SRL C */ //d
|
||||
case 0x3A: /* SRL D */ //d
|
||||
case 0x3B: /* SRL E */ //d
|
||||
case 0x3C: /* SRL H */ //d
|
||||
case 0x3D: /* SRL L */ //d
|
||||
case 0x3F: /* SRL A */ //d
|
||||
_value = (uint8)(REG8(inst) >> 1);
|
||||
cpu->af.b.l = ((!_value) << 7) | ((REG8(inst) & 0x01) << 4);
|
||||
REG8(inst) = _value;
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x3E: /* SRL (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w);
|
||||
_value = (uint8)(_tmp >> 1);
|
||||
cpu->af.b.l = ((!_value) << 7) | ((_tmp & 0x01) << 4);
|
||||
cpu->mwrite(cpu->hl.w, _value);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x40: /* BIT 0, B */ //d
|
||||
case 0x41: /* BIT 0, C */ //d
|
||||
case 0x42: /* BIT 0, D */ //d
|
||||
case 0x43: /* BIT 0, E */ //d
|
||||
case 0x44: /* BIT 0, H */ //d
|
||||
case 0x45: /* BIT 0, L */ //d
|
||||
case 0x47: /* BIT 0, A */ //d
|
||||
case 0x48: /* BIT 1, B */ //d
|
||||
case 0x49: /* BIT 1, C */ //d
|
||||
case 0x4A: /* BIT 1, D */ //d
|
||||
case 0x4B: /* BIT 1, E */ //d
|
||||
case 0x4C: /* BIT 1, H */ //d
|
||||
case 0x4D: /* BIT 1, L */ //d
|
||||
case 0x4F: /* BIT 1, A */ //d
|
||||
case 0x50: /* BIT 2, B */ //d
|
||||
case 0x51: /* BIT 2, C */ //d
|
||||
case 0x52: /* BIT 2, D */ //d
|
||||
case 0x53: /* BIT 2, E */ //d
|
||||
case 0x54: /* BIT 2, H */ //d
|
||||
case 0x55: /* BIT 2, L */ //d
|
||||
case 0x57: /* BIT 2, A */ //d
|
||||
case 0x58: /* BIT 3, B */ //d
|
||||
case 0x59: /* BIT 3, C */ //d
|
||||
case 0x5A: /* BIT 3, D */ //d
|
||||
case 0x5B: /* BIT 3, E */ //d
|
||||
case 0x5C: /* BIT 3, H */ //d
|
||||
case 0x5D: /* BIT 3, L */ //d
|
||||
case 0x5F: /* BIT 3, A */ //d
|
||||
case 0x60: /* BIT 4, B */ //d
|
||||
case 0x61: /* BIT 4, C */ //d
|
||||
case 0x62: /* BIT 4, D */ //d
|
||||
case 0x63: /* BIT 4, E */ //d
|
||||
case 0x64: /* BIT 4, H */ //d
|
||||
case 0x65: /* BIT 4, L */ //d
|
||||
case 0x67: /* BIT 4, A */ //d
|
||||
case 0x68: /* BIT 5, B */ //d
|
||||
case 0x69: /* BIT 5, C */ //d
|
||||
case 0x6A: /* BIT 5, D */ //d
|
||||
case 0x6B: /* BIT 5, E */ //d
|
||||
case 0x6C: /* BIT 5, H */ //d
|
||||
case 0x6D: /* BIT 5, L */ //d
|
||||
case 0x6F: /* BIT 5, A */ //d
|
||||
case 0x70: /* BIT 6, B */ //d
|
||||
case 0x71: /* BIT 6, C */ //d
|
||||
case 0x72: /* BIT 6, D */ //d
|
||||
case 0x73: /* BIT 6, E */ //d
|
||||
case 0x74: /* BIT 6, H */ //d
|
||||
case 0x75: /* BIT 6, L */ //d
|
||||
case 0x77: /* BIT 6, A */ //d
|
||||
case 0x78: /* BIT 7, B */ //d
|
||||
case 0x79: /* BIT 7, C */ //d
|
||||
case 0x7A: /* BIT 7, D */ //d
|
||||
case 0x7B: /* BIT 7, E */ //d
|
||||
case 0x7C: /* BIT 7, H */ //d
|
||||
case 0x7D: /* BIT 7, L */ //d
|
||||
case 0x7F: /* BIT 7, A */ //d
|
||||
_tmp = REG8(inst) & (1 << ((inst >> 3) & 0x07));
|
||||
cpu->af.b.l = ((!_tmp) << 7) | 0x20 | (cpu->af.b.l & 0x10);
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x46: /* BIT 0, (HL) */ //d
|
||||
case 0x4E: /* BIT 1, (HL) */ //d
|
||||
case 0x56: /* BIT 2, (HL) */ //d
|
||||
case 0x5E: /* BIT 3, (HL) */ //d
|
||||
case 0x66: /* BIT 4, (HL) */ //d
|
||||
case 0x6E: /* BIT 5, (HL) */ //d
|
||||
case 0x76: /* BIT 6, (HL) */ //d
|
||||
case 0x7E: /* BIT 7, (HL) */ //d
|
||||
_tmp = cpu->mread(cpu->hl.w) & (1 << ((inst >> 3) & 0x07));
|
||||
cpu->af.b.l = ((!_tmp) << 7) | 0x20 | (cpu->af.b.l & 0x10);
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0x80: /* RES 0, B */ //d
|
||||
case 0x81: /* RES 0, C */ //d
|
||||
case 0x82: /* RES 0, D */ //d
|
||||
case 0x83: /* RES 0, E */ //d
|
||||
case 0x84: /* RES 0, H */ //d
|
||||
case 0x85: /* RES 0, L */ //d
|
||||
case 0x87: /* RES 0, A */ //d
|
||||
case 0x88: /* RES 1, B */ //d
|
||||
case 0x89: /* RES 1, C */ //d
|
||||
case 0x8A: /* RES 1, D */ //d
|
||||
case 0x8B: /* RES 1, E */ //d
|
||||
case 0x8C: /* RES 1, H */ //d
|
||||
case 0x8D: /* RES 1, L */ //d
|
||||
case 0x8F: /* RES 1, A */ //d
|
||||
case 0x90: /* RES 2, B */ //d
|
||||
case 0x91: /* RES 2, C */ //d
|
||||
case 0x92: /* RES 2, D */ //d
|
||||
case 0x93: /* RES 2, E */ //d
|
||||
case 0x94: /* RES 2, H */ //d
|
||||
case 0x95: /* RES 2, L */ //d
|
||||
case 0x97: /* RES 2, A */ //d
|
||||
case 0x98: /* RES 3, B */ //d
|
||||
case 0x99: /* RES 3, C */ //d
|
||||
case 0x9A: /* RES 3, D */ //d
|
||||
case 0x9B: /* RES 3, E */ //d
|
||||
case 0x9C: /* RES 3, H */ //d
|
||||
case 0x9D: /* RES 3, L */ //d
|
||||
case 0x9F: /* RES 3, A */ //d
|
||||
case 0xA0: /* RES 4, B */ //d
|
||||
case 0xA1: /* RES 4, C */ //d
|
||||
case 0xA2: /* RES 4, D */ //d
|
||||
case 0xA3: /* RES 4, E */ //d
|
||||
case 0xA4: /* RES 4, H */ //d
|
||||
case 0xA5: /* RES 4, L */ //d
|
||||
case 0xA7: /* RES 4, A */ //d
|
||||
case 0xA8: /* RES 5, B */ //d
|
||||
case 0xA9: /* RES 5, C */ //d
|
||||
case 0xAA: /* RES 5, D */ //d
|
||||
case 0xAB: /* RES 5, E */ //d
|
||||
case 0xAC: /* RES 5, H */ //d
|
||||
case 0xAD: /* RES 5, L */ //d
|
||||
case 0xAF: /* RES 5, A */ //d
|
||||
case 0xB0: /* RES 6, B */ //d
|
||||
case 0xB1: /* RES 6, C */ //d
|
||||
case 0xB2: /* RES 6, D */ //d
|
||||
case 0xB3: /* RES 6, E */ //d
|
||||
case 0xB4: /* RES 6, H */ //d
|
||||
case 0xB5: /* RES 6, L */ //d
|
||||
case 0xB7: /* RES 6, A */ //d
|
||||
case 0xB8: /* RES 7, B */ //d
|
||||
case 0xB9: /* RES 7, C */ //d
|
||||
case 0xBA: /* RES 7, D */ //d
|
||||
case 0xBB: /* RES 7, E */ //d
|
||||
case 0xBC: /* RES 7, H */ //d
|
||||
case 0xBD: /* RES 7, L */ //d
|
||||
case 0xBF: /* RES 7, A */ //d
|
||||
REG8(inst) &= ~(1 << ((inst >> 3) & 0x07));
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0x86: /* RES 0, (HL) */ //d
|
||||
case 0x8E: /* RES 1, (HL) */ //d
|
||||
case 0x96: /* RES 2, (HL) */ //d
|
||||
case 0x9E: /* RES 3, (HL) */ //d
|
||||
case 0xA6: /* RES 4, (HL) */ //d
|
||||
case 0xAE: /* RES 5, (HL) */ //d
|
||||
case 0xB6: /* RES 6, (HL) */ //d
|
||||
case 0xBE: /* RES 7, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cpu->mwrite(cpu->hl.w, _value & ~(1 << ((inst >> 3) & 0x07)));
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
|
||||
case 0xC0: /* SET 0, B */ //d
|
||||
case 0xC1: /* SET 0, C */ //d
|
||||
case 0xC2: /* SET 0, D */ //d
|
||||
case 0xC3: /* SET 0, E */ //d
|
||||
case 0xC4: /* SET 0, H */ //d
|
||||
case 0xC5: /* SET 0, L */ //d
|
||||
case 0xC7: /* SET 0, A */ //d
|
||||
case 0xC8: /* SET 1, B */ //d
|
||||
case 0xC9: /* SET 1, C */ //d
|
||||
case 0xCA: /* SET 1, D */ //d
|
||||
case 0xCB: /* SET 1, E */ //d
|
||||
case 0xCC: /* SET 1, H */ //d
|
||||
case 0xCD: /* SET 1, L */ //d
|
||||
case 0xCF: /* SET 1, A */ //d
|
||||
case 0xD0: /* SET 2, B */ //d
|
||||
case 0xD1: /* SET 2, C */ //d
|
||||
case 0xD2: /* SET 2, D */ //d
|
||||
case 0xD3: /* SET 2, E */ //d
|
||||
case 0xD4: /* SET 2, H */ //d
|
||||
case 0xD5: /* SET 2, L */ //d
|
||||
case 0xD7: /* SET 2, A */ //d
|
||||
case 0xD8: /* SET 3, B */ //d
|
||||
case 0xD9: /* SET 3, C */ //d
|
||||
case 0xDA: /* SET 3, D */ //d
|
||||
case 0xDB: /* SET 3, E */ //d
|
||||
case 0xDC: /* SET 3, H */ //d
|
||||
case 0xDD: /* SET 3, L */ //d
|
||||
case 0xDF: /* SET 3, A */ //d
|
||||
case 0xE0: /* SET 4, B */ //d
|
||||
case 0xE1: /* SET 4, C */ //d
|
||||
case 0xE2: /* SET 4, D */ //d
|
||||
case 0xE3: /* SET 4, E */ //d
|
||||
case 0xE4: /* SET 4, H */ //d
|
||||
case 0xE5: /* SET 4, L */ //d
|
||||
case 0xE7: /* SET 4, A */ //d
|
||||
case 0xE8: /* SET 5, B */ //d
|
||||
case 0xE9: /* SET 5, C */ //d
|
||||
case 0xEA: /* SET 5, D */ //d
|
||||
case 0xEB: /* SET 5, E */ //d
|
||||
case 0xEC: /* SET 5, H */ //d
|
||||
case 0xED: /* SET 5, L */ //d
|
||||
case 0xEF: /* SET 5, A */ //d
|
||||
case 0xF0: /* SET 6, B */ //d
|
||||
case 0xF1: /* SET 6, C */ //d
|
||||
case 0xF2: /* SET 6, D */ //d
|
||||
case 0xF3: /* SET 6, E */ //d
|
||||
case 0xF4: /* SET 6, H */ //d
|
||||
case 0xF5: /* SET 6, L */ //d
|
||||
case 0xF7: /* SET 6, A */ //d
|
||||
case 0xF8: /* SET 7, B */ //d
|
||||
case 0xF9: /* SET 7, C */ //d
|
||||
case 0xFA: /* SET 7, D */ //d
|
||||
case 0xFB: /* SET 7, E */ //d
|
||||
case 0xFC: /* SET 7, H */ //d
|
||||
case 0xFD: /* SET 7, L */ //d
|
||||
case 0xFF: /* SET 7, A */ //d
|
||||
REG8(inst) |= (1 << ((inst >> 3) & 0x07));
|
||||
cycles_done += 8;
|
||||
goto out;
|
||||
|
||||
case 0xC6: /* SET 0, (HL) */ //d
|
||||
case 0xCE: /* SET 1, (HL) */ //d
|
||||
case 0xD6: /* SET 2, (HL) */ //d
|
||||
case 0xDE: /* SET 3, (HL) */ //d
|
||||
case 0xE6: /* SET 4, (HL) */ //d
|
||||
case 0xEE: /* SET 5, (HL) */ //d
|
||||
case 0xF6: /* SET 6, (HL) */ //d
|
||||
case 0xFE: /* SET 7, (HL) */ //d
|
||||
_value = cpu->mread(cpu->hl.w);
|
||||
cpu->mwrite(cpu->hl.w, _value | (1 << ((inst >> 3) & 0x07)));
|
||||
cycles_done += 16;
|
||||
goto out;
|
||||
}
|
||||
-3557
File diff suppressed because it is too large
Load Diff
@@ -1,77 +0,0 @@
|
||||
#ifndef _Z80_H_
|
||||
#define _Z80_H_
|
||||
|
||||
typedef unsigned char UINT8;
|
||||
typedef unsigned short UINT16;
|
||||
typedef unsigned int UINT32;
|
||||
|
||||
typedef signed char INT8;
|
||||
typedef signed short INT16;
|
||||
typedef signed int INT32;
|
||||
|
||||
#define TRUE (1 == 1)
|
||||
#define FALSE (1 == 0)
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define INPUT_LINE_NMI 127
|
||||
#define CLEAR_LINE 0
|
||||
|
||||
#define INLINE inline
|
||||
|
||||
enum {
|
||||
Z80_PC=1, Z80_SP,
|
||||
Z80_A, Z80_B, Z80_C, Z80_D, Z80_E, Z80_H, Z80_L,
|
||||
Z80_AF, Z80_BC, Z80_DE, Z80_HL,
|
||||
Z80_IX, Z80_IY, Z80_AF2, Z80_BC2, Z80_DE2, Z80_HL2,
|
||||
Z80_R, Z80_I, Z80_IM, Z80_IFF1, Z80_IFF2, Z80_HALT,
|
||||
Z80_DC0, Z80_DC1, Z80_DC2, Z80_DC3
|
||||
};
|
||||
|
||||
enum {
|
||||
Z80_TABLE_op,
|
||||
Z80_TABLE_cb,
|
||||
Z80_TABLE_ed,
|
||||
Z80_TABLE_xy,
|
||||
Z80_TABLE_xycb,
|
||||
Z80_TABLE_ex /* cycles counts for taken jr/jp/call and interrupt latency (rst opcodes) */
|
||||
};
|
||||
|
||||
#define LSB_FIRST
|
||||
|
||||
typedef union {
|
||||
#ifdef LSB_FIRST
|
||||
struct { UINT8 l,h,h2,h3; } b;
|
||||
struct { UINT16 l,h; } w;
|
||||
#else
|
||||
struct { UINT8 h3,h2,h,l; } b;
|
||||
struct { UINT16 h,l; } w;
|
||||
#endif
|
||||
UINT32 d;
|
||||
} PAIR;
|
||||
|
||||
/****************************************************************************/
|
||||
/* The Z80 registers. HALT is set to 1 when the CPU is halted, the refresh */
|
||||
/* register is calculated as follows: refresh=(Z80.r&127)|(Z80.r2&128) */
|
||||
/****************************************************************************/
|
||||
typedef struct
|
||||
{
|
||||
PAIR prvpc,pc,sp,af,bc,de,hl,ix,iy;
|
||||
PAIR af2,bc2,de2,hl2;
|
||||
UINT8 r,r2,iff1,iff2,halt,im,i;
|
||||
UINT8 nmi_state; /* nmi line state */
|
||||
UINT8 nmi_pending; /* nmi pending */
|
||||
UINT8 irq_state; /* irq line state */
|
||||
UINT8 after_ei; /* are we in the EI shadow? */
|
||||
const struct z80_irq_daisy_chain *daisy;
|
||||
int (*irq_callback)(int irqline);
|
||||
} Z80_Regs;
|
||||
|
||||
void z80_init();
|
||||
void z80_exit(void);
|
||||
void z80_set_irq_line(int irqline, int state);
|
||||
int z80_execute(int cycles);
|
||||
void z80_reset(void);
|
||||
|
||||
#endif
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIDOjCCAi0GByqGSM44BAEwggIgAoIBAQD/nSuLzikeU6MrymSPr34AiU2RDJz6
|
||||
0pm/3PPs27f6QTtbUAvGC13q6XWrE3HJU8Gz7RM0EJZGpiwb86LgqOJhjfQ7WKse
|
||||
uaGdbHnvJEFsGqpKX7rnUWaWO3HAsKzDx4HzJqKupFEbTbzXl/GT3JMm+a3PXA3S
|
||||
kyATydRPrX583Ih8iHO1zQs1pwp2AnUvaQXvp4QOLFWheH5napkDZBjc9B7H38dg
|
||||
f4c0QqEXJw0lJbG26FxhpOWMiXwpWpOHSfb+XtmzpeTDIHxKPLMEdf2wc+xNC/M9
|
||||
GdVBfM7+hCEhl1JGqNWPpT7ZzciX8Cxg+5/MIfx+BQ7xQ4wo1SN7y1bZAhUAuBcT
|
||||
+ZsHbLXlR3uGHLIMg8boUKcCggEAGKgD56/tB6siFr5zEY+RTozPLmbBEcvhz5/p
|
||||
hAQo7z7ODimZAA1MN1J7Hv7o1S5D9dpZuOnGhiXaN3Z50WdePb9LVH7twrB8v3Xn
|
||||
ZOEHpumYNcYhvXrIx8LbhnMZmU27xSnrOQF9UfokRxGnXkNmlIwjT0NWqZCqQZwM
|
||||
wVj1Bgld3YV7CpGTF3aGM6a0pk9LBB71PoczXAx0Cum87ouXXy3c8F/82RF9tgXE
|
||||
ZBQ+7K2QXYPv9smbYDB+ZlsQ4VTOi3AReG5AeHTVWha3uaSFxY5vKpfau31iMRuT
|
||||
b1VQ2HVynBpemwhAw4FFvj5dm94aN0Rrvq9BKM09DL4zoOlLogOCAQUAAoIBAE8B
|
||||
I8z38GdbtswSoSV5JZ9z9cBMj/7x/sHi5ED9v1M8MD6AcxJSbo2cbK98xA3A9U22
|
||||
6XFHthGReVLa0dyN3Ov+GhBdf8b4CBuebAAS4z8du2EvUFtwDUHorLIo2Qtq2SYR
|
||||
wpznpB9+3g+z+8GZbzZkdm2mFBKa6GItj82qfEHwxB40aEBx7uQXzM3TH0rV8sMq
|
||||
u91aAVUQuoiG2/IYWc5O34kKMlHUTt6+uuJiSG0ax8ysWE5XltCfSh/VJcGrzKU6
|
||||
ZjGBzh8j5+KSXgoe9TuKlsnN5HDCZKaFTCvrxq889HJj0/ks4UrIF+meEyJvDt4P
|
||||
O4HHoKRtUdLEBMcY0JA=
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,2 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* fmem.c : fmemopen() on top of BSD's funopen()
|
||||
* 20081017 AF
|
||||
*/
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
/* Don't build if the deployment target is 10.13 or above. */
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "fmemopen.h"
|
||||
#undef fmemopen
|
||||
|
||||
#ifndef __has_builtin // Optional of course.
|
||||
#define __has_builtin(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
#ifndef linux
|
||||
struct fmem {
|
||||
size_t pos;
|
||||
size_t size;
|
||||
char *buffer;
|
||||
};
|
||||
typedef struct fmem fmem_t;
|
||||
|
||||
static int readfn(void *handler, char *buf, int size)
|
||||
{
|
||||
int count = 0;
|
||||
fmem_t *mem = handler;
|
||||
size_t available = mem->size - mem->pos;
|
||||
|
||||
if(size > available) size = available;
|
||||
for(count=0; count < size; mem->pos++, count++)
|
||||
buf[count] = mem->buffer[mem->pos];
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int writefn(void *handler, const char *buf, int size)
|
||||
{
|
||||
int count = 0;
|
||||
fmem_t *mem = handler;
|
||||
size_t available = mem->size - mem->pos;
|
||||
|
||||
if(size > available) size = available;
|
||||
for(count=0; count < size; mem->pos++, count++)
|
||||
mem->buffer[mem->pos] = buf[count];
|
||||
|
||||
return count; // ? count : size;
|
||||
}
|
||||
|
||||
static fpos_t seekfn(void *handler, fpos_t offset, int whence)
|
||||
{
|
||||
size_t pos;
|
||||
fmem_t *mem = handler;
|
||||
|
||||
switch(whence) {
|
||||
case SEEK_SET: pos = offset; break;
|
||||
case SEEK_CUR: pos = mem->pos + offset; break;
|
||||
case SEEK_END: pos = mem->size + offset; break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if(pos < 0 || pos > mem->size) return -1;
|
||||
|
||||
mem->pos = pos;
|
||||
return (fpos_t) pos;
|
||||
}
|
||||
|
||||
static int closefn(void *handler)
|
||||
{
|
||||
free(handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* simple, but portable version of fmemopen for OS X / BSD */
|
||||
FILE *fmemopen_(void *buf, size_t size, const char *mode)
|
||||
{
|
||||
#if __has_builtin(__builtin_available)
|
||||
if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
|
||||
return fmemopen(buf, size, mode);
|
||||
}
|
||||
#endif
|
||||
fmem_t *mem = (fmem_t *) malloc(sizeof(fmem_t));
|
||||
|
||||
memset(mem, 0, sizeof(fmem_t));
|
||||
mem->size = size, mem->buffer = buf;
|
||||
return funopen(mem, readfn, writefn, seekfn, closefn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright 2012 Jeff Verkoeyen
|
||||
// Originally ported from https://github.com/ingenuitas/python-tesseract/blob/master/fmemopen.c
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef FMEMOPEN_H_
|
||||
#define FMEMOPEN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A BSD port of the fmemopen Linux method using funopen.
|
||||
*
|
||||
* man docs for fmemopen:
|
||||
* http://linux.die.net/man/3/fmemopen
|
||||
*
|
||||
* man docs for funopen:
|
||||
* https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/funopen.3.html
|
||||
*
|
||||
* This method is ported from ingenuitas' python-tesseract project.
|
||||
*
|
||||
* You must call fclose on the returned file pointer or memory will be leaked.
|
||||
*
|
||||
* @param buf The data that will be used to back the FILE* methods. Must be at least
|
||||
* @c size bytes.
|
||||
* @param size The size of the @c buf data.
|
||||
* @param mode The permitted stream operation modes.
|
||||
* @returns A pointer that can be used in the fread/fwrite/fseek/fclose family of methods.
|
||||
* If a failure occurred NULL will be returned.
|
||||
*/
|
||||
FILE *fmemopen_(void *buf, size_t size, const char *mode);
|
||||
#define fmemopen fmemopen_
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #ifndef FMEMOPEN_H_
|
||||
@@ -0,0 +1,144 @@
|
||||
/* Use funopen(3) to provide open_memstream(3) like functionality. */
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
/* But only if the deployment target is below 10.13 */
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "open_memstream.h"
|
||||
#undef open_memstream
|
||||
|
||||
struct memstream {
|
||||
char **cp;
|
||||
size_t *lenp;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
static void
|
||||
memstream_grow(struct memstream *ms, size_t newsize)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (newsize > *ms->lenp) {
|
||||
buf = realloc(*ms->cp, newsize + 1);
|
||||
if (buf != NULL) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "MS: %p growing from %zd to %zd\n",
|
||||
ms, *ms->lenp, newsize);
|
||||
#endif
|
||||
memset(buf + *ms->lenp + 1, 0, newsize - *ms->lenp);
|
||||
*ms->cp = buf;
|
||||
*ms->lenp = newsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
memstream_read(void *cookie, char *buf, int len)
|
||||
{
|
||||
struct memstream *ms;
|
||||
int tocopy;
|
||||
|
||||
ms = cookie;
|
||||
memstream_grow(ms, ms->offset + len);
|
||||
tocopy = *ms->lenp - ms->offset;
|
||||
if (len < tocopy)
|
||||
tocopy = len;
|
||||
memcpy(buf, *ms->cp + ms->offset, tocopy);
|
||||
ms->offset += tocopy;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "MS: read(%p, %d) = %d\n", ms, len, tocopy);
|
||||
#endif
|
||||
return (tocopy);
|
||||
}
|
||||
|
||||
static int
|
||||
memstream_write(void *cookie, const char *buf, int len)
|
||||
{
|
||||
struct memstream *ms;
|
||||
int tocopy;
|
||||
|
||||
ms = cookie;
|
||||
memstream_grow(ms, ms->offset + len);
|
||||
tocopy = *ms->lenp - ms->offset;
|
||||
if (len < tocopy)
|
||||
tocopy = len;
|
||||
memcpy(*ms->cp + ms->offset, buf, tocopy);
|
||||
ms->offset += tocopy;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "MS: write(%p, %d) = %d\n", ms, len, tocopy);
|
||||
#endif
|
||||
return (tocopy);
|
||||
}
|
||||
|
||||
static fpos_t
|
||||
memstream_seek(void *cookie, fpos_t pos, int whence)
|
||||
{
|
||||
struct memstream *ms;
|
||||
#ifdef DEBUG
|
||||
size_t old;
|
||||
#endif
|
||||
|
||||
ms = cookie;
|
||||
#ifdef DEBUG
|
||||
old = ms->offset;
|
||||
#endif
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
ms->offset = pos;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
ms->offset += pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
ms->offset = *ms->lenp + pos;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "MS: seek(%p, %zd, %d) %zd -> %zd\n", ms, pos, whence,
|
||||
old, ms->offset);
|
||||
#endif
|
||||
return (ms->offset);
|
||||
}
|
||||
|
||||
static int
|
||||
memstream_close(void *cookie)
|
||||
{
|
||||
|
||||
free(cookie);
|
||||
return (0);
|
||||
}
|
||||
|
||||
FILE *
|
||||
open_memstream_(char **cp, size_t *lenp)
|
||||
{
|
||||
#if __has_builtin(__builtin_available)
|
||||
if (__builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
|
||||
return open_memstream(cp, lenp);
|
||||
}
|
||||
#endif
|
||||
struct memstream *ms;
|
||||
int save_errno;
|
||||
FILE *fp;
|
||||
|
||||
*cp = NULL;
|
||||
*lenp = 0;
|
||||
ms = malloc(sizeof(*ms));
|
||||
ms->cp = cp;
|
||||
ms->lenp = lenp;
|
||||
ms->offset = 0;
|
||||
fp = funopen(ms, memstream_read, memstream_write, memstream_seek,
|
||||
memstream_close);
|
||||
if (fp == NULL) {
|
||||
save_errno = errno;
|
||||
free(ms);
|
||||
errno = save_errno;
|
||||
}
|
||||
return (fp);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef OPEN_MEMSTREAM_H_
|
||||
#define OPEN_MEMSTREAM_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
FILE *open_memstream_(char **cp, size_t *lenp);
|
||||
#define open_memstream open_memstream_
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #ifndef FMEMOPEN_H_
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2012, 2013 Lawrence Sebald
|
||||
Copyright (C) 2012, 2013, 2015 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -86,6 +86,8 @@ static int guess_on_ext(const char *fn, int chop) {
|
||||
return CONSOLE_COLECOVISION;
|
||||
else if(!strcasecmp(ext, ".nes"))
|
||||
return CONSOLE_NES;
|
||||
else if(!strcasecmp(ext, ".ch8") || !strcasecmp(ext, ".c8"))
|
||||
return CONSOLE_CHIP8;
|
||||
#ifndef NO_ZLIB
|
||||
else if(!strcasecmp(ext, ".gz"))
|
||||
return TYPE_GZIP;
|
||||
|
||||
+2
-13
@@ -23,6 +23,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <mach/task.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/semaphore.h>
|
||||
|
||||
#include "CrabEmu.h"
|
||||
@@ -138,8 +139,7 @@ int sound_init(int channels, int region) {
|
||||
AudioStreamBasicDescription basic_desc;
|
||||
Component comp;
|
||||
AURenderCallbackStruct callback;
|
||||
UInt32 bufsz, size;
|
||||
AudioDeviceID dev;
|
||||
UInt32 bufsz;
|
||||
int rv = 0;
|
||||
|
||||
if(initted)
|
||||
@@ -214,17 +214,6 @@ int sound_init(int channels, int region) {
|
||||
goto err2;
|
||||
}
|
||||
|
||||
size = sizeof(dev);
|
||||
|
||||
error = AudioUnitGetProperty(outputAU,
|
||||
kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global, 0, &dev, &size);
|
||||
|
||||
if(error != noErr) {
|
||||
rv = -5;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
bufsz = 512;
|
||||
error = AudioUnitSetProperty(outputAU,
|
||||
kAudioUnitProperty_MaximumFramesPerSlice,
|
||||
|
||||
@@ -41,6 +41,8 @@ static int32 fds_process(void)
|
||||
/* write to registers */
|
||||
static void fds_write(uint32 address, uint8 value)
|
||||
{
|
||||
(void)address;
|
||||
(void)value;
|
||||
}
|
||||
|
||||
/* reset state of vrcvi sound channels */
|
||||
|
||||
@@ -1080,6 +1080,7 @@ apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean st
|
||||
{
|
||||
apu_t *temp_apu;
|
||||
/* int channel; */
|
||||
(void)stereo;
|
||||
|
||||
temp_apu = malloc(sizeof(apu_t));
|
||||
if (NULL == temp_apu)
|
||||
|
||||
+1757
-2240
File diff suppressed because it is too large
Load Diff
+171
-41
@@ -1,41 +1,171 @@
|
||||
#ifndef _H_YM2413_
|
||||
#define _H_YM2413_
|
||||
|
||||
/* select output bits size of output : 8 or 16 */
|
||||
#define SAMPLE_BITS 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 int UINT32; /* unsigned 32bit */
|
||||
typedef signed char INT8; /* signed 8bit */
|
||||
typedef signed short INT16; /* signed 16bit */
|
||||
typedef signed int INT32; /* signed 32bit */
|
||||
#endif
|
||||
|
||||
#if (SAMPLE_BITS==16)
|
||||
typedef INT16 SAMP;
|
||||
#endif
|
||||
#if (SAMPLE_BITS==8)
|
||||
typedef INT8 SAMP;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
int YM2413Init(int num, int clock, int rate);
|
||||
void YM2413Shutdown(void);
|
||||
void YM2413ResetChip(int which);
|
||||
void YM2413Write(int which, int a, int v);
|
||||
unsigned char YM2413Read(int which, int a);
|
||||
void YM2413UpdateOne(int which, INT16 *buffers, int length);
|
||||
void YM2413UpdateOneMono(int which, INT16 *buffers, int length);
|
||||
|
||||
typedef void (*OPLL_UPDATEHANDLER)(int param,int min_interval_us);
|
||||
|
||||
void YM2413SetUpdateHandler(int which, OPLL_UPDATEHANDLER UpdateHandler, int param);
|
||||
|
||||
|
||||
#endif /*_H_YM2413_*/
|
||||
/*
|
||||
CrabEmu Modifications to this code:
|
||||
- Added typedefs and such to unglue the code from MAME's core.
|
||||
- Added mono update function for the Dreamcast port.
|
||||
- Updated to mamedev current as of September 4, 2016, backporting to C
|
||||
from C++.
|
||||
|
||||
Copyright (C) 2011, 2012, 2016 Lawrence Sebald
|
||||
|
||||
Modifications distributed under the same license as the rest of the code in
|
||||
this file (GPLv2 or newer).
|
||||
*/
|
||||
|
||||
// license:GPL-2.0+
|
||||
// copyright-holders:Jarek Burczynski,Ernesto Corvi
|
||||
#ifndef __YM2413_H__
|
||||
#define __YM2413_H__
|
||||
|
||||
/* MAME glue... */
|
||||
#if __STDC_VERSION__ >= 199901L
|
||||
#include <stdint.h>
|
||||
typedef uint8_t UINT8;
|
||||
typedef uint32_t UINT32;
|
||||
|
||||
typedef int8_t INT8;
|
||||
typedef int16_t INT16;
|
||||
typedef int32_t INT32;
|
||||
#else
|
||||
#warning Check integer types, they may not be correct!
|
||||
|
||||
typedef unsigned char UINT8;
|
||||
typedef unsigned int UINT32;
|
||||
|
||||
typedef signed char INT8;
|
||||
typedef signed int INT32;
|
||||
#endif
|
||||
/* End MAME glue... */
|
||||
|
||||
struct OPLL_SLOT
|
||||
{
|
||||
UINT32 ar; /* attack rate: AR<<2 */
|
||||
UINT32 dr; /* decay rate: DR<<2 */
|
||||
UINT32 rr; /* release rate:RR<<2 */
|
||||
UINT8 KSR; /* key scale rate */
|
||||
UINT8 ksl; /* keyscale level */
|
||||
UINT8 ksr; /* key scale rate: kcode>>KSR */
|
||||
UINT8 mul; /* multiple: mul_tab[ML] */
|
||||
|
||||
/* Phase Generator */
|
||||
UINT32 phase; /* frequency counter */
|
||||
UINT32 freq; /* frequency counter step */
|
||||
UINT8 fb_shift; /* feedback shift value */
|
||||
INT32 op1_out[2]; /* slot1 output for feedback */
|
||||
|
||||
/* Envelope Generator */
|
||||
UINT8 eg_type; /* percussive/nonpercussive mode*/
|
||||
UINT8 state; /* phase type */
|
||||
UINT32 TL; /* total level: TL << 2 */
|
||||
INT32 TLL; /* adjusted now TL */
|
||||
INT32 volume; /* envelope counter */
|
||||
UINT32 sl; /* sustain level: sl_tab[SL] */
|
||||
|
||||
UINT8 eg_sh_dp; /* (dump state) */
|
||||
UINT8 eg_sel_dp; /* (dump state) */
|
||||
UINT8 eg_sh_ar; /* (attack state) */
|
||||
UINT8 eg_sel_ar; /* (attack state) */
|
||||
UINT8 eg_sh_dr; /* (decay state) */
|
||||
UINT8 eg_sel_dr; /* (decay state) */
|
||||
UINT8 eg_sh_rr; /* (release state for non-perc.)*/
|
||||
UINT8 eg_sel_rr; /* (release state for non-perc.)*/
|
||||
UINT8 eg_sh_rs; /* (release state for perc.mode)*/
|
||||
UINT8 eg_sel_rs; /* (release state for perc.mode)*/
|
||||
|
||||
UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
|
||||
|
||||
/* LFO */
|
||||
UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
|
||||
UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
|
||||
|
||||
/* waveform select */
|
||||
unsigned int wavetable;
|
||||
};
|
||||
|
||||
struct OPLL_CH
|
||||
{
|
||||
struct OPLL_SLOT SLOT[2];
|
||||
/* phase generator state */
|
||||
UINT32 block_fnum; /* block+fnum */
|
||||
UINT32 fc; /* Freq. freqement base */
|
||||
UINT32 ksl_base; /* KeyScaleLevel Base step */
|
||||
UINT8 kcode; /* key code (for key scaling) */
|
||||
UINT8 sus; /* sus on/off (release speed in percussive mode)*/
|
||||
};
|
||||
|
||||
enum {
|
||||
RATE_STEPS = (8),
|
||||
|
||||
/* sinwave entries */
|
||||
SIN_BITS = 10,
|
||||
SIN_LEN = (1<<SIN_BITS),
|
||||
SIN_MASK = (SIN_LEN-1),
|
||||
|
||||
TL_RES_LEN = (256), /* 8 bits addressing (real chip) */
|
||||
|
||||
/* TL_TAB_LEN is calculated as:
|
||||
* 11 - sinus amplitude bits (Y axis)
|
||||
* 2 - sinus sign bit (Y axis)
|
||||
* TL_RES_LEN - sinus resolution (X axis)
|
||||
*/
|
||||
TL_TAB_LEN = (11*2*TL_RES_LEN),
|
||||
|
||||
LFO_AM_TAB_ELEMENTS = 210
|
||||
|
||||
};
|
||||
|
||||
typedef struct ym2413_s {
|
||||
int tl_tab[TL_TAB_LEN];
|
||||
|
||||
/* sin waveform table in 'decibel' scale */
|
||||
/* two waveforms on OPLL type chips */
|
||||
unsigned int sin_tab[SIN_LEN * 2];
|
||||
|
||||
struct OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/
|
||||
UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/
|
||||
|
||||
UINT32 eg_cnt; /* global envelope generator counter */
|
||||
UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
|
||||
UINT32 eg_timer_add; /* step of eg_timer */
|
||||
UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
|
||||
|
||||
UINT8 rhythm; /* Rhythm mode */
|
||||
|
||||
/* LFO */
|
||||
UINT32 LFO_AM;
|
||||
INT32 LFO_PM;
|
||||
UINT32 lfo_am_cnt;
|
||||
UINT32 lfo_am_inc;
|
||||
UINT32 lfo_pm_cnt;
|
||||
UINT32 lfo_pm_inc;
|
||||
|
||||
UINT32 noise_rng; /* 23 bit noise shift register */
|
||||
UINT32 noise_p; /* current noise 'phase' */
|
||||
UINT32 noise_f; /* current noise period */
|
||||
|
||||
|
||||
/* instrument settings */
|
||||
/*
|
||||
0-user instrument
|
||||
1-15 - fixed instruments
|
||||
16 -bass drum settings
|
||||
17,18 - other percussion instruments
|
||||
*/
|
||||
UINT8 inst_tab[19][8];
|
||||
|
||||
UINT32 fn_tab[1024]; /* fnumber->increment counter */
|
||||
|
||||
UINT8 address; /* address register */
|
||||
|
||||
signed int output[2];
|
||||
} YM2413;
|
||||
|
||||
/* CrabEmu's public interface... */
|
||||
YM2413 *ym2413_init(int clock, int rate);
|
||||
void ym2413_shutdown(YM2413 *fm);
|
||||
void ym2413_reset(YM2413 *fm);
|
||||
void ym2413_write(YM2413 *fm, int a, int v);
|
||||
void ym2413_update(YM2413 *fm, int16_t *buf, int samples);
|
||||
void ym2413_update_mono(YM2413 *fm, int16_t buf, int samples);
|
||||
unsigned char ym2413_read(YM2413 *fm, int a);
|
||||
|
||||
#endif /*__YM2413_H__*/
|
||||
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
This file is part of CrabClaw.
|
||||
|
||||
Copyright (C) 2008, 2009 Lawrence Sebald
|
||||
|
||||
CrabClaw is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabClaw is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabClaw; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "list.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
cc_dlist_t *cc_dlist_create(void) {
|
||||
cc_dlist_t *rv = (cc_dlist_t *)malloc(sizeof(cc_dlist_t));
|
||||
|
||||
if(!rv) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rv->head = rv->tail = NULL;
|
||||
rv->count = 0;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void cc_dlist_destroy(cc_dlist_t *list) {
|
||||
/* Remove any nodes still in the list. The user is responsible for freeing
|
||||
any data associated with the nodes. */
|
||||
while(list->head) {
|
||||
cc_dlist_remove(list, list->head);
|
||||
}
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
int cc_dlist_remove(cc_dlist_t *list, cc_dlist_node_t *node) {
|
||||
/* Make sure the node and list are valid. */
|
||||
if(!list || !node) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(list->head == node) {
|
||||
/* Case 1: The node is the head node, advance the head to the next
|
||||
node in the list. */
|
||||
list->head = node->next;
|
||||
}
|
||||
|
||||
if(list->tail == node) {
|
||||
/* Case 2: The node is the tail node, point the tail pointer at the
|
||||
previous node in the list. If this is the only item in the list, this
|
||||
can happen at the same time as case 1 above. */
|
||||
list->tail = node->prev;
|
||||
}
|
||||
else {
|
||||
/* Case 3: The node is in the middle of the list. Remove the node from
|
||||
the chain and readjust some pointers. */
|
||||
node->prev->next = node->next;
|
||||
node->next->prev = node->prev;
|
||||
|
||||
free(node);
|
||||
}
|
||||
|
||||
free(node);
|
||||
--list->count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cc_dlist_remove_at(cc_dlist_t *list, uint32 pos) {
|
||||
cc_dlist_node_t *node = list->head;
|
||||
|
||||
/* Make sure the user has passed us a valid position to remove. */
|
||||
if(!list || list->count <= pos) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Grab the node that the user has requested removed. */
|
||||
while(pos-- && node != NULL) {
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
/* We're guaranteed to have a node at this point, assuming the user hasn't
|
||||
screwed around with the internals of the list. The check at the beginning
|
||||
of cc_dlist_remove will take care of the case if the user has screwed
|
||||
with the innards. */
|
||||
return cc_dlist_remove(list, node);
|
||||
}
|
||||
|
||||
int cc_dlist_insert_head(cc_dlist_t *list, void *data) {
|
||||
cc_dlist_node_t *node;
|
||||
|
||||
if(!list) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make sure we allocate memory properly. */
|
||||
if(!(node = (cc_dlist_node_t *)malloc(sizeof(cc_dlist_node_t)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Fill in the node structure. */
|
||||
node->data = data;
|
||||
node->prev = NULL;
|
||||
node->next = list->head;
|
||||
|
||||
/* Update the head of the list and increment the list count. */
|
||||
if(list->head) {
|
||||
list->head->prev = node;
|
||||
}
|
||||
|
||||
list->head = node;
|
||||
|
||||
if(!list->tail) {
|
||||
list->tail = node;
|
||||
}
|
||||
|
||||
++list->count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cc_dlist_insert_tail(cc_dlist_t *list, void *data) {
|
||||
cc_dlist_node_t *node;
|
||||
|
||||
if(!list) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make sure we allocate memory properly. */
|
||||
if(!(node = (cc_dlist_node_t *)malloc(sizeof(cc_dlist_node_t)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Fill in the node structure. */
|
||||
node->data = data;
|
||||
node->prev = list->tail;
|
||||
node->next = NULL;
|
||||
|
||||
/* Update the tail of the list and increment the list count. */
|
||||
if(list->tail) {
|
||||
list->tail->next = node;
|
||||
}
|
||||
|
||||
list->tail = node;
|
||||
|
||||
if(!list->head) {
|
||||
list->head = node;
|
||||
}
|
||||
|
||||
++list->count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cc_dlist_insert_after(cc_dlist_t *list, cc_dlist_node_t *node,
|
||||
void *data) {
|
||||
cc_dlist_node_t *n;
|
||||
|
||||
if(!list || !node) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make sure we allocate memory properly. */
|
||||
if(!(n = (cc_dlist_node_t *)malloc(sizeof(cc_dlist_node_t)))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Fill in the node structure. */
|
||||
n->data = data;
|
||||
n->prev = node;
|
||||
n->next = node->next;
|
||||
|
||||
/* Update the node passed in and increment the list count. */
|
||||
node->next->prev = n;
|
||||
node->next = n;
|
||||
++list->count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *cc_dlist_get_data_at(cc_dlist_t *list, int idx) {
|
||||
cc_dlist_node_t *tmp;
|
||||
|
||||
CC_DLIST_FOREACH(list, tmp) {
|
||||
if(!(idx--))
|
||||
return tmp->data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
This file is part of CrabClaw.
|
||||
|
||||
Copyright (C) 2008, 2009 Lawrence Sebald
|
||||
|
||||
CrabClaw is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabClaw is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CrabClaw; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* This linked list code was "ported" from CrabClaw. */
|
||||
|
||||
#ifndef CRABCLAW__LIST_H
|
||||
#define CRABCLAW__LIST_H
|
||||
|
||||
#include "CrabEmu.h"
|
||||
|
||||
/**
|
||||
* A node in a doubly-linked list.
|
||||
* This object is used in a doubly-linked list to hold one data item.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
typedef struct cc_dlist_node {
|
||||
/**
|
||||
* The previous node in the list.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
struct cc_dlist_node *prev;
|
||||
|
||||
/**
|
||||
* The next node in the list.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
struct cc_dlist_node *next;
|
||||
|
||||
/**
|
||||
* The data stored by this node.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
void *data;
|
||||
} cc_dlist_node_t;
|
||||
|
||||
/**
|
||||
* A doubly-linked list.
|
||||
* This object is used to hold a doubly-linked list of free-form data items.
|
||||
* All fields in this structure should be considered to be read-only from
|
||||
* outside of CrabClaw.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
typedef struct cc_dlist {
|
||||
/**
|
||||
* The head of this list.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
struct cc_dlist_node *head;
|
||||
|
||||
/**
|
||||
* The tail of this list.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
struct cc_dlist_node *tail;
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
uint32 count;
|
||||
} cc_dlist_t;
|
||||
|
||||
/**
|
||||
* Create a new doubly-linked list.
|
||||
* Create and initialize a empty doubly-linked list.
|
||||
* @since 1.0.0
|
||||
* @return A new <code>cc_dlist_t</code> object on success, NULL on
|
||||
* failure.
|
||||
*/
|
||||
extern cc_dlist_t *cc_dlist_create(void);
|
||||
|
||||
/**
|
||||
* Destroy a doubly-linked list.
|
||||
* Destroy an existing doubly-linked list. You are responsible for freeing any
|
||||
* memory used by the data in the list. This function will free only the nodes
|
||||
* used and the list object itself.
|
||||
* @since 1.0.0
|
||||
* @param list The list to destroy.
|
||||
*/
|
||||
extern void cc_dlist_destroy(cc_dlist_t *list);
|
||||
|
||||
/**
|
||||
* Insert a new item at the head of the list.
|
||||
* This function inserts a new node with the given data at the head of the
|
||||
* given list.
|
||||
* @since 1.0.0
|
||||
* @param list The list to insert into.
|
||||
* @param data The data to add to the list.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
extern int cc_dlist_insert_head(cc_dlist_t *list, void *data);
|
||||
|
||||
/**
|
||||
* Insert a new item at the tail of the list.
|
||||
* This function inserts a new node with the given data at the tail of the
|
||||
* given list.
|
||||
* @since 1.0.0
|
||||
* @param list The list to insert into.
|
||||
* @param data The data to add to the list.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
extern int cc_dlist_insert_tail(cc_dlist_t *list, void *data);
|
||||
|
||||
/**
|
||||
* Insert a new item after a given node.
|
||||
* This function inserts a new node with the given data after the node passed
|
||||
* into the function.
|
||||
* @since 1.0.0
|
||||
* @param list The list to insert into.
|
||||
* @param node The node to insert after.
|
||||
* @param data The data to add to the list.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
extern int cc_dlist_insert_after(cc_dlist_t *list, cc_dlist_node_t *node,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Remove a given node from a list.
|
||||
* This function removes the given node from the list. You are responsible for
|
||||
* cleaning up any resources used by the data, this function only frees the
|
||||
* memory used by the node itself.
|
||||
* @since 1.0.0
|
||||
* @param list The list to remove from.
|
||||
* @param node The node to remove.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
extern int cc_dlist_remove(cc_dlist_t *list, cc_dlist_node_t *node);
|
||||
|
||||
/**
|
||||
* Remove the element at a given position in a list.
|
||||
* This function removes the element at a given position in the list passed in.
|
||||
* You are responsible for cleaning up any resources used by the data in the
|
||||
* node, this function only frees the memory used by the node itself.
|
||||
* @since 1.0.0
|
||||
* @param list The list to remove from.
|
||||
* @param pos The element to remove (zero-based).
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
extern int cc_dlist_remove_at(cc_dlist_t *list, uint32 pos);
|
||||
|
||||
void *cc_dlist_get_data_at(cc_dlist_t *list, int idx);
|
||||
|
||||
/**
|
||||
* Loop through each element in a given list.
|
||||
* This macro defines a "foreach" style loop for the given list.
|
||||
* @since 1.0.0
|
||||
* @param list The list to traverse.
|
||||
* @param tmp The name of the temporary <code>cc_dlist_node_t</code>
|
||||
* pointer to use while traversing the list.
|
||||
*/
|
||||
#define CC_DLIST_FOREACH(list, tmp) \
|
||||
for(tmp = list->head; tmp != NULL; tmp = tmp->next)
|
||||
|
||||
#endif /* !CRABCLAW__LIST_H */
|
||||
+14
-14
@@ -34,40 +34,40 @@
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
voidpf ZCALLBACK fopen_file_func OF((
|
||||
voidpf ZCALLBACK fopen_file_func (
|
||||
voidpf opaque,
|
||||
const char* filename,
|
||||
int mode));
|
||||
int mode);
|
||||
|
||||
uLong ZCALLBACK fread_file_func OF((
|
||||
uLong ZCALLBACK fread_file_func (
|
||||
voidpf opaque,
|
||||
voidpf stream,
|
||||
void* buf,
|
||||
uLong size));
|
||||
uLong size);
|
||||
|
||||
uLong ZCALLBACK fwrite_file_func OF((
|
||||
uLong ZCALLBACK fwrite_file_func (
|
||||
voidpf opaque,
|
||||
voidpf stream,
|
||||
const void* buf,
|
||||
uLong size));
|
||||
uLong size);
|
||||
|
||||
long ZCALLBACK ftell_file_func OF((
|
||||
long ZCALLBACK ftell_file_func (
|
||||
voidpf opaque,
|
||||
voidpf stream));
|
||||
voidpf stream);
|
||||
|
||||
long ZCALLBACK fseek_file_func OF((
|
||||
long ZCALLBACK fseek_file_func (
|
||||
voidpf opaque,
|
||||
voidpf stream,
|
||||
uLong offset,
|
||||
int origin));
|
||||
int origin);
|
||||
|
||||
int ZCALLBACK fclose_file_func OF((
|
||||
int ZCALLBACK fclose_file_func (
|
||||
voidpf opaque,
|
||||
voidpf stream));
|
||||
voidpf stream);
|
||||
|
||||
int ZCALLBACK ferror_file_func OF((
|
||||
int ZCALLBACK ferror_file_func (
|
||||
voidpf opaque,
|
||||
voidpf stream));
|
||||
voidpf stream);
|
||||
|
||||
|
||||
voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
|
||||
|
||||
@@ -35,13 +35,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
|
||||
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
|
||||
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
|
||||
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
|
||||
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
|
||||
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
|
||||
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
|
||||
typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode);
|
||||
typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size);
|
||||
typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size);
|
||||
typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream);
|
||||
typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin);
|
||||
typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream);
|
||||
typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream);
|
||||
|
||||
typedef struct zlib_filefunc_def_s
|
||||
{
|
||||
@@ -57,7 +57,7 @@ typedef struct zlib_filefunc_def_s
|
||||
|
||||
|
||||
|
||||
void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
|
||||
void fill_fopen_filefunc (zlib_filefunc_def* pzlib_filefunc_def);
|
||||
|
||||
#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
|
||||
#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
|
||||
|
||||
+10
-10
@@ -175,10 +175,10 @@ typedef struct
|
||||
*/
|
||||
|
||||
|
||||
local int unzlocal_getByte OF((
|
||||
local int unzlocal_getByte (
|
||||
const zlib_filefunc_def* pzlib_filefunc_def,
|
||||
voidpf filestream,
|
||||
int *pi));
|
||||
int *pi);
|
||||
|
||||
local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
|
||||
const zlib_filefunc_def* pzlib_filefunc_def;
|
||||
@@ -205,10 +205,10 @@ local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
|
||||
/* ===========================================================================
|
||||
Reads a long in LSB order from the given gz_stream. Sets
|
||||
*/
|
||||
local int unzlocal_getShort OF((
|
||||
local int unzlocal_getShort (
|
||||
const zlib_filefunc_def* pzlib_filefunc_def,
|
||||
voidpf filestream,
|
||||
uLong *pX));
|
||||
uLong *pX);
|
||||
|
||||
local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
|
||||
const zlib_filefunc_def* pzlib_filefunc_def;
|
||||
@@ -233,10 +233,10 @@ local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
|
||||
return err;
|
||||
}
|
||||
|
||||
local int unzlocal_getLong OF((
|
||||
local int unzlocal_getLong (
|
||||
const zlib_filefunc_def* pzlib_filefunc_def,
|
||||
voidpf filestream,
|
||||
uLong *pX));
|
||||
uLong *pX);
|
||||
|
||||
local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
|
||||
const zlib_filefunc_def* pzlib_filefunc_def;
|
||||
@@ -336,9 +336,9 @@ extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivit
|
||||
Locate the Central directory of a zipfile (at the end, just before
|
||||
the global comment)
|
||||
*/
|
||||
local uLong unzlocal_SearchCentralDir OF((
|
||||
local uLong unzlocal_SearchCentralDir (
|
||||
const zlib_filefunc_def* pzlib_filefunc_def,
|
||||
voidpf filestream));
|
||||
voidpf filestream);
|
||||
|
||||
local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream)
|
||||
const zlib_filefunc_def* pzlib_filefunc_def;
|
||||
@@ -575,7 +575,7 @@ local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
|
||||
/*
|
||||
Get Info about the current file in the zipfile, with internal only info
|
||||
*/
|
||||
local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
|
||||
local int unzlocal_GetCurrentFileInfoInternal (unzFile file,
|
||||
unz_file_info *pfile_info,
|
||||
unz_file_info_internal
|
||||
*pfile_info_internal,
|
||||
@@ -584,7 +584,7 @@ local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize));
|
||||
uLong commentBufferSize);
|
||||
|
||||
local int unzlocal_GetCurrentFileInfoInternal (file,
|
||||
pfile_info,
|
||||
|
||||
+30
-30
@@ -123,9 +123,9 @@ typedef struct unz_file_info_s
|
||||
tm_unz tmu_date;
|
||||
} unz_file_info;
|
||||
|
||||
extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
|
||||
extern int ZEXPORT unzStringFileNameCompare (const char* fileName1,
|
||||
const char* fileName2,
|
||||
int iCaseSensitivity));
|
||||
int iCaseSensitivity);
|
||||
/*
|
||||
Compare two filename (fileName1,fileName2).
|
||||
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
|
||||
@@ -136,7 +136,7 @@ extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
|
||||
*/
|
||||
|
||||
|
||||
extern unzFile ZEXPORT unzOpen OF((const char *path));
|
||||
extern unzFile ZEXPORT unzOpen (const char *path);
|
||||
/*
|
||||
Open a Zip file. path contain the full pathname (by example,
|
||||
on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
|
||||
@@ -147,31 +147,31 @@ extern unzFile ZEXPORT unzOpen OF((const char *path));
|
||||
of this unzip package.
|
||||
*/
|
||||
|
||||
extern unzFile ZEXPORT unzOpen2 OF((const char *path,
|
||||
zlib_filefunc_def* pzlib_filefunc_def));
|
||||
extern unzFile ZEXPORT unzOpen2 (const char *path,
|
||||
zlib_filefunc_def* pzlib_filefunc_def);
|
||||
/*
|
||||
Open a Zip file, like unzOpen, but provide a set of file low level API
|
||||
for read/write the zip file (see ioapi.h)
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzClose OF((unzFile file));
|
||||
extern int ZEXPORT unzClose (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));
|
||||
extern int ZEXPORT unzGetGlobalInfo (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,
|
||||
extern int ZEXPORT unzGetGlobalComment (unzFile file,
|
||||
char *szComment,
|
||||
uLong uSizeBuf));
|
||||
uLong uSizeBuf);
|
||||
/*
|
||||
Get the global comment string of the ZipFile, in the szComment buffer.
|
||||
uSizeBuf is the size of the szComment buffer.
|
||||
@@ -182,22 +182,22 @@ extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
|
||||
/***************************************************************************/
|
||||
/* Unzip package allow you browse the directory of the zipfile */
|
||||
|
||||
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
|
||||
extern int ZEXPORT unzGoToFirstFile (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));
|
||||
extern int ZEXPORT unzGoToNextFile (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,
|
||||
extern int ZEXPORT unzLocateFile (unzFile file,
|
||||
const char *szFileName,
|
||||
int iCaseSensitivity));
|
||||
int iCaseSensitivity);
|
||||
/*
|
||||
Try locate the file szFileName in the zipfile.
|
||||
For the iCaseSensitivity signification, see unzStringFileNameCompare
|
||||
@@ -227,14 +227,14 @@ extern int ZEXPORT unzGoToFilePos(
|
||||
|
||||
/* ****************************************** */
|
||||
|
||||
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
|
||||
extern int ZEXPORT unzGetCurrentFileInfo (unzFile file,
|
||||
unz_file_info *pfile_info,
|
||||
char *szFileName,
|
||||
uLong fileNameBufferSize,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize));
|
||||
uLong commentBufferSize);
|
||||
/*
|
||||
Get Info about the current file
|
||||
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
|
||||
@@ -253,24 +253,24 @@ extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
|
||||
from it, and close it (you can close it before reading all the file)
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
|
||||
extern int ZEXPORT unzOpenCurrentFile (unzFile file);
|
||||
/*
|
||||
Open for reading data the current file in the zipfile.
|
||||
If there is no error, the return value is UNZ_OK.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
|
||||
const char* password));
|
||||
extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file,
|
||||
const char* password);
|
||||
/*
|
||||
Open for reading data the current file in the zipfile.
|
||||
password is a crypting password
|
||||
If there is no error, the return value is UNZ_OK.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
|
||||
extern int ZEXPORT unzOpenCurrentFile2 (unzFile file,
|
||||
int* method,
|
||||
int* level,
|
||||
int raw));
|
||||
int raw);
|
||||
/*
|
||||
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||
if raw==1
|
||||
@@ -280,11 +280,11 @@ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
|
||||
but you CANNOT set method parameter as NULL
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
|
||||
extern int ZEXPORT unzOpenCurrentFile3 (unzFile file,
|
||||
int* method,
|
||||
int* level,
|
||||
int raw,
|
||||
const char* password));
|
||||
const char* password);
|
||||
/*
|
||||
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||
if raw==1
|
||||
@@ -295,15 +295,15 @@ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
|
||||
*/
|
||||
|
||||
|
||||
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
|
||||
extern int ZEXPORT unzCloseCurrentFile (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,
|
||||
extern int ZEXPORT unzReadCurrentFile (unzFile file,
|
||||
voidp buf,
|
||||
unsigned len));
|
||||
unsigned len);
|
||||
/*
|
||||
Read bytes from the current file (opened by unzOpenCurrentFile)
|
||||
buf contain buffer where data must be copied
|
||||
@@ -315,19 +315,19 @@ extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
|
||||
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
|
||||
*/
|
||||
|
||||
extern z_off_t ZEXPORT unztell OF((unzFile file));
|
||||
extern z_off_t ZEXPORT unztell (unzFile file);
|
||||
/*
|
||||
Give the current position in uncompressed data
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzeof OF((unzFile file));
|
||||
extern int ZEXPORT unzeof (unzFile file);
|
||||
/*
|
||||
return 1 if the end of file was reached, 0 elsewhere
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
|
||||
extern int ZEXPORT unzGetLocalExtrafield (unzFile file,
|
||||
voidp buf,
|
||||
unsigned len));
|
||||
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
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Copyright (c) 2006-2009, WolfWings
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,558 +0,0 @@
|
||||
/* This source code is Copyright (C) 2006-2009 by WolfWings. */
|
||||
/* It is, however, released under the ISC license. */
|
||||
/* en.wikipedia.org/wiki/ISC_Licence */
|
||||
/* --------------------------------------------------------- */
|
||||
/* It is a formula-level rederiviation of the HQ2x technique */
|
||||
/* and is, thus, not a derivative work of the original code, */
|
||||
/* only the original equations behind the code. */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pastlib.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define bzero(x, y) memset(x, 0, y)
|
||||
#endif
|
||||
|
||||
static const uint8_t blends_2x[14*4] = {
|
||||
/* 5 2 4 1 */
|
||||
|
||||
8, 4, 4, 0,
|
||||
8, 4, 0, 4,
|
||||
8, 0, 4, 4,
|
||||
16, 0, 0, 0,
|
||||
12, 4, 0, 0,
|
||||
12, 0, 4, 0,
|
||||
12, 0, 0, 4,
|
||||
|
||||
4, 6, 6, 0, /* Interp9 */
|
||||
12, 2, 2, 0, /* Interp7 */
|
||||
14, 1, 1, 0, /* Interp10*/
|
||||
10, 4, 2, 0, /* Interp6 */
|
||||
10, 2, 4, 0, /* Interp6 */
|
||||
4, 6, 6, 0, /* Added for secondary blending used in HQ4x */
|
||||
8, 4, 4, 0, /* Added for secondary blending used in HQ4x */
|
||||
};
|
||||
|
||||
static const uint8_t blends_4x[14*16] = {
|
||||
/* 5 2 4 1 5 2 4 1 5 2 4 1 5 2 4 1 */
|
||||
|
||||
8, 4, 4, 0, 10, 4, 2, 0, 10, 2, 4, 0, 12, 2, 2, 0, /* Needs to be split. B */
|
||||
10, 0, 0, 6, 10, 4, 0, 2, 12, 0, 0, 4, 14, 0, 0, 2,
|
||||
10, 0, 0, 6, 10, 0, 4, 2, 12, 0, 0, 4, 14, 0, 0, 2,
|
||||
16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, /* Solid colors */
|
||||
10, 6, 0, 0, 10, 6, 0, 0, 14, 2, 0, 0, 14, 2, 0, 0,
|
||||
10, 0, 6, 0, 14, 0, 2, 0, 10, 0, 6, 0, 14, 0, 2, 0,
|
||||
10, 0, 0, 6, 12, 0, 0, 4, 12, 0, 0, 4, 14, 0, 0, 2,
|
||||
0, 8, 8, 0, 4, 8, 4, 0, 0, 6,10, 0, 12, 2, 2, 0, /* Needs to be split. A */
|
||||
8, 4, 4, 0, 12, 4, 0, 0, 12, 0, 4, 0, 16, 0, 0, 0,
|
||||
8, 4, 4, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0,
|
||||
12, 4, 0, 0, 4,12, 0, 0, 10, 0, 4, 0, 14, 0, 2, 0,
|
||||
12, 0, 4, 0, 10, 4, 0, 0, 4, 0,12, 0, 14, 2, 0, 0,
|
||||
0, 8, 8, 0, 0,10, 6, 0, 4, 4, 8, 0, 12, 2, 2, 0, /* Needs to be split. A */
|
||||
0, 8, 8, 0, 8, 8, 0, 0, 8, 0, 8, 0, 16, 0, 0, 0, /* Needs to be split. B */
|
||||
};
|
||||
|
||||
static const uint8_t tree_hq[0x800] = {
|
||||
/* 6 6 6 6 6 6 6 6
|
||||
* 2 2 2 2 2 2 2 2
|
||||
* 4 4 4 4 4 4 4 4
|
||||
* 8 8 8 8 8 8 8 8
|
||||
*/
|
||||
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8, /* */
|
||||
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8, /* 3 */
|
||||
0, 0, 5,10, 4, 4,13,13, 0, 0, 5, 5,11, 4,13,13, /* 1 */
|
||||
0, 0, 5,10, 4, 4,12,13, 0, 0, 5, 5,11, 4,13,13, /* 31 */
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8, /* 7 */
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, /* 3 7 */
|
||||
0, 0, 5,10, 4, 4, 7,13, 0, 0, 5, 5,11, 4,13,13, /* 17 */
|
||||
0, 0, 5,10, 4, 4, 9, 9, 0, 0, 5,10,11,11, 9, 9, /* 317 */
|
||||
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8, /* 9 */
|
||||
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6, /* 3 9 */
|
||||
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, /* 1 9 */
|
||||
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13, /* 31 9 */
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6, /* 79 */
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, /* 3 79 */
|
||||
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13, /* 179 */
|
||||
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, /* 3179 */
|
||||
/* DIFF26 */
|
||||
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8,
|
||||
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8,
|
||||
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5,11, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5,11, 4,13,13,
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8,
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 7,13, 0, 0, 5, 5,11, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5,11,11, 9, 9,
|
||||
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8,
|
||||
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6,
|
||||
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13,
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6,
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
|
||||
/* DIFF24 */
|
||||
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5,10,11,11, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
/* DIFF26 DIFF24 */
|
||||
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11,11, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
/* DIFF84 */
|
||||
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8,
|
||||
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8,
|
||||
0, 0, 5,10, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5,10, 4, 4,12,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8,
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5,10, 4, 4, 7,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5,10, 4, 4, 9, 9, 0, 0, 5,10, 4, 4, 9, 9,
|
||||
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8,
|
||||
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6,
|
||||
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13,
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6,
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
|
||||
/* DIFF26 DIFF84 */
|
||||
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8,
|
||||
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8,
|
||||
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8,
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 7,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
|
||||
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8,
|
||||
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6,
|
||||
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13,
|
||||
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6,
|
||||
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13,
|
||||
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
|
||||
/* DIFF24 DIFF84 */
|
||||
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5,10, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
/* DIFF26 DIFF24 DIFF84 */
|
||||
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
|
||||
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3};
|
||||
|
||||
#define RB(y,u,v) (((y)&0x3FF)<<22)|(((u)&0x3FF)<<11)|((v)&0x3FF)
|
||||
#define GO(y,v) ((((y)+64)&0x3FF)<<22)|(16<<11)|(((v)+16)&0x3FF)
|
||||
static const uint32_t DiffTable[128] = {
|
||||
RB(-85, 160, 80), RB(-83, 155, 77), RB(-80, 150, 75), RB(-77, 145, 72),
|
||||
RB(-75, 140, 70), RB(-72, 135, 67), RB(-69, 130, 65), RB(-67, 125, 62),
|
||||
RB(-64, 120, 60), RB(-61, 115, 57), RB(-59, 110, 55), RB(-56, 105, 52),
|
||||
RB(-53, 100, 50), RB(-51, 95, 47), RB(-48, 90, 45), RB(-45, 85, 42),
|
||||
RB(-43, 80, 40), RB(-40, 75, 37), RB(-37, 70, 35), RB(-35, 65, 32),
|
||||
RB(-32, 60, 30), RB(-29, 55, 27), RB(-27, 50, 25), RB(-24, 45, 22),
|
||||
RB(-21, 40, 20), RB(-19, 35, 17), RB(-16, 30, 15), RB(-13, 25, 12),
|
||||
RB(-11, 20, 10), RB( -8, 15, 7), RB( -5, 10, 5), RB( -3, 5, 2),
|
||||
RB( 0, 0, 0), RB( 2, -5, -2), RB( 5, -10, -5), RB( 7, -15, -7),
|
||||
RB( 10, -20, -10), RB( 13, -25, -12), RB( 15, -30, -15), RB( 18, -35, -17),
|
||||
RB( 21, -40, -20), RB( 23, -45, -22), RB( 26, -50, -25), RB( 29, -55, -27),
|
||||
RB( 31, -60, -30), RB( 34, -65, -32), RB( 37, -70, -35), RB( 39, -75, -37),
|
||||
RB( 42, -80, -40), RB( 45, -85, -42), RB( 47, -90, -45), RB( 50, -95, -47),
|
||||
RB( 53,-100, -50), RB( 55,-105, -52), RB( 58,-110, -55), RB( 61,-115, -57),
|
||||
RB( 63,-120, -60), RB( 66,-125, -62), RB( 69,-130, -65), RB( 71,-135, -67),
|
||||
RB( 74,-140, -70), RB( 77,-145, -72), RB( 79,-150, -75), RB( 82,-155, -77),
|
||||
GO(-84, -160), GO(-81, -155), GO(-78, -150), GO(-76, -145),
|
||||
GO(-73, -140), GO(-70, -135), GO(-68, -130), GO(-65, -125),
|
||||
GO(-63, -120), GO(-60, -115), GO(-57, -110), GO(-55, -105),
|
||||
GO(-52, -100), GO(-49, -95), GO(-47, -90), GO(-44, -85),
|
||||
GO(-42, -80), GO(-39, -75), GO(-36, -70), GO(-34, -65),
|
||||
GO(-31, -60), GO(-28, -55), GO(-26, -50), GO(-23, -45),
|
||||
GO(-21, -40), GO(-18, -35), GO(-15, -30), GO(-13, -25),
|
||||
GO(-10, -20), GO( -7, -15), GO( -5, -10), GO( -2, -5),
|
||||
GO( 0, 0), GO( 2, 4), GO( 5, 9), GO( 7, 14),
|
||||
GO( 10, 19), GO( 13, 24), GO( 15, 29), GO( 18, 34),
|
||||
GO( 21, 39), GO( 23, 44), GO( 26, 49), GO( 28, 54),
|
||||
GO( 31, 59), GO( 34, 64), GO( 36, 69), GO( 39, 74),
|
||||
GO( 42, 79), GO( 44, 84), GO( 47, 89), GO( 49, 94),
|
||||
GO( 52, 99), GO( 55, 104), GO( 57, 109), GO( 60, 114),
|
||||
GO( 63, 119), GO( 65, 124), GO( 68, 129), GO( 70, 134),
|
||||
GO( 73, 139), GO( 76, 144), GO( 78, 149), GO( 81, 154)};
|
||||
|
||||
__inline__ static int ExpandedDiff (uint32_t c0, uint32_t c1) {
|
||||
uint32_t r, g;
|
||||
g = c0;
|
||||
g += 0x4008020;
|
||||
g -= c1;
|
||||
r = DiffTable[(int)((unsigned char)g) + 0];
|
||||
r ^= (0x3FF << 11);
|
||||
g >>= 10;
|
||||
r += DiffTable[(int)((unsigned char)g) + 0];
|
||||
g >>= 11;
|
||||
r += DiffTable[(int)((unsigned char)g) + 64];
|
||||
r &= 0xE01F03E0;
|
||||
return r;
|
||||
}
|
||||
|
||||
#define DIFF56 0x001
|
||||
#define DIFF52 0x002
|
||||
#define DIFF54 0x004
|
||||
#define DIFF58 0x008
|
||||
#define DIFF53 0x010
|
||||
#define DIFF51 0x020
|
||||
#define DIFF57 0x040
|
||||
#define DIFF59 0x080
|
||||
#define DIFF26 0x100
|
||||
#define DIFF24 0x200
|
||||
#define DIFF84 0x400
|
||||
#define DIFF86 0x800
|
||||
|
||||
/* lastLineDiffs is previous line's 52, 53, 26, all >> 1 */
|
||||
|
||||
uint8_t lastLineDiffs[__PAST_LIBRARY_WIDTH];
|
||||
|
||||
#define RotatePattern(x) ((((x) & 0x777) << 1) | (((x) & 0x888) >> 3))
|
||||
|
||||
void ProcessHQ2x(const pixel *in, pixel *out, signed int height) {
|
||||
signed int y, x;
|
||||
unsigned int pattern, newpattern;
|
||||
uint32_t pixels[9];
|
||||
int prevline, nextline;
|
||||
|
||||
bzero(lastLineDiffs, sizeof(lastLineDiffs));
|
||||
prevline = 1;
|
||||
nextline = 1 + __PAST_LIBRARY_WIDTH;
|
||||
y = height - 1;
|
||||
|
||||
do {
|
||||
pixels[1-1] =
|
||||
pixels[2-1] = RGBUnpack(in[prevline-1]); /* Pixel 2 */
|
||||
pixels[3-1] = RGBUnpack(in[prevline ]); /* Pixel 3 */
|
||||
|
||||
pixels[4-1] =
|
||||
pixels[5-1] = RGBUnpack(in[ 0]); /* Pixel 5 */
|
||||
pixels[6-1] = RGBUnpack(in[ 1]); /* Pixel 6 */
|
||||
|
||||
pixels[7-1] =
|
||||
pixels[8-1] = RGBUnpack(in[nextline-1]); /* Pixel 8 */
|
||||
pixels[9-1] = RGBUnpack(in[nextline ]); /* Pixel 9 */
|
||||
|
||||
pattern = 0;
|
||||
|
||||
x = __PAST_LIBRARY_WIDTH - 1;
|
||||
do {
|
||||
newpattern = 0;
|
||||
if (pattern & DIFF26) newpattern |= DIFF51;
|
||||
if (pattern & DIFF56) newpattern |= DIFF54;
|
||||
if (pattern & DIFF86) newpattern |= DIFF57;
|
||||
if (pattern & DIFF53) newpattern |= DIFF24;
|
||||
if (pattern & DIFF59) newpattern |= DIFF84;
|
||||
|
||||
if (lastLineDiffs[x] & (DIFF52 >> 1)) newpattern |= DIFF58;
|
||||
if (lastLineDiffs[x] & (DIFF26 >> 1)) newpattern |= DIFF59;
|
||||
if (lastLineDiffs[x] & (DIFF53 >> 1)) newpattern |= DIFF86;
|
||||
|
||||
pattern = newpattern;
|
||||
|
||||
if (ExpandedDiff(pixels[5-1], pixels[2-1])) pattern |= DIFF52;
|
||||
if (ExpandedDiff(pixels[5-1], pixels[3-1])) pattern |= DIFF53;
|
||||
if (ExpandedDiff(pixels[5-1], pixels[6-1])) pattern |= DIFF56;
|
||||
if (ExpandedDiff(pixels[2-1], pixels[6-1])) pattern |= DIFF26;
|
||||
lastLineDiffs[x] = pattern >> 1;
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[ 0] =
|
||||
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
|
||||
(pixels[2-1] * blends_2x[(newpattern * 4) + 1]) +
|
||||
(pixels[4-1] * blends_2x[(newpattern * 4) + 2]) +
|
||||
(pixels[1-1] * blends_2x[(newpattern * 4) + 3])) / 16);
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[ 1] =
|
||||
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
|
||||
(pixels[6-1] * blends_2x[(newpattern * 4) + 1]) +
|
||||
(pixels[2-1] * blends_2x[(newpattern * 4) + 2]) +
|
||||
(pixels[3-1] * blends_2x[(newpattern * 4) + 3])) / 16);
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[(__PAST_LIBRARY_WIDTH*2)+1] =
|
||||
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
|
||||
(pixels[8-1] * blends_2x[(newpattern * 4) + 1]) +
|
||||
(pixels[6-1] * blends_2x[(newpattern * 4) + 2]) +
|
||||
(pixels[9-1] * blends_2x[(newpattern * 4) + 3])) / 16);
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[(__PAST_LIBRARY_WIDTH*2) ] =
|
||||
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
|
||||
(pixels[4-1] * blends_2x[(newpattern * 4) + 1]) +
|
||||
(pixels[8-1] * blends_2x[(newpattern * 4) + 2]) +
|
||||
(pixels[7-1] * blends_2x[(newpattern * 4) + 3])) / 16);
|
||||
|
||||
out += 2;
|
||||
in++;
|
||||
x--;
|
||||
|
||||
if (x == 0) {
|
||||
pixels[1-1] = pixels[2-1];
|
||||
pixels[2-1] = pixels[3-1];
|
||||
pixels[4-1] = pixels[5-1];
|
||||
pixels[5-1] = pixels[6-1];
|
||||
pixels[7-1] = pixels[8-1];
|
||||
pixels[8-1] = pixels[9-1];
|
||||
continue;
|
||||
}
|
||||
|
||||
pixels[1-1] = pixels[2-1];
|
||||
pixels[2-1] = pixels[3-1];
|
||||
pixels[3-1] = RGBUnpack(in[prevline]); /* Pixel 3 */
|
||||
pixels[4-1] = pixels[5-1];
|
||||
pixels[5-1] = pixels[6-1];
|
||||
pixels[6-1] = RGBUnpack(in[1 ]); /* Pixel 6 */
|
||||
pixels[7-1] = pixels[8-1];
|
||||
pixels[8-1] = pixels[9-1];
|
||||
pixels[9-1] = RGBUnpack(in[nextline]); /* Pixel 9 */
|
||||
} while (x >= 0);
|
||||
|
||||
prevline = 1 - __PAST_LIBRARY_WIDTH;
|
||||
out += (__PAST_LIBRARY_WIDTH * 2);
|
||||
|
||||
y--;
|
||||
if (y > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextline = 0;
|
||||
} while (y >= 0);
|
||||
}
|
||||
|
||||
void ProcessHQ4x(const pixel *in, pixel *out, signed int height) {
|
||||
signed int y, x;
|
||||
unsigned int pattern, newpattern;
|
||||
uint32_t pixels[9];
|
||||
int prevline, nextline;
|
||||
|
||||
bzero(lastLineDiffs, sizeof(lastLineDiffs));
|
||||
prevline = 1;
|
||||
nextline = 1 + __PAST_LIBRARY_WIDTH;
|
||||
y = height - 1;
|
||||
|
||||
do {
|
||||
pixels[1-1] =
|
||||
pixels[2-1] = RGBUnpack(in[prevline-1]); /* Pixel 2 */
|
||||
pixels[3-1] = RGBUnpack(in[prevline ]); /* Pixel 3 */
|
||||
|
||||
pixels[4-1] =
|
||||
pixels[5-1] = RGBUnpack(in[ 0]); /* Pixel 5 */
|
||||
pixels[6-1] = RGBUnpack(in[ 1]); /* Pixel 6 */
|
||||
|
||||
pixels[7-1] =
|
||||
pixels[8-1] = RGBUnpack(in[nextline-1]); /* Pixel 8 */
|
||||
pixels[9-1] = RGBUnpack(in[nextline ]); /* Pixel 9 */
|
||||
|
||||
pattern = 0;
|
||||
|
||||
x = __PAST_LIBRARY_WIDTH - 1;
|
||||
do {
|
||||
newpattern = 0;
|
||||
if (pattern & DIFF26) newpattern |= DIFF51;
|
||||
if (pattern & DIFF56) newpattern |= DIFF54;
|
||||
if (pattern & DIFF86) newpattern |= DIFF57;
|
||||
if (pattern & DIFF53) newpattern |= DIFF24;
|
||||
if (pattern & DIFF59) newpattern |= DIFF84;
|
||||
|
||||
if (lastLineDiffs[x] & (DIFF52 >> 1)) newpattern |= DIFF58;
|
||||
if (lastLineDiffs[x] & (DIFF26 >> 1)) newpattern |= DIFF59;
|
||||
if (lastLineDiffs[x] & (DIFF53 >> 1)) newpattern |= DIFF86;
|
||||
|
||||
pattern = newpattern;
|
||||
|
||||
if (ExpandedDiff(pixels[5-1], pixels[2-1])) pattern |= DIFF52;
|
||||
if (ExpandedDiff(pixels[5-1], pixels[3-1])) pattern |= DIFF53;
|
||||
if (ExpandedDiff(pixels[5-1], pixels[6-1])) pattern |= DIFF56;
|
||||
if (ExpandedDiff(pixels[2-1], pixels[6-1])) pattern |= DIFF26;
|
||||
lastLineDiffs[x] = pattern >> 1;
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[ 0] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
|
||||
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
|
||||
out[ 1] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
|
||||
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x4) ] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
|
||||
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x4)+1] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
|
||||
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[ 3] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
|
||||
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x4)+3] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
|
||||
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
|
||||
out[ 2] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
|
||||
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x4)+2] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
|
||||
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
|
||||
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[(__PAST_LIBRARY_WIDTH*0xC)+3] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
|
||||
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0xC)+2] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
|
||||
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x8)+3] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
|
||||
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x8)+2] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
|
||||
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
|
||||
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
|
||||
|
||||
newpattern = tree_hq[pattern & 0x7FF];
|
||||
pattern = RotatePattern(pattern);
|
||||
out[(__PAST_LIBRARY_WIDTH*0xC) ] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
|
||||
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x8) ] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
|
||||
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0xC)+1] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
|
||||
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
|
||||
out[(__PAST_LIBRARY_WIDTH*0x8)+1] =
|
||||
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
|
||||
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
|
||||
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
|
||||
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
|
||||
|
||||
out += 4;
|
||||
in++;
|
||||
x--;
|
||||
|
||||
if (x == 0) {
|
||||
pixels[1-1] = pixels[2-1];
|
||||
pixels[2-1] = pixels[3-1];
|
||||
pixels[4-1] = pixels[5-1];
|
||||
pixels[5-1] = pixels[6-1];
|
||||
pixels[7-1] = pixels[8-1];
|
||||
pixels[8-1] = pixels[9-1];
|
||||
continue;
|
||||
}
|
||||
|
||||
pixels[1-1] = pixels[2-1];
|
||||
pixels[2-1] = pixels[3-1];
|
||||
pixels[3-1] = RGBUnpack(in[prevline]); /* Pixel 3 */
|
||||
pixels[4-1] = pixels[5-1];
|
||||
pixels[5-1] = pixels[6-1];
|
||||
pixels[6-1] = RGBUnpack(in[1 ]); /* Pixel 6 */
|
||||
pixels[7-1] = pixels[8-1];
|
||||
pixels[8-1] = pixels[9-1];
|
||||
pixels[9-1] = RGBUnpack(in[nextline]); /* Pixel 9 */
|
||||
} while (x >= 0);
|
||||
|
||||
prevline = 1 - __PAST_LIBRARY_WIDTH;
|
||||
out += (__PAST_LIBRARY_WIDTH * 0xC);
|
||||
|
||||
y--;
|
||||
if (y > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nextline = 0;
|
||||
} while (y >= 0);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/* This source code is Copyright (C) 2006-2009 by WolfWings. */
|
||||
/* It is, however, released under the ISC license. */
|
||||
/* en.wikipedia.org/wiki/ISC_Licence */
|
||||
/* --------------------------------------------------------- */
|
||||
/* It is a formula-level rederiviation of the HQ2x technique */
|
||||
/* and is, thus, not a derivative work of the original code, */
|
||||
/* only the original equations behind the code. */
|
||||
|
||||
#ifndef __PAST_LIBRARY_H
|
||||
#define __PAST_LIBRARY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef __PAST_LIBRARY_WIDTH
|
||||
#define __PAST_LIBRARY_WIDTH 256
|
||||
#endif
|
||||
|
||||
#ifndef __PAST_LIBRARY_HEIGHT
|
||||
#define __PAST_LIBRARY_HEIGHT 224
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define __inline__
|
||||
#endif
|
||||
|
||||
#include "CrabEmu.h"
|
||||
|
||||
CLINKAGE
|
||||
|
||||
typedef uint16_t pixel;
|
||||
|
||||
/* These two functions are what need to be modified to interface other
|
||||
* pixel formats with the library. They need to return values inside a
|
||||
* format like this: (Red/Blue can be reversed.)
|
||||
*
|
||||
* 0000-00GG-GGG0-0000-0RRR-RR00-000B-BBBB
|
||||
*
|
||||
* This is VERY fast to create from 15-bit (NOT 16-bit) RGB/BGR, as
|
||||
* below.
|
||||
*
|
||||
* If you can't return data in this format, you'll have to rebuild the
|
||||
* ExpandedDiff function and DiffTable array to compensate, and possibly
|
||||
* change the blending functions in the Process* functions to handle any
|
||||
* guard-bits and masking.
|
||||
*/
|
||||
|
||||
static __inline__ uint32_t RGBUnpack(pixel i) {
|
||||
uint32_t o = i;
|
||||
o = (o * 0x10001);
|
||||
o = o & 0x03E07C1F;
|
||||
return o;
|
||||
}
|
||||
static __inline__ pixel RGBPack(uint32_t x) {
|
||||
x &= 0x03E07C1F;
|
||||
x |= (x >> 16);
|
||||
return x;
|
||||
}
|
||||
|
||||
void ProcessHQ2x(const pixel *inbuffer, pixel *outbuffer, signed int height);
|
||||
void ProcessHQ4x(const pixel *inbuffer, pixel *outbuffer, signed int height);
|
||||
|
||||
ENDCLINK
|
||||
|
||||
#endif /* __PAST_LIBRARY_H */
|
||||
@@ -1,28 +0,0 @@
|
||||
-=[ Pixel Art Scaling Toolkit - v0.2 by WolfWings ]=-
|
||||
|
||||
This is a low-level graphics-scaling library intented primarilly for use with
|
||||
low-resolution 'pixel art' such as that found in emulation. It will implement
|
||||
most scaling methods with a relatively fixed time, though it may require more
|
||||
work than some other choices to add to your software. On a Core 2 Duo it will
|
||||
render HQ2x (one of the most demanding algo's currently) in around 21.5 clock
|
||||
cycles per output pixel without any MMX/SSE code, while HQ4x costs half that,
|
||||
approximately 10.75 clock cycles per output pixel. So quadruple the scale, at
|
||||
only double the cost.
|
||||
|
||||
Please examine and understand the pastlib.h file, it is how you'd modify the
|
||||
library to support other resolutions and/or pixel formats. And please do not
|
||||
try to make the library accomidate dynamic horizontal resolutions, it's just
|
||||
not possible without suffering a very measurable 20-25% speed loss on x86-64
|
||||
or even worse on plain x86 with even fewer registers available.
|
||||
|
||||
Also, note that this code is developed under GCC 4.3, and takes advantage of
|
||||
the profile optimized compiling technique. For example, the 'unguided' build
|
||||
of the algo is almost a third slower than the profiled version. There's some
|
||||
work to be done yet to integrate MMX code and the like, but for now enjoy.
|
||||
|
||||
And yes, I will be creating tables for HQ3x, 2xSai, Scale2x, etc, etc. While
|
||||
most of these will likely be redundant, and perhaps inefficient, they'll all
|
||||
be entirely ISC Licensed implementations at that point, allowing use in many
|
||||
situations where their current licenses would prevent that.
|
||||
|
||||
- WolfWings, who is way too good at making full-justified text.
|
||||
+753
@@ -0,0 +1,753 @@
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
/*
|
||||
* This file defines four types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists and tail queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may be traversed in either direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ
|
||||
* _HEAD + + + +
|
||||
* _CLASS_HEAD + + + +
|
||||
* _HEAD_INITIALIZER + + + +
|
||||
* _ENTRY + + + +
|
||||
* _CLASS_ENTRY + + + +
|
||||
* _INIT + + + +
|
||||
* _EMPTY + + + +
|
||||
* _FIRST + + + +
|
||||
* _NEXT + + + +
|
||||
* _PREV - + - +
|
||||
* _LAST - - + +
|
||||
* _FOREACH + + + +
|
||||
* _FOREACH_FROM + + + +
|
||||
* _FOREACH_SAFE + + + +
|
||||
* _FOREACH_FROM_SAFE + + + +
|
||||
* _FOREACH_REVERSE - - - +
|
||||
* _FOREACH_REVERSE_FROM - - - +
|
||||
* _FOREACH_REVERSE_SAFE - - - +
|
||||
* _FOREACH_REVERSE_FROM_SAFE - - - +
|
||||
* _INSERT_HEAD + + + +
|
||||
* _INSERT_BEFORE - + - +
|
||||
* _INSERT_AFTER + + + +
|
||||
* _INSERT_TAIL - - + +
|
||||
* _CONCAT - - + +
|
||||
* _REMOVE_AFTER + - + -
|
||||
* _REMOVE_HEAD + - + -
|
||||
* _REMOVE + + + +
|
||||
* _SWAP + + + +
|
||||
*
|
||||
*/
|
||||
#ifdef QUEUE_MACRO_DEBUG
|
||||
/* Store the last 2 places the queue element or head was altered */
|
||||
struct qm_trace {
|
||||
unsigned long lastline;
|
||||
unsigned long prevline;
|
||||
const char *lastfile;
|
||||
const char *prevfile;
|
||||
};
|
||||
|
||||
#define TRACEBUF struct qm_trace trace;
|
||||
#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } ,
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
|
||||
|
||||
#define QMD_TRACE_HEAD(head) do { \
|
||||
(head)->trace.prevline = (head)->trace.lastline; \
|
||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
||||
(head)->trace.lastline = __LINE__; \
|
||||
(head)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TRACE_ELEM(elem) do { \
|
||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
||||
(elem)->trace.lastline = __LINE__; \
|
||||
(elem)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define QMD_TRACE_ELEM(elem)
|
||||
#define QMD_TRACE_HEAD(head)
|
||||
#define QMD_SAVELINK(name, link)
|
||||
#define TRACEBUF
|
||||
#define TRACEBUF_INITIALIZER
|
||||
#define TRASHIT(x)
|
||||
#endif /* QUEUE_MACRO_DEBUG */
|
||||
|
||||
#ifdef __cplusplus
|
||||
/*
|
||||
* In C++ there can be structure lists and class lists:
|
||||
*/
|
||||
#define QUEUE_TYPEOF(type) type
|
||||
#else
|
||||
#define QUEUE_TYPEOF(type) struct type
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_CLASS_HEAD(name, type) \
|
||||
struct name { \
|
||||
class type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
#define SLIST_CLASS_ENTRY(type) \
|
||||
struct { \
|
||||
class type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != NULL; \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_REMOVE_AFTER(curelm, field); \
|
||||
} \
|
||||
TRASHIT(*oldnext); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
SLIST_NEXT(elm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_SWAP(head1, head2, type) do { \
|
||||
QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \
|
||||
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
|
||||
SLIST_FIRST(head2) = swap_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_CLASS_HEAD(name, type) \
|
||||
struct name { \
|
||||
class type *stqh_first; /* first element */ \
|
||||
class type **stqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_CLASS_ENTRY(type) \
|
||||
struct { \
|
||||
class type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? NULL : \
|
||||
__containerof((head)->stqh_last, \
|
||||
QUEUE_TYPEOF(type), field.stqe_next))
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
TRASHIT(*oldnext); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
||||
QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \
|
||||
QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \
|
||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_FIRST(head2) = swap_first; \
|
||||
(head2)->stqh_last = swap_last; \
|
||||
if (STAILQ_EMPTY(head1)) \
|
||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
||||
if (STAILQ_EMPTY(head2)) \
|
||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_CLASS_HEAD(name, type) \
|
||||
struct name { \
|
||||
class type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
#define LIST_CLASS_ENTRY(type) \
|
||||
struct { \
|
||||
class type *le_next; /* next element */ \
|
||||
class type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
||||
#define QMD_LIST_CHECK_HEAD(head, field) do { \
|
||||
if (LIST_FIRST((head)) != NULL && \
|
||||
LIST_FIRST((head))->field.le_prev != \
|
||||
&LIST_FIRST((head))) \
|
||||
panic("Bad list head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL && \
|
||||
LIST_NEXT((elm), field)->field.le_prev != \
|
||||
&((elm)->field.le_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_LIST_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.le_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_LIST_CHECK_HEAD(head, field)
|
||||
#define QMD_LIST_CHECK_NEXT(elm, field)
|
||||
#define QMD_LIST_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) */
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_NEXT(listelm, field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_LIST_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_LIST_CHECK_HEAD((head), field); \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_PREV(elm, head, type, field) \
|
||||
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
|
||||
__containerof((elm)->field.le_prev, \
|
||||
QUEUE_TYPEOF(type), field.le_next))
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.le_next); \
|
||||
QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
|
||||
QMD_LIST_CHECK_NEXT(elm, field); \
|
||||
QMD_LIST_CHECK_PREV(elm, field); \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
TRASHIT(*oldnext); \
|
||||
TRASHIT(*oldprev); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
||||
QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \
|
||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
||||
LIST_FIRST((head2)) = swap_tmp; \
|
||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_CLASS_HEAD(name, type) \
|
||||
struct name { \
|
||||
class type *tqh_first; /* first element */ \
|
||||
class type **tqh_last; /* addr of last next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define TAILQ_CLASS_ENTRY(type) \
|
||||
struct { \
|
||||
class type *tqe_next; /* next element */ \
|
||||
class type **tqe_prev; /* address of previous next element */ \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#if (defined(_KERNEL) && defined(INVARIANTS))
|
||||
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
|
||||
if (!TAILQ_EMPTY(head) && \
|
||||
TAILQ_FIRST((head))->field.tqe_prev != \
|
||||
&TAILQ_FIRST((head))) \
|
||||
panic("Bad tailq head %p first->prev != head", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
|
||||
if (*(head)->tqh_last != NULL) \
|
||||
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
|
||||
if (TAILQ_NEXT((elm), field) != NULL && \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev != \
|
||||
&((elm)->field.tqe_next)) \
|
||||
panic("Bad link elm %p next->prev != elm", (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
|
||||
if (*(elm)->field.tqe_prev != (elm)) \
|
||||
panic("Bad link elm %p prev->next != elm", (elm)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define QMD_TAILQ_CHECK_HEAD(head, field)
|
||||
#define QMD_TAILQ_CHECK_TAIL(head, headname)
|
||||
#define QMD_TAILQ_CHECK_NEXT(elm, field)
|
||||
#define QMD_TAILQ_CHECK_PREV(elm, field)
|
||||
#endif /* (_KERNEL && INVARIANTS) */
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
QMD_TRACE_HEAD(head1); \
|
||||
QMD_TRACE_HEAD(head2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_NEXT(listelm, field); \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else { \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&(listelm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_PREV(listelm, field); \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
QMD_TRACE_ELEM(&(listelm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_HEAD(head, field); \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
QMD_TAILQ_CHECK_TAIL(head, field); \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
|
||||
QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
|
||||
QMD_TAILQ_CHECK_NEXT(elm, field); \
|
||||
QMD_TAILQ_CHECK_PREV(elm, field); \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else { \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
QMD_TRACE_HEAD(head); \
|
||||
} \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
TRASHIT(*oldnext); \
|
||||
TRASHIT(*oldprev); \
|
||||
QMD_TRACE_ELEM(&(elm)->field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
||||
QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \
|
||||
QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \
|
||||
(head1)->tqh_first = (head2)->tqh_first; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
(head2)->tqh_first = swap_first; \
|
||||
(head2)->tqh_last = swap_last; \
|
||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
||||
else \
|
||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
||||
else \
|
||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
This file is part of CrabEmu.
|
||||
|
||||
Copyright (C) 2009 Lawrence Sebald
|
||||
Copyright (C) 2009, 2014 Lawrence Sebald
|
||||
|
||||
CrabEmu is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
CrabEmu is distributed in the hope that it will be useful,
|
||||
@@ -29,7 +29,7 @@
|
||||
void scale2x_scale(const pixel_t *inbuf, pixel_t *outbuf, int width,
|
||||
int height) {
|
||||
int i;
|
||||
int a, b, c, d, p;
|
||||
pixel_t a, b, c, d, p;
|
||||
int neww = (width << 1) - 2;
|
||||
|
||||
/* Top row comes first. */
|
||||
@@ -146,3 +146,8 @@ void scale2x_scale(const pixel_t *inbuf, pixel_t *outbuf, int width,
|
||||
*(outbuf + neww + 1) = PIXEL3(a, 0, c, 0, p); /* Duplicate. */
|
||||
*(outbuf + neww) = PIXEL3(a, 0, c, 0, p);
|
||||
}
|
||||
|
||||
#undef PIXEL1
|
||||
#undef PIXEL2
|
||||
#undef PIXEL3
|
||||
#undef PIXEL4
|
||||
|
||||
Reference in New Issue
Block a user