Compare commits

...
340 Commits
Author SHA1 Message Date
Luke Usher 443aabb89b bump version number to v129 2022-07-13 17:53:31 +01:00
Luke Usher 93275dba2c a26: preliminary atari 2600 emulation
This commit introduces preliminary Atari 2600 emulation.

In the current state, many games are working but with the following caveats

1. Only a small subset of mappers are supported
2. The TIA isn't cycle accurate yet; many games have rendering and/or collision issues.

There is currently little-to-no reason to use this over other Atari 2600 emulators at this time,
but the goal is to eventually have cycle-accurate atari emulation.
2022-07-13 16:56:52 +01:00
Giovanni Bajo 3767568cdb n64: fix RSP emulation on ARM
ARM computers fall back to the standard SISD code, where there were some
bugs. Fixed through n64-systemtest (which is now a clean pass on
Apple M1)
2022-07-07 16:46:23 +01:00
Giovanni Bajo 7cfb150be8 n64: improve freeze bit emulation
It looks like the DMA register latches are still working while RDP freeze
is set, so just the DMA itself is frozen.

Fix some tests in n64-systemtest.
2022-06-28 23:55:45 +01:00
Giovanni Bajo 049211abae n64: implement basic behavior of freeze bit
The exact behavior of the freeze bit hasn't been investigate yet, but
at the very least it stops the RDP command engine. This PR does a
basic approximation of it, pending more in-depth tests.

Fixes Banjo-Tooie.
2022-06-22 09:13:03 +01:00
Giovanni Bajo a7ce0c22fa n64: add region check to TLBP
This was missed in the 64-bit TLB series (the region check only makes
sense for 64-bit TLBs).

Fix one test in n64-systemtest
2022-06-19 22:45:51 +01:00
Giovanni Bajo 7205a39f23 n64: fix 64-bit TLB vaddr match
Fix several tests in n64-systemtest.
2022-06-16 16:40:24 +01:00
Giovanni Bajo 99b759b630 n64: improve 64-bit TLB exceptions
It looks like address exceptions and TLB exceptions always set the
same registers (though some are marked as undefined in the manual), so
we unify the two implementations.

Fix several tests in n64-systemtest.
2022-06-16 16:40:24 +01:00
Giovanni Bajo a9ced1175b n64: rename address variables to vaddr when they refer to a VA
There's a big confusion in the CPU core between virtual and physical
addresses, and we have been fixing many bugs around this. Now that the
situation seems stable, do a big sweep and rename all "address" variables
to "vaddr" when they refer to a virtual address, to simplify reading
the code.

"address" is kept for physical addresses, which is consistent to the
rest of the code outside the CPU that only talks about physical
addresses anyway.

No functional changes.
2022-06-16 16:40:24 +01:00
Giovanni Bajo aeb5bc4597 n64: fix 64-bit addressing
This commit do further fixes 64-bit addressing. In 64-bit mode, the bus
addressable range is 2Gb (instead of 512Mb), so we need to mask
appropriately all physical addresses.

Fix several tests in n64-systemtest.
2022-06-16 16:40:24 +01:00
Giovanni Bajo f031407a14 n64: more accurate RDP status bit management
This begins improving the RDP status bits to more accurately reflect
what it can be seen on real hardware. It also undoes a hack for RDP
freeze that was allowing Banjo-Tooie to boot. Being a game-specific
hack, I think it is better to simply disable and wait until we find
out how to properly fix the game.

Fix a few new tests in n64-systemtest.
2022-06-14 13:26:13 +01:00
Giovanni Bajo 3c42e12b75 n64: implement invalid COP0 register access
Fix a few tests in n64-systemtest
2022-06-13 09:22:53 +01:00
TascoDLX 2ce9d210af md: handle controller port disconnect 2022-06-11 14:41:02 +01:00
TascoDLX a038b2cf61 desktop-ui: omit null allocation call when disconnecting port device 2022-06-11 14:41:02 +01:00
TascoDLX cee2ea332f mcd: force workaround for Corpse Killer (CD) gamepad bug 2022-06-11 14:41:02 +01:00
TascoDLX b1e3d90790 mia: extract serial number from MCD header to id disc 2022-06-11 14:41:02 +01:00
TascoDLX f306d1223c md: enable controller port disconnect via deallocation 2022-06-11 14:41:02 +01:00
TascoDLX e90e6ab846 desktop-ui: allow disconnection from ports 2022-06-11 14:41:02 +01:00
Luke Usher 6a103ed073 md: add destructor to fighting pad 2022-06-11 14:39:08 +01:00
Luke Usher 96663687f2 thread: prevent switching to threads that were destroyed while a call to Thread::synchronize() was in progress 2022-06-11 14:32:06 +01:00
remutro 8d17ae1697 validate we found correct number of bios files for NeoGeo 2022-06-07 07:42:37 +01:00
Giovanni Bajo a2e4807810 n64: fix RSP opcode STV
Fix one test in n64-systemtest
2022-06-04 00:23:15 +01:00
Giovanni Bajo 803daf7642 n64: fix RSP opcode SFV
This one is the weirdest. The current implementation is unrolled, but
I can't find a pattern that works. Surely there must be one as the
hardware doesn't surely have a lookup table. Anyway for now this
implementation is completely accurate, albeit verbose.

Fix one test in n64-systemtest
2022-06-04 00:23:15 +01:00
remutro c88fb1155b Fix identifier for WCW Mayhem 2022-06-04 00:05:58 +01:00
Giovanni Bajo 246a5eee93 n64: fix LWL/LWR accesses over RCP ranges
On real hardware, LWL/LWR execute a SysAD 32-bit masked read. Ares
was emulating them with one or multiple shorter accesses, which is
not the correct behavior wrt RCP hardware registers and PI bus area,
where the RCP incorrectly ignores the access size causing several bugs.

After this commit, LWL/LWR always do a 32-bit access on the bus, which
makes sure that we replicate the exact behavior.

SWL/SWR have a similar problem but harder to fix, because the SysAD
bus can do 24-bit writes, which is not something that Ares memory
functions support. So that's for another day.
2022-06-04 00:05:48 +01:00
remutro 4607b40e23 Fix incorrect identifier for South Park Rally (USA) 2022-06-03 21:43:36 +01:00
Giovanni Bajo d0a963ab42 n64: fix RDP scissoring in copy/fill mode
This fixes a bug in mame RDP with respect to scissoring: the upper X
bound is inclusive in copy and fill modes (and exclusives in the others).

This seems another bug in Mame's refactorings that doesn't appear
in upstream angrylion, given the fact that both cen64 and parallel-RDP
emulates this correctly.
2022-06-03 21:43:30 +01:00
Luke Usher bb58366efd desktop-ui: support choosing which system to emulate from command line
examples:

`ares --system "PlayStation" game.cue`
`ares --system "Super Famicom" game.sfc`
2022-06-02 15:54:52 +01:00
CasualPokePlayer 0891a102e4 Update instruction.hpp 2022-06-01 13:36:51 +01:00
CasualPokePlayer 0e74d5c8e3 avoid u128 usage
Also adding in protection against another bad shift case. Can't use the same code here though... could avoid the branching if it can be safely assumed _addressBits is always at least 3
2022-06-01 13:36:51 +01:00
CasualPokePlayer 672940cf06 debugger: fix address masking in instruction tracer (for 64 bit addresses)
This is similar to https://github.com/ares-emulator/ares/commit/822186413c9274295e49b922297f0cc744921eda, but fixes masking for N64 as it has 64 bit addresses.
2022-06-01 13:36:51 +01:00
Luke Usher 9ecfbdbe76 gba: prevent window from blocking translucent sprite blending
fixes Pokemon fog effect.
2022-05-31 21:05:40 +01:00
Luke Usher 679dbaccc9 sh2: implement watchdog in interval timer mode 2022-05-31 11:43:04 +01:00
Giovanni Bajo 57b4d68298 n64: fix PI DMA timing calculation
329a8c9 had a bug: it moved the queue.insert line *after* dmaWrite;
but dmaWrite updates the io.writeLength to its post-transfer status,
so the calculated timing was incorrect.

Fix the regression in Cruisin' Exotica and possibly others.
2022-05-31 09:01:26 +01:00
Luke Usher 5d55f2b8e3 ps1: clear param fifo after command execution 2022-05-30 21:59:27 +01:00
Luke Usher f094529d6d ps1: stub 'motor-on' 2022-05-30 21:59:27 +01:00
Giovanni Bajo c2735c5c1c n64: use a stronger hash in RSP recompiler to avoid collisions
Luckily enough, n64-systemtest triggered an interesting bug in our
RSP recompiler: a hash collision of the IMEM contents. The used hash
is objectively very poor so a collision is well possible.

Switch to CRC32 that should be enough to our scope.

The idea of doing a full IMEM hash is probably a bit weak to begin with,
but that's for another time.

Fix a new test in n64-systemtest (that only failed because of this).
2022-05-27 13:13:03 +01:00
Giovanni Bajo e1f5ab8333 n64: fix marginal cases of SHV and SWV
Fix two new tests in n64-systemtest
2022-05-27 13:13:03 +01:00
Luke Usher cf0ef61c1f ps1: implement SCEX test commands used for anti-mod detection 2022-05-27 08:12:46 +01:00
Luke Usher 2b9907c8d0 ps1: implement ReadToc command 2022-05-27 08:12:46 +01:00
Luke Usher 2abe2286b2 ps1: data reads should stop cd-da reads 2022-05-27 08:12:46 +01:00
Luke Usher b6d8be087f ps1: seek commands are two-stage, not one-stage 2022-05-27 08:12:46 +01:00
Giovanni Bajo 49892159d5 n64: fix serialization
I broke it in 8236464.
2022-05-26 22:17:05 +01:00
Luke Usher e88e2cf032 sfc: lazily sync co-processors
Significant performance improvement for some users, especially on low-power machines such as laptops/tablets.
2022-05-26 22:16:22 +01:00
Giovanni Bajo bcf9cab22d n64: rework RSP DMA double buffering
This change implements RSP DMA double buffering in a more faithful
way, so that reads from the registers always return the current values
(ongoing transfer or finished transfer) rather than the pending ones.

Moreover, this also makes Ares transfer bytes in background a row at
a time, rather than all of them at the end. It is a bit more accurate,
and it was also easier to implement in the new architecture.

This also fixes several tests in n64-systemtest.
2022-05-26 12:57:23 +01:00
Giovanni Bajo 2973fa71e5 n64: reactivate 64-bit memory map
Ares used to have full 64-bit TLB support but it was removed at some
point because of some wrong testing that apparently made us believe
that the CPU didn't support that mode. It will have to be restored from
the git history in the future, though it's not very urgent because
nobody is using 64-bit pointers on N64 (there is no toolchain with
64-bit pointers, nor it seems useful).

Luckily, at least the segment parsing was left in and just commented out.
This allows to pass the last two remaining tests in n64-systemtest.
2022-05-25 09:04:55 +01:00
Giovanni Bajo 16f86afd9f n64: improve RSP DMA timing
RSP DMA runs at 8 bytes per RSP cycle, and each RSP cycle is 3 "ares cycles".
2022-05-24 07:49:28 +01:00
Giovanni Bajo 329a8c958b n64: improve PI DMA timings
PI DMA (at default PI configuration) is roughly 9 COP0 cycles per byte,
which is 18 CPU cycles, which is 36 "ares cycles".
2022-05-24 07:49:28 +01:00
Giovanni Bajo 91f3dfaf3d n64: run PI DMA immediately, and delay only the interrupt
This is less likely to create troubles to games, and matches what it
is being done for RSP DMA.

This makes DK64 boot.
2022-05-24 07:49:28 +01:00
Giovanni Bajo daa8d82f7b n64: implement more null opcodes in RSP
Fix several tests in n64-systemtest
2022-05-24 07:48:57 +01:00
Giovanni Bajo fb98870d56 n64: fix VMACQ and fix+simplify VMOV
Fix two tests in n64-systemtest.
2022-05-24 07:48:57 +01:00
Giovanni Bajo 07183d56f4 n64: fix clamping of RSP vrndn/vrndp opcodes
Fix some tests in n64-systemtest
2022-05-23 16:39:46 +01:00
LuigiBlood 232cb6fcf6 n64: remove readId from controller 2022-05-23 16:39:28 +01:00
LuigiBlood 320779a845 n64: implement per controller joybus pif communication 2022-05-23 16:39:28 +01:00
LuigiBlood 86e962bc13 n64: add n64 mouse emulation 2022-05-23 16:39:28 +01:00
Luke Usher 0cafaad19b sfc: alternate fix for voxel demo; restores SuperFX compatibility 2022-05-21 13:32:36 +01:00
Giovanni Bajo 043b4ffd37 n64: implement AI DMA hardware bug
Fix audio in Twisted Edge
2022-05-19 07:25:09 +01:00
Luke Usher 6174fa384e bump version number to v128 2022-05-17 19:45:05 +01:00
invertego e5d1f2dfea recompiler: update to latest SLJIT API 2022-05-13 18:48:45 +01:00
Luke Usher 54f6ea31f5 Merge commit '0d26512309d871d62972bfcd8e48f48528881332' 2022-05-13 18:47:17 +01:00
Luke Usher 1d26512309 Squashed 'thirdparty/sljit/' changes from d6a0fa61e..761d48d2b
761d48d2b Improve constant generation
1f32a9f23 Implement fpu operations for RISC-V
ad99f17a9 Improve x86 float compare
fb55788d0 Initial implementation of RISC-V port
7f08de6ff Improve jump code generator on MIPS
469c61db9 Improve mips code generator
a4e8e34ef Support unsaved registers on s390x
f404f3608 Support unsaved registers
88d4778b9 Add license file
160b6f99d windows: fix building in arm64 with clang (#126)
022482eeb wxexec: officially drop support for EOL NetBSD < 8 versions (#124)
beda74f07 Support reworked floating point compare on s390x
050215b22 Remove unnecessary checks
6b950049d Improve floating point compare.
6ace72c90 windows: allow building with MinGW64 gcc/clang (#125)
186b07064 mips: avoid sign conversion warnings with clang (#122)
e0b17f5a6 macos: fix recent regression breaking build in macos/arm64e (#121)
68d9b4bac misc: mostly to avoid warnings (#120)
76330309a Update s390x.
6807c7a8f Remove SLJIT_NEG.
ca530a4a7 Readd carry flag support.
5f60134b5 s390x: be explicit about cache coherence needs (#119)
75a555604 power: avoid bogus clear_cache builtin in gcc >= 10 (#118)
1448ef97c sparc: avoid NOP implementation for clear_cache with gcc (#117)
ad69ad87b sparc: build fixes (#116)
47646b943 Support incoming arguments in scratch registers for s390x.
4fbdd6953 Support incoming arguments in scratch registers.
86ebfbd50 Rework x86-32 function prologue/epilogue.
2f8cf7a08 Increase the number of floating point registers on ppc, mips, and sparc.
2737606ee Increase the number floating point registers on s390x.
03469c8e1 Rename tail call to call return and minor ARM improvements.
7cb8a5c24 Increase the number floating point registers on x86 and ARM.
b35ab5e7e Implement fpu support on s390x.
dcc8fc149 misc: fix -Wextra related errors (#114)
92fcb0fe5 Fix fallthrough errors.
d345a64b3 misc: build/documentation fixes from recent changes (#113)
ff6ec4f74 Implement tail call on MIPS.
eed28632f Implement tail call on ARM.
d7793570f Reduce signed comparison warnings.
30b2dcd83 Reduce value conversion warnings on s390x.
c783c1119 Reduce value conversion warnings on MIPS, PPC, SPARC.
335ee51f5 Reduce value conversion warnings on ARM.
8d0f668fa Improve value conversion warnings.
373163144 Implementing tail calls for x86.
9c8b6961b Improve argument sanity checks.
24c547b7e Swap f32 and f64 argument types.
7f0afb25e Rework function argument list descriptor macros.
585ac42bf Implement four argument support for emit enter.
b5e0b1234 Improve mov32 operation.
433294658 Fix incorrect function call (#112)
cf594d74d Change SLJIT_I32_OP and SLJIT_F32_OP to SLJIT_32.
ae5fff841 Improve arm code generator.
43538b877 Replace SLJIT_UNUSED with appropriate function calls.

git-subtree-dir: thirdparty/sljit
git-subtree-split: 761d48d2b89a048badbd9c5cf45da75336891cfe
2022-05-13 18:47:16 +01:00
Luke Usher 43f1e524e0 Merge commit '402c8acc5f30b9ec355deb52618d99eb69f58916' 2022-05-13 18:47:05 +01:00
Luke Usher 402c8acc5f Squashed 'ares/n64/vulkan/parallel-rdp/' changes from 05136e3b4..af9b4b00f
af9b4b00f Update to b355d60aa32c8647670634d30ea1b107c3f78b05
0dcebe11e Update to e5efa5836cd43a597c706e07f04e79083d5ffb4f
e79d0cb77 Update to b3eeb49e708a215ee990415f303ce9936e55bd66

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: af9b4b00f0e6ec17cfd59f9e71c4976b5c3c40b0
2022-05-13 18:47:05 +01:00
Giovanni Bajo 2451fec704 n64: internal mask of physicalAddress register in TLB entries
Fix one test in n64-systemtest
2022-05-08 14:31:34 +01:00
Giovanni Bajo 8a70118446 n64: fix 64-bit pc calculation in J opcode
This was missed in the previous sweep on 64-bit PC. Fix one test
in n64-systemtest.
2022-05-08 14:31:34 +01:00
Giovanni Bajo fa017c26a5 n64: implement exceptions in non-taken delay slots
When an exception happen in a delay slot, it behaves the same whether
the branch was taken or not. In Ares, instead, the state "delay slot"
only applied to taken branches.

Fix several tests in n64-systemtest
2022-05-08 14:31:34 +01:00
Giovanni Bajo 670920a5a4 n64: increase a little the PI DMA timing.
These timings actually depend on the PI registers, even though they
are almost always set to fixed default values. We will get there
eventually and provide a more accurate figure, but for now this
allows to pass timing tests in n64-systemtest so that it can't be
more wrong than before.
2022-05-08 14:31:34 +01:00
Giovanni Bajo 82364646d3 n64: fix controller input after PIF refactoring
A last minute change in 741320e0be introduced a regression in controller
input.

Similar to what we did with PI DMA, SI DMA does not go through the main
bus (SysAD bus) but directly communicate with pif and rdram. This is not
just for the sake of correctness, but SysAD bus half-word writes are
now emulated with the same brokeness they have on real hardware, and
obviously DMA works instead. Moreover, since SI only reads/writes
32-bit words, also switch to word access.
2022-05-07 21:59:27 +01:00
Giovanni Bajo 40220a6ba1 n64: map domain 1 address 3 zone into main memory map
This was missed in the PI bus refactoring. All addresses up to
7fff'ffff are mapped to the PI bus.
2022-05-07 11:33:48 +01:00
Giovanni Bajo 96a6f98862 n64: refactor PIF chip into its own class
Currently, Ares does not model the PIF chip on its own: it looks like
there was an original mistake that conflated the PI (peripheral
interface, one of the components of RCP) with the PIF-NUS (a separate
chip, connected to SI component of RCP via a serial bus).

This refactor introduces a PIF class and moves all the related code
into it. Since the class implements the Memory::IO protocol for memory
accesses, it also fixes memory accesses with non standard sizes, as
tested by n64-systemtest.
2022-05-07 11:33:48 +01:00
Giovanni Bajo 3423bcf65e n64: improve TLB register masking
Fix a few tests from n64-systemtest
2022-05-07 11:33:48 +01:00
Giovanni Bajo a74ef69d86 n64: add address error for invalid 64bit address in 32bit mode
Fix a few tests from n64-systemtest
2022-05-07 11:33:48 +01:00
Giovanni Bajo 177475f053 n64: fix a few memory access opcodes that were using a 32-bit address 2022-05-07 11:33:48 +01:00
Giovanni Bajo 32ba2fe676 n64: activate address errors by default
These improve accuracy and help with homebrew development, while not
affecting performance much.
2022-05-07 11:33:48 +01:00
Giovanni Bajo 3aa1b3b738 n64: fix LL / SC / LLD / SCD opcodes
Updates #140.

Unfortunately this doesn't fix Gauntlet Legends.
2022-05-06 10:24:40 +01:00
Giovanni Bajo abf4c58ce6 n64: fix cache to use physical addresses
There is a bit confusion of virtual vs physical addresses in Ares,
so dcache/icache were sometimes using a virtual address, sometimes
a physical one. This is wrong: cache is based on physical addresses
only.

Moreover, the CACHE opcode was not even going through TLBs, while it
should. So a CACHE opcode on a TLB-mapped address wasn't working before.
2022-05-06 10:23:31 +01:00
Luke Usher 6b0a52fcb6 Merge commit 'c7e59d13fa1eaa6d6e19daec8dc22a3bdf7e0e70' 2022-05-03 18:02:47 +01:00
Luke Usher c7e59d13fa Squashed 'thirdparty/libchdr/' changes from a03e69319..6eeb6abc4
6eeb6abc4 Merge pull request #76 from invertego/zlib_codec_init_free
254cdaccc Remove erroneous free() call in zlib_codec_init()

git-subtree-dir: thirdparty/libchdr
git-subtree-split: 6eeb6abc4adc094d489c8ba8cafdcff9ff61251b
2022-05-03 18:02:47 +01:00
invertego ea2b6886b5 nall: handle CHD paths with non-ASCII chars on Windows 2022-05-03 09:11:15 +01:00
Giovanni Bajo 80e651741c n64: make sure TLB entries are correctly initialized at power
This bug has always been there, but was hidden by the incorrect TLB
matching logic. After fixing the logic in 0bf24b3, this bug was
uncovered.

Fixes Goldeneye.
2022-05-03 09:10:20 +01:00
Giovanni Bajo 6b70ae6d69 n64: fix RSP LTV opcode in its corner cases
Fix one test in n64-systemtest ROM.
2022-05-03 09:09:08 +01:00
Giovanni Bajo a3a777ef93 nall: optimize priority_queue remove function 2022-04-30 22:06:12 +01:00
Giovanni Bajo abaf7a82f3 n64: implement proper PI bus writes with IO busy bit
All writes through the PI bus are actually performed asynchronous by
the hardware, and the status is reflected in the PI IO busy bit.

This commit emulates this properly, and also emulates what happens while
the busy bit is set: further writes are ignored, and reads at any
address return the word being written.

This fixes A Bug's Life and Toy Story 2.
2022-04-30 22:06:12 +01:00
Giovanni Bajoandremutro 0bf24b3161 n64: fix (again) TLB logic in case of invalid TLBs
This is a revert of 56458d5b, while keeping b7f96b7109 which is the
real fix that should have been done all along. The former just papered
over the issue and actually introduced a regression (Goldeneye wouldn't
boot).

Co-Authored-By: remutro <[email protected]>
2022-04-29 22:02:24 +01:00
bigbass1997 a527ed6d1d n64: add separate CIC 7102 detection 2022-04-27 16:31:01 +01:00
Giovanni Bajo cf0582f607 n64: implement undocumented broken RSP opcodes
Fix several tests in n64-systemtest ROM.
2022-04-27 16:30:46 +01:00
Giovanni Bajo 02942aca6b n64: Fix RSP MTC2 and LFV opcodes
Fix several tests in n64-systemtest ROM.
2022-04-27 16:30:46 +01:00
Giovanni Bajo aae3caab38 n64: fix RSP BGEZAL/BLTZAL when RA is being tested
Fix several tests in n64-systemtest ROM.
2022-04-27 16:30:46 +01:00
Giovanni Bajo 2a71b978be n64: fix cop0 LL write mask
Fix one test in n64-systemtest ROM.
2022-04-27 16:30:46 +01:00
Giovanni Bajo 336af4c219 Revert "n64: update RSP DMA registers after transfers."
This reverts commit 5e4e64dbd7.

Reason: it breaks Zelda OOT. Requires more thinking.
2022-04-27 00:32:38 +01:00
Giovanni Bajo 1afbf893d7 n64: fix RSP DMEM/IMEM accesses with all access sizes
Like anything else, DMEM/IMEM need to go through the mangling performed
by Memory::IO.

Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo 6bcab5e7ed n64: fix Dual accesses on MMIO regs
Those accesses are really broken beyond repair, and they just write
the upper half.

Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo 5e4e64dbd7 n64: update RSP DMA registers after transfers.
Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo 728db642f0 n64: fix RSP DMA wraparound
RSP DMA always wraps within the same region.

Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo e1f1cbbb4b rsp: small accuracy tweak to semaphore register
Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo b7f96b7109 n64: fix TLB logic for non-global entries
Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo 32c15726ae n64: fix masking of COP0 context / xcontext
Fix several tests in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo 6160a81368 n64: fix TLB random register when wired > 31
Also take the chance to remove the "hopefully accurate but then not
really" implementation which runs after every instruction in favor
of a real random.

Fix one test in n64-systemtest ROM.
2022-04-26 09:14:50 +01:00
Giovanni Bajo 523b6ce5cf n64: improve emulation of vector load opcodes in side cases
Fix several tests in n64-systemtest ROM.
2022-04-24 21:14:49 +01:00
Giovanni Bajo 95e407aae1 n64: implement LWU opcode in RSP
Fix one test in n64-systemtest ROM.
2022-04-24 21:14:49 +01:00
Giovanni Bajo 5e1d6d1aab n64: improve address exceptions with correct COP0 status
Fix several tests in n64-systemtest ROM.
2022-04-24 21:14:49 +01:00
Giovanni Bajo 9f0f4b3432 n64: implement buggy byte/half reads from cartridge space
Fix several tests in n64-systemtest ROM.
2022-04-24 21:14:49 +01:00
Giovanni Bajo f4a38d121d n64; implement correct behaviours of jumps within delay slots
Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo abf454cecb n64: fix JALR when jumping to RA
Fix one test in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo 6c40a5b4c6 n64: fix handling of invalid pagemasks
Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo 56458d5ba5 n64: fix exception kind in case of TLB invalid page and with nonmatching asid
Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo 8d04515b5f n64: fix behavior of conflicting bits in writes to SP_STATUS
Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo f66c93c0c2 n64: fix BGEZAL/BGEZALL opcodes when testing RA
Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo f4c9c11596 n64: fix RSP PC to stay within 12 bits in all cases
Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo 906191ac45 n64: fix COP0 config initial status
Fix a test in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
Giovanni Bajo a0c063cabe n64: fix CPU core to correctly handle 64-bit PC
PC register in VR4300 is 64-bit but there were many places where
the register was incorrectly treated as a 32-bit one, effectively
discarding the higher half.

Fix several tests in n64-systemtest ROM.
2022-04-23 15:49:13 +01:00
invertego ebf41cb696 mia: update out of date resources 2022-04-23 15:49:02 +01:00
invertego c0dfad661b build: fix clean targets on Windows
To get clean makefile targets working robustly under a Windows command
prompt, allow the entire contents of obj/out directories to be deleted.
They are now recreated by the build as needed.
2022-04-23 15:49:02 +01:00
invertego 0b80107cdf build: fix building from Windows command prompt
Replace uses of Bourne shell commands (cp, mkdir) with shell-agnostic
versions that work from a Windows command prompt. Some Bourne shell
commands still remain in macOS and Linux specific makefile lines.
2022-04-23 15:49:02 +01:00
invertego f7eb4483b2 build: fix zlib include path 2022-04-23 15:49:02 +01:00
shinra-electric 9178761932 Fix typo: type --> typeStr
using `type` results in compiler warnings: `warning: result of comparison against a string literal is unspecified (use an explicit string comparison function instead)` 

Was informed by merryhime that `typeStr` was probably intended.
2022-04-16 20:28:18 +01:00
Luke Usher 235a04d54d nall: prevent chd memory leaks + don't allow unsupported track types 2022-04-16 09:43:33 +01:00
Luke Usher 75c0a5f36e mia: support for loading CHD compressed CD-ROM images 2022-04-16 01:09:02 +01:00
Luke Usher 6de4810ab7 Squashed 'thirdparty/libchdr/' content from commit a03e69319
git-subtree-dir: thirdparty/libchdr
git-subtree-split: a03e69319164f69d781ab8e453f8cf407387bd13
2022-04-15 09:58:55 +01:00
Luke Usher 1a5ab89cd4 Merge commit '6de4810ab78cbdc0f18863b9c3ee2a954c7bb4d5' as 'thirdparty/libchdr' 2022-04-15 09:58:55 +01:00
Luke Usher 4a80dbabcb desktop-ui: minor tweaks to msx keyboard map 2022-04-14 16:06:38 +01:00
Luke Usher 6399a5cd15 nall: sse42 -> sse41 2022-04-11 18:32:13 +01:00
Giovanni Bajo acd2130a4d n64: fix SIGILL at startup when SSE4.2 is not supported
The N64 core was forcing -msse4.2 for compilation of the whole Ares.
On host PCs without SSE 4.2, Ares would crash at startup with SIGILL
because GCC used SSE 4.2 instructions even in hiro, while building up
the UI.

RSP SIMD emulation requires SSE 4.1, but the solution here is to
fallback to SISD if SSE 4.1 is not supported. This can be checked
with a predefined macro, that will be set by GCC. We do compile with
-march=native, so this will reflect the host PC of the build process.
For non-local builds (eg: official CI builds), default to nehalem
(Core 2) which is the first architecture with SSE 4.1.

Since this is yet another compile-time flag that affects the N64 core
performance, add a status message warning if SIMD is disabled.

Fixes #193
2022-04-11 07:55:53 +01:00
Giovanni Bajo b3b32ef026 n64: add missing file for mame RDP SSE2 implementation
Mame RDP has two codepath: one for SSE4.1 and one for SSE2. The latter
relies on some static tables that weren't imported from upstream.
2022-04-11 07:55:53 +01:00
Luke Usher 4ce8290e49 sfc: pad bsx flash to minimum size of a memory cart
Fixes running BS-X content smaller than 1M
2022-04-11 00:00:33 +01:00
Luke Usher fd6698c5f6 sfc: fix a hang when loading Satallaview content 2022-04-10 23:22:49 +01:00
Luke Usher f92605cfbd desktop-ui: add support for using Supergrafx and CD together
The homebrew game "Space Ava 201" does this
2022-04-10 20:42:25 +01:00
Luke Usher 2c3c9fe25f desktop-ui: implement status queue + 'keyboard capture' notice 2022-04-09 10:17:39 +01:00
Luke Usher 18e013beda mia: formatting fixes 2022-04-07 21:55:13 +01:00
Luke Usher 1e26a8be76 msx: improve rom type detection and add more board types 2022-04-07 21:52:15 +01:00
Luke Usher 300fa4801f msx: fix a number of memory layout issues
The results of the "tinyslotchecker" rom are sane now;
it finally reports a valid configuration.
2022-04-07 20:25:28 +01:00
Luke Usher c5106a1982 msx: require a proper bios rom for msx1
This is because C-BIOS has significant compatibility issues, including
the ability to boot any software that uses BASIC.

The MSX2 emulation is still too preliminary to be able to boot the
official bios, so msx2 will continue to use C-BIOS for the time being.
2022-04-07 20:24:37 +01:00
Luke Usher 1d22db7e1c desktop-ui: fix an issue where settings would not persist after previous change 2022-04-06 22:08:45 +01:00
Luke Usher c986a14401 desktop-ui: remap all inputs based on physical controller layouts
Note: you will need to reconfigure your virtual pad after this commit.
2022-04-06 21:29:38 +01:00
Luke Usher a6d3a57df9 gba: slightly reduce red tint, to bring the display closer to my GBA SP 2022-04-06 09:47:33 +01:00
Luke Usher 8d04df1641 desktop-ui: add hotkey to toggle fast forward 2022-04-05 23:23:15 +01:00
Luke Usher 6baebd0f06 msx: update cbios to latest japanese revision
Fixes kana fonts in many games
2022-04-04 23:23:15 +01:00
Luke Usher e34bfc04e3 msx: fix linear cartridge rom mapping
This allows many titles that didn't boot previously to start
2022-04-04 22:07:11 +01:00
Luke Usher 0d7c70bdba tms9918: implement text mode 2022-04-04 21:51:09 +01:00
Luke Usher fccd73f8e3 gb: tweak display timing; fixes dmg-acid2 and cgb-acid2 2022-03-31 18:44:19 +01:00
kev4cards c52c7fd1bb Change inner dead-zone size and geometry
Replace the large, inner circular dead-zone of the N64 control stick with a small, inner square dead-zone; separate the inner and outer dead-zones; attach the scaling equation to the outer dead-zone; fixes https://github.com/ares-emulator/ares/issues/86 .
2022-03-31 09:53:11 +01:00
kev4cards 5210341ad3 Update gamepad.cpp 2022-03-31 09:53:11 +01:00
AmatCoder d7e456a46f hiro: fix menubar visibility with gtk3
Fixing issue #229
2022-03-23 11:31:06 +00:00
Luke Usher a3b126f079 pce: initialise bram for cd-rom system 2022-03-20 21:54:58 +00:00
Giovanni Bajo bc8c308ee9 n64: add support for homebrew-specific special header flag
Nintendo 64 homebrew community is standardizing on a convention to
declare the save memory type as a header flag. This convention has been
first introduced by EverDrive 64 as a way to handle games not present
in their DB, but it's been since adopted by libdragon as well.

With this commit, Ares will automatically configure the correct savetype
for homebrew ROMs using the special header flag.
2022-03-19 10:12:27 +00:00
TascoDLX 124a678c8e m68000: omit illegal addressing modes for CMPA & TST 2022-03-17 09:08:29 +00:00
TascoDLX 333dd85ba5 m68000: fixed group 2 exception prefetch (CHK, DIV by zero) 2022-03-17 09:08:29 +00:00
jbo-85 d08a9bdcc2 sfc: Add database entry for the "Super FX voxel demo" to specify a RAM size of 64k instead of 32k
Support ROM/RAM addressing for Super FX ROMs that are not a power of 2
2022-03-14 22:45:55 +00:00
Luke Usher f4a018036d ruby: fix typo (missing semicolon) in pulseaudio driver 2022-03-14 09:31:34 +00:00
AmatCoder 65f4e176c2 ruby: pulseaudio shouldn't block for events
Now it sets a timeout of one second
2022-03-14 09:14:59 +00:00
Luke Usher 3b8e9bbff5 msx: connect/fix up keyboard emulation
This is implemented via a 'keyboard capture' hotkey; this ensures that
users can switch between keyboard & gamepad emulation, even if they have
no physical gamepad.

When keyboard capture is enabled:
 - All non-hotkey key-presses are translated and dispatched to the guest
 - Virtual GamePad Inputs that are mapped to Keyboard no longer trigger
 - Virtual GamePad inputs that are mapped to Mouse or physical GamePads work normally.
2022-03-10 23:56:01 +00:00
Luke Usher 8f2c6eff9c desktop-ui: fix an issue where System directories were created incorrectly 2022-03-10 21:24:02 +00:00
Luke Usher 85a5292648 desktop-ui: default filter/shader to "None" rather than "Blur" 2022-03-10 19:57:57 +00:00
Luke Usher c8f261201c mia: recompile resources 2022-03-08 19:17:32 +00:00
Luke Usher e42c79677d Merge branch 'master' of https://github.com/ares-emulator/ares 2022-03-08 19:14:59 +00:00
Luke Usher de9ec5d83c mia: oops, title => label 2022-03-08 10:35:27 +00:00
Luke Usher 93e89446e2 mia: update sfc board database & heuristics 2022-03-08 10:04:22 +00:00
Luke Usher 2a28c46a41 desktop-ui: update locate to carry over old settings, explain choices. 2022-03-07 22:00:43 +00:00
Luke Usher 23fa258dfd nall: remove hardcoding of lto=true
This was overriding the given command line preference, making it difficult to build with lto disabled.
2022-03-07 09:59:30 +00:00
invertego e86a18f293 md/32x: add missing initialization during power-on
Nearly all state is initialized to a fixed or random value during
power-on, though a few things are deliberately left alone on soft reset.

A handful of fields weren't being initialized at all, save for the zero
initialization that occurs during process creation. This state would be
persisted from one ROM load to the next.

This fix is intended to address 32x PWM audio eventually going silent
after switching between multiple roms.
2022-03-07 09:32:22 +00:00
Giovanni Bajo 665229a892 macos: codesign binary after build
This is required on Apple Silicon and won't harm on Intel.
2022-03-07 09:21:05 +00:00
Giovanni Bajo 4d46be8344 Add top-level Makefile 2022-03-07 09:21:05 +00:00
Luke Usher 5a839f1010 Revert "sfc: slight ppu performance boost by templating arguments"
This reverts commit d01b8db441.

This was an experimental local change that should've never made it to master
2022-03-05 21:58:33 +00:00
Luke Usher d5dcc645e4 desktop-ui: ensure ares is 'portable' on Windows 2022-03-04 20:09:03 +00:00
Luke Usher 35f6a93713 nall: determine desktop path based on environment 2022-03-04 20:09:03 +00:00
Luke Usher d01b8db441 sfc: slight ppu performance boost by templating arguments 2022-03-04 20:09:03 +00:00
TascoDLX a8373002b5 ares: bump serializer version (md updates) 2022-03-04 20:08:15 +00:00
TascoDLX 5d192fb215 md/32x: readjust cpu sync after recent changes (Kolibri fix) 2022-03-04 20:08:15 +00:00
TascoDLX e13722870e m68000: adjust interrupt/exception timing
*based on Motorola app note AN1012/D. Timing for autovector action is approximated based on MegaDrive h/w tests.
2022-03-04 20:08:15 +00:00
TascoDLX 82e49616a2 md: reimplement refresh timings
*based on research by Mask of Destiny and approximations from h/w tests
2022-03-04 20:08:15 +00:00
TascoDLX 7d425dc5e7 md: vdp vscroll bug implemented 2022-03-04 20:08:15 +00:00
TascoDLX aa975f8f6c md: vdp dma transfer is delayed after bus is acquired 2022-03-04 20:08:15 +00:00
TascoDLX 7fcd6d1f5a md: vdp timing tweaks for accuracy
- vtick() shifted; can't be any later w/o breaking due to render alignment
- vblank flag update moved to vtick(); also updated on v28/v30 switch (per ekeeke's note re: Kabuto's findings)
- corrected apu irq timing
- readded extra refresh slot when display is disabled (confirmed after latest timing fixes)
2022-03-04 20:08:15 +00:00
Luke Usher 9d7c83aa39 Update README.md 2022-03-04 09:02:47 +00:00
Luke Usher 1d8aa3fc3f ares v127 released
ares v127 brings significant improvements to Mega Drive and Nintendo 64
emulation, as well as improvements to NES / Famicom and SNES / Super Famicom.

Other than the usual emulation improvements, there have been the following notable
changes:

**Apple Silicon Support**

ares v127 fixes the recompiler for aarch64 architecture, meaning that it is now
possible to create Apple Silicon/M1 native builds, without relying on Rosetta
and without losing support for the high performance JIT recompilers.

In order to be Apple Silicon Native, it is currently required to compile ares
from source code as automated builds have not yet been configured, but users
who wish to do so will no longer lose functionality or suffer poor performance
as a result.

**MAME RDP**

ares's Nintendo 64 core uses paraLLEl-RDP by default; this brings fast and
accurate RDP emulation as long as Vulkan is present on the users machine; this
meant that Nintendo 64 emulation was completely broken for all configurations
without Vulkan support, including macOS.

ares v127 adds support for MAME's RDP implementation as a fallback, allowing
Nintendo 64 emulation to be used when Vulkan is not present. This is handled
automatically, however, a new option has been added to video settings to allow
Vulkan support to be toggled, giving all users the ability to test the MAME
RDP, if they wish to do so.

Although MAME RDP is now an option, paraLLEl-RDP is still the recommended
choice, for both performance and accuracy.

**Pixel Accuracy Mode**

ares has contains two implementations of some of our emulated hardware; one
optimised for performance, and another optimised for accuracy.
Historically, the choice of which path to use has never been exposed to the
user; higan *always* used the 'accurate' profiles, with ares always opting for
the 'performance' profiles; any user wishing to change this would be required
to compile ares themselves from source.

As of ares v127, we now provide a new option in the emulator settings: "Pixel
Accuracy"; when this is enabled, any emulator core that supports a pixel
accurate mode will use it.

For 99% of games, the default fast profiles will be sufficient, but enabling
"Pixel Accuracy" allows games that require mid-scanline effects, such as the
infamous "Air Strike Patrol" to function properly.

The following systems are currently support the Pixel Accuracy setting:

* NEC - PC-Engine / TurboGrafx
* Nintendo - Super Famicom / SNES

**Changelog:**
* desktop-ui: hook up pc-engine 6-button pads to virtual pads [Luke Usher]
* desktop-ui: implement frame advance [Luke Usher]
* fc: add bus conflicts to cnrom [encoded-byte]
* fc: check for ram on mmc1 [encoded-byte]
* fc: check if ram exists on mmc3 [encoded-byte]
* fc: clear oam address on each scanline [encoded-byte]
* fc: improve mmc3 irq behavior [encoded-byte]
* fc: improve ppu skipped clock timing [encoded-byte]
* fc: use hkrom for mmc6 [encoded-byte]
* m68000: allow recovery from zero divide [TascoDLX]
* m68000: reimplement DBcc instruction with correct timing[TascoDLX]
* md: A few fixes to SRAM save game [rasky]
* md: correct overscan / output when display is off [TascoDLX]
* md: correct reads of CRAM and VSRAM [rasky]
* md: detect region 'K' as NTSC-J [invertego]
* md: fix APU port in [rasky]
* md: fix debug register sprite masking [rasky]
* md: fix high bits in control port read [rasky]
* md: fix misaligned reads from VRAM [rasky]
* md: fix register masked write in mode5 [rasky]
* md: fix vblank bit toggling horizontal timing [rasky]
* md: fix VSRAM out of bound accesses [rasky]
* md: ignore erroneous device string used by Codemasters [invertego]
* md: implement undocumented VDP VRAM 8-bit reading mode [rasky]
* md: persist VDP state on reset [invertego]
* md: restore vdp free slot lost to refresh [TascoDLX]
* mia: Correct save type for Premier Manager 64 (N64) [sp1187]
* mia: Correct save type for Transformers: Beast Wars Transmetals (N64) [sp1187]
* mia: correct type for pak attribute [encoded-byte]
* mia: fix 32x sram [Luke Usher]
* mia: properly pass MD eeprom details to ares [Luke Usher]
* mia: updated famicom database [encoded-byte]
* mos6502: add illegal nops [encoded-byte]
* ms: correct overscan inc. dynamic screen resizing [TascoDLX]
* n64: add MAME RDP as a fallback for parallel-RDP [invertego]
* n64: allow vulkan to be disabled [Luke Usher]
* n64: change PI DMA to use 16 bit fetches [CasualPokePlayer]
* n64: fix mult/div opcode timings [rasky]
* n64: fix RSP halt condition to be more accurate [rasky]
* n64: fix several RDP regressions [invertego]
* n64: fix small bug in VMACQ [rasky]
* n64: fix SRA/SRAV opcodes [rasky]
* n64: fix vulkan detection [Luke Usher]
* n64: improve rsp recompiler pool allocation [invertego]
* n64: swap RSP/RDP order [CasualPokePlayer]
* n64: templatize rsp vpu [invertego]
* n64: vulkan tweaks [Luke Usher]
* nall: fix many compilation warnings on macOS [Luke Usher]
* nall: fix page protection on Apple silicon [invertego]
* nall: rewrite recompiler for machine-independence using sljit [invertego]
* pce: runtime pixel accurate VDP setting [invertego]
* sfc: fix horizontal off-screen test for sprites [jbo-85]
* sfc: fix missing sprite tile on Super Conflict title screen [jbo-85]
* sfc: fix missing sprites in Jurassic Park that are partly offscreen [jbo-85]
* sfc: runtime pixel accurate PPU setting [invertego]
* sh2: move registers into POD struct [invertego]
2022-03-03 11:12:11 +00:00
invertego 7692f102e5 pce: runtime pixel accurate VDP setting 2022-03-03 09:31:59 +00:00
invertego 3be1ced3ef sfc: runtime pixel accurate PPU setting 2022-03-02 09:59:35 +00:00
Luke Usher c78d931e28 Revert "ares: remove performance renderers"
This reverts commit dc5ff13b5c.
2022-02-26 21:59:51 +00:00
TascoDLX e7f24c06d5 m68000: reimplement DBcc instruction with correct timing 2022-02-26 12:13:56 +00:00
Luke Usher dc5ff13b5c ares: remove performance renderers
Some systems in ares contained two implementations of video hardware;
these systems were Mega Drive / Genesis, Super Famicom / SNES, and
PC-Engine / TurboGrafx 16.

For the Mega Drive, we switched to the accuracy core quite some time ago,
SNES and PC-Engine we were still using the performance core.

Going forward, we wish to only maintain the accuracy focused renderers.
2022-02-23 13:04:45 +00:00
Luke Usher 7140b64b13 nall: fix many compilation warnings on macOS 2022-02-23 13:00:45 +00:00
Giovanni Bajo 4d4d3308b9 md: fix debug register sprite masking
VDP always writes transparent pixels to the internal sprite buffers,
overwriting previous transparent pixels; then the first non-transparent
pixel sticks and wins. This implementation detail only reveals itself
when sprite plane masking is activated using the VDP debug register:
in that case, the palette number of transparent pixels concurs in the
AND of the visible pixels, and thus the correct palette number should
be found in the buffer.

Fixes the logo effect of Overdrive 2 (ref #255)
2022-02-23 08:50:24 +00:00
Giovanni Bajo e02a7cc6fb md: fix APU port in
It's technically unused, but it must return 0xFF. Looks like some
game erroneously read it anyway.

Fixes #286
2022-02-23 08:50:13 +00:00
Giovanni Bajo ce981795f5 md: second attempt at vblank bit timing
After more analysis, it looks like the correct thing to do is to
check for vblank status on most of the raster line. Kabuto's doc says
that it is possibly to switch from V30 to V28 during line 224 and
still see the vblank triggered, which suggests that the hardware does
actually check it on most of the time.

This seems to fix #402 and does not seem to cause any immediate
regression in timing-sensitive ROMs.
2022-02-23 08:50:00 +00:00
Giovanni Bajo 51d16a58cd md: fix vblank bit toggling horizontal timing
Currently, the vblank bit is turned on/off at the same time vcounter
is incremented, which is a reasonable assumption, missing any real
data on this.

But it turns out this is responsible for most of the breakage of
Overdrive 1. I did some tests and it looks like anything in the
range >= 0xF0 (same line), or <= 0x80 (on next line) fixes those bugs.
I decided to bind it to vedge(), which means that vblank bit now is
triggered the same instant that VINT is raised, which also makes sense
when you think of it.

Updates #212
2022-02-22 11:05:19 +00:00
TascoDLX 89287dabf1 ms: correct overscan inc. dynamic screen resizing 2022-02-21 08:17:21 +00:00
TascoDLX 5b52f94aa1 md: correct overscan / output when display is off 2022-02-21 08:17:21 +00:00
Luke Usher f67373732c mia: properly pass MD eeprom details to ares 2022-02-21 08:00:10 +00:00
Luke Usher 8d43e67b96 fix eeprom after PR #357 2022-02-20 20:32:44 +00:00
Luke Usher 8a37b00fef mia: fix 32x sram 2022-02-20 20:20:49 +00:00
Luke Usher 80c2678930 desktop-ui: hook up pc-engine 6-button pads to virtual pads 2022-02-18 13:46:07 +00:00
TascoDLX 510cc55d76 m68000: allow recovery from zero divide 2022-02-18 09:15:28 +00:00
TascoDLX 5c285e3ce3 md: restore vdp free slot lost to refresh 2022-02-18 09:15:28 +00:00
Giovanni Bajo 21203b2401 md: fix misaligned reads from VRAM
VRAM misaligned addresses were implemented with partial slot reads.
This seems to be wrong, as proven by vdpfifotesting: once the slot is
read, the whole word is invalidated.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 2c8f3ef84d md: implement undocumented VDP VRAM 8-bit reading mode
Using target 0xC, it is possible to do 8-bit reads from VRAM.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 80348ae691 md: correct reads of CRAM and VSRAM
Reading unmapped bits in CRAM and VSRAM returns a copy of the same bits
in the top slot of the FIFO.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 5e55f1a8a7 md: fix VSRAM out of bound accesses
VSRAM is actually made of 40 11-bit words, though the chip only accesses
the first 10 bits. The top bit on each word is still readable and writable
though, so they should be emulated.

Also, reading at addresses >= 40 returns a copy of the first word,
rather than 0.
2022-02-17 19:04:06 +00:00
Giovanni Bajo c63257029e md: fix register masked write in mode5
Mode5 (SMS compatibility mode) is unsupported and mostly undocumented,
but the vdpfifotesting ROM at least checks one know behavior: non-SMS
registers can't be written.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 93d44072da md: fix high bits in control port read
Bits 10-15 in control port read (aka status register) are not fixed
as found in some online docs, but they are actually an open bus
that returns the instruction prefetched by m68k at that cycle.
2022-02-17 19:04:06 +00:00
Luke Usher 2cf921be41 desktop-ui: implement frame advance 2022-02-16 22:35:17 +00:00
Luke Usher cc8ccbf1bd point to the new ares website 2022-02-16 15:34:01 +00:00
Giovanni Bajo 10e589672f [MD] A few fixes to SRAM save game
* Allow MIA to declare address for mapping (typically from ROM header)
   rather than hardcoding 0x200000.
 * Autoenable SRAM if the ROM size is smaller. Also make the ramEnable
   I/O conditional to the same layout.
 * Add MIA entries for NBA Live 98, NHL 96 and NHL 98, which have a 8-bit
   SRAM not declared in the header.
2022-02-16 10:27:10 +00:00
jbo-85 3c28ecd8b6 sfc: fix missing sprites in Jurassic Park that are partly offscreen 2022-02-14 07:26:10 +00:00
jbo-85 cc64eb50c0 sfc: fix missing sprite tile on Super Conflict title screen; sprites that start on x==256 (offscreen) are considered for the range-tile-over checks 2022-02-14 07:26:10 +00:00
jbo-85 5cb877b49c sfc: fix horizontal off-screen test for sprites; fixes SNES test card Burn-In test, maybe others 2022-02-14 07:26:10 +00:00
CasualPokePlayer 275ba735de change PI DMA to use 16 bit fetches
Using the read<Byte> currently in use breaks DMAs from Flash (breaking Paper Mario, probably others), which always return 0. I assume 8 bit reads are just not allowed for Flash, although I can't verify this myself.
Checking https://github.com/n64dev/cen64/commit/8367698e20cca7eb262779f19c8c7847ae31bb29, it seems that fetches should always be 16 bits anyways, so I presume this is the correct behavior, and fixes DMAs from Flash.
No regressions with the original testrom have occured.
2022-02-12 10:15:37 +00:00
CasualPokePlayer 2b894add40 Swap RSP/RDP order, fixes #348 2022-02-11 12:13:33 +00:00
Luke Usher 58690cd5fc n64: fix vulkan detection 2022-02-09 07:22:20 +00:00
Luke Usher b03da384d8 n64: vulkan tweaks
- introduce a status api, so cores can report status messages to the UI
- avoid calling vulkan-related functions when vulkan is disabled
- show status messages so the user is aware of what RDP implementation is active
- Disabled 'loaded' status message, it prevented the Vulkan messages being shown
2022-02-08 20:34:05 +00:00
invertego f382a18d10 libco: update Windows thread information block
Update the stack base and limit in the thread information block. This is
required to support kernel stack unwinding, which is used for both C++
exception dispatch and setjmp/longjmp on Windows x64 and ARM64.

Add a cothread entry routine that consistently aborts when the user
supplied routine returns. Debugger stack backtraces now stop
consistently at this entry routine.
2022-02-07 08:56:29 +00:00
invertego f63cad6de9 md: ignore erroneous device string used by Codemasters 2022-02-06 16:09:57 +00:00
invertego 34d2c091c7 md: detect region 'K' as NTSC-J
The Korean release of Tiny Toon Adventures specifies its region with a
'K' and, like several other Korean releases, is region locked to
Japanese consoles. It may be the only one with a 'K' specifier, though.
2022-02-03 12:36:32 +00:00
invertego d845582d4a md: persist VDP state on reset
The game Skitchin' performs a fade out effect after reset with the
expectation that VDP state was persisted across the reset.
2022-02-02 09:55:28 +00:00
encoded-byte 9788752bc1 fc: check for ram on mmc1 2022-01-26 20:15:20 +00:00
encoded-byte 5f60ab2dde mia: updated fc database 2022-01-26 20:15:20 +00:00
encoded-byte 628432e75c fc: clear oam address on each scanline 2022-01-26 20:15:20 +00:00
Luke Usher 980e9ed687 n64: allow vulkan to be disabled 2022-01-19 19:52:19 +00:00
Giovanni Bajo e98a313ed6 n64: fix mult/div opcode timings
Taken from NEC VR4300 manual. These are easier than expected to
implement accurately as the CPU doesn't execute them in parallel but
stalls the entire pipeline while they are running.
2022-01-18 09:14:56 +00:00
Giovanni Bajo 90d42d1ea3 n64: fix SRA/SRAV opcodes
These two opcodes were incorrectly ignoring the higher part of the
register before shifting.
2022-01-18 08:02:23 +00:00
Giovanni Bajo 11d6b7c673 rsp: fix small bug in VMACQ
Real hardware testing showed that VMACQ does not modify the accumulator
if it is exactly zero. Fix it.

Also remove two "unconfirmed" comments which I have personally verified.
2022-01-13 08:41:58 +00:00
invertego c04bcf03e0 n64: fix several RDP regressions
- YUV texture fetch and color space conversion
- textured rectangle command parsing
- set primitive depth command parsing
2022-01-12 14:44:39 +00:00
Luke Usher c4d73cc427 Update split-subtrees.yml
Debugging CI action
2022-01-12 11:26:05 +00:00
Luke Usher 8634c11cff Update split-subtrees.yml 2022-01-12 11:22:37 +00:00
Luke Usher 8a5c48e3ed add split-subtrees github action 2022-01-12 11:18:27 +00:00
encoded-byte b22513a6e8 fc: check if ram exists on mmc3 2022-01-05 23:24:10 +00:00
Giovanni Bajo e5220e8fd0 n64: fix RSP halt condition to be more accurate
* Fix usage of break opcode in a delay slot. After unhalt, the
   RSP continues execution, taking the jump if needed. So the
   branch state should not be affected by the break opcode.
 * Reading from SP_PC produces garbage (open bus?) when the RSP is
   running. This fix is unlikely to fix game but may help homebrew
   developers.
 * Writing to SP_PC resets the RSP MIPS pipeline, so any pending branch
   is canceled.
2022-01-03 12:21:45 +00:00
encoded-byte 8099bf54fd mia: updated famicom database 2022-01-01 20:13:57 +00:00
encoded-byte bed4ce3659 fc: use hkrom for mmc6 2022-01-01 20:13:57 +00:00
encoded-byte f630a9418d fc: improve mmc3 irq behavior 2022-01-01 20:13:57 +00:00
invertego f9e4070b15 n64: improve rsp recompiler pool allocation
The existing implementation was allocating pools in the code cache only
to copy them to the heap. This was wasteful on multiple counts: the
space in the cache would go unused until the next allocator flush and
the data copied to the heap consisted almost entirely of zeroes. Best of
all, writing the hash code to the code cache was triggering a page
protection fault on Apple M1.

A small data structure refactor avoids the heap allocations, the copies,
the wasted space in the code cache, and the page protection issue.
2021-12-20 18:16:01 +00:00
invertego 5050d6c45d n64: add MAME RDP as a fallback for parallel-RDP
For systems that do not meet parallel-RDP's Vulkan requirements, use the
pure software RDP implementation from MAME as a fallback.

All of the source files taken from MAME are either BSD-3-Clause licensed
or public domain. Edits were kept to a minimum and guarded by ifdefs to
ease future integrations.
2021-12-20 18:16:01 +00:00
invertego b56dac15e9 recompiler: fix page protection on Apple silicon
- when dynamically allocating executable memory, use VirtualAlloc/mmap()
  for finer control over allocation flags (like MAP_JIT on macOS/arm64)
- toggle JIT memory protection around writes on macOS/arm64
- reduce recompiler code cache sizes to improve likelihood of dynamic
  allocations being close to ares image (affects branch perf)
- remove arch x86_64 override on macOS non-local builds; native arm64
  builds should now be at feature parity with x86_64
- sh2: prevent pollution of code cache with empty pools by invalidate()
2021-12-15 20:01:40 +00:00
invertego a8e35d0299 recompiler: rewrite for machine-independence 2021-12-14 09:02:52 +00:00
invertego 1462bac4c5 sljit: wire up to the build 2021-12-14 09:02:52 +00:00
invertego 5e2a8f08bf n64: templatize rsp vpu 2021-12-14 09:02:52 +00:00
invertego e88cb48f19 sh2: move registers into POD struct 2021-12-14 09:02:52 +00:00
invertego 70a669287b build: arm64 build fixes 2021-12-14 09:02:52 +00:00
invertego 9d3addc241 mia: fix build break 2021-12-14 09:02:52 +00:00
Luke Usher f22e1405dc Squashed 'thirdparty/sljit/' content from commit d6a0fa61e
git-subtree-dir: thirdparty/sljit
git-subtree-split: d6a0fa61e09266ad2e36d8ccd56f775e37b749e9
2021-12-11 19:43:23 +00:00
Luke Usher 4542ac5674 Merge commit 'f22e1405dc12ed4fdc05a3f6a9e5fa7ee966f627' as 'thirdparty/sljit' 2021-12-11 19:43:23 +00:00
encoded-byte 5c3ac88547 mia: updated famicom database 2021-12-10 15:55:35 +00:00
encoded-byte 85fcda5b42 mia: correct type for pak attribute 2021-12-10 15:55:35 +00:00
encoded-byte f512fdc969 fc: add bus conflicts to cnrom 2021-12-10 15:55:35 +00:00
encoded-byte 64286fbd46 fc: improve ppu skipped clock timing 2021-12-06 16:54:50 +00:00
Simon Eriksson e0c4cc76f2 mia: Correct save type for Transformers: Beast Wars Transmetals (N64) 2021-12-03 21:59:50 +00:00
encoded-byte 7b4a346336 component: add illegal nops to mos6502 cpu 2021-12-02 21:49:57 +00:00
Simon Eriksson 763f7da939 mia: Correct save type for Premier Manager 64 (N64) 2021-12-01 20:05:09 +00:00
Luke Usher ee188dcb31 ares: bump version number to v126 2021-11-28 08:00:14 +00:00
encoded-byte 1f8b3f86b9 fc: partial support for bandai datach 2021-11-27 14:30:51 +00:00
encoded-byte 18e334a6cf fc: support for bandai oeka board 2021-11-27 14:30:51 +00:00
encoded-byte 24675b0161 fc: fix saving on bandai lz93d50 + m24c02 2021-11-25 21:33:15 +00:00
encoded-byte f4caedcd5e component: fix eeprom m24c mode selection 2021-11-25 21:33:15 +00:00
encoded-byte bdc6313e46 fc: support for bandai karaoke board 2021-11-25 09:52:14 +00:00
encoded-byte fd3a56b0a2 fc: support for cprom board 2021-11-25 09:52:14 +00:00
encoded-byte 663b6951e3 fc: attach sunsoft 4 + ext rom 2021-11-25 09:52:14 +00:00
encoded-byte 554430276c fc: support for namco 163 board (no sound) 2021-11-22 20:47:23 +00:00
Luke Usher 3748baf8e8 Revert "libco: disable backing up SSE registers on Win64"
This reverts commit 32690bb6f0.

Skipping restoration of SSE registers turned out to be too fragile/risky
2021-11-22 15:11:28 +00:00
encoded-byte e1bd1bfdf9 fc: support txrom variants 2021-11-22 09:06:54 +00:00
encoded-byte 5f6b75a013 fc: support for cnrom with security 2021-11-22 09:06:54 +00:00
encoded-byte 2d6fe4aa16 fc: support for sxrom boards with mmc1a 2021-11-22 09:06:54 +00:00
encoded-byte b49e5496da fc: support for unrom variants 2021-11-22 09:06:54 +00:00
encoded-byte 645d481493 fc: support for bandai lz93d50 variant 2021-11-22 09:06:54 +00:00
Simon Eriksson bc14c35cb5 fc: Support for Action 52 board 2021-11-18 06:40:36 +00:00
encoded-byte 16210cf1bf fc: split bandai fcg / lz93d50 boards
To better describe chips and prepare support for other related boards
2021-11-17 09:04:23 +00:00
encoded-byte d34e54c808 fc: support for bandai 74161 boards 2021-11-17 09:04:23 +00:00
encoded-byte 76508d02bd mia: updated fc database
Games tested so far. Two new not working games discovered:
- Aladdin (Europe): Hangs after starting game
- Wizards & Warriors (Europe): Graphicals glitches on HUD, severe slowdowns. PAL timing related?
2021-11-17 09:04:23 +00:00
Luke Usher 32690bb6f0 libco: disable backing up SSE registers on Win64
For some users, this gives a dramatic performance boost, and finally
brings the Windows builds in-line with Linux/Mac builds in terms of
performance.

This technically goes against the Win64 ABI, which demands that SSE
registers are saved, however, in-practice, our testing has shown that
this has not caused any issues so far.
2021-11-16 11:10:41 +00:00
Simon Eriksson 9eca885d8b fc: Support for Color Dreams board 2021-11-16 06:28:36 +00:00
Luke Usher f9a53d2167 msx: remove rom debug print 2021-11-12 18:28:41 +00:00
Luke Usher fd5b22c5cd msx: use the slot layout and memory expected by cbios 2021-11-12 18:23:21 +00:00
Luke Usher 2f6edc7efe msx: add huristics for rom type detection 2021-11-12 18:22:48 +00:00
invertego 12ac1b5534 hiro: monitor activate messages for window focus
Monitor WM_ACTIVATE instead of WM_SETFOCUS/KILLFOCUS messages to avoid
responding to spurious notifications.
2021-11-11 09:09:50 +00:00
encoded-byte c8fa67ec39 mia: updated fc database
All entries tested and working (except for "Akira")
2021-11-10 22:17:27 +00:00
encoded-byte 5341d0d7d4 fc: support for namco 118 board 2021-11-10 22:17:27 +00:00
encoded-byte ad0105de25 fc: support for namco 175/340 boards 2021-11-10 22:17:27 +00:00
Luke Usher e3663a9eec bump version to v125.1 2021-11-10 21:34:06 +00:00
invertego a01ccb72f2 hiro: track window focus via messages, not polling
On Windows, track WM_SETFOCUS and WM_KILLFOCUS messages to determine
focused status rather than polling with GetForegroundWindow(). The
latter involves a syscall and causes noticeable slowdown when called
repeatedly, such as when GameBoy games poll joypad status in a loop.
2021-11-10 21:32:41 +00:00
Luke Usher 27996d81d4 fc: implement gtrom mapper 2021-11-10 21:01:42 +00:00
invertego 47af07c507 ms, sfc: fix light gun related crashes
Fix unloading systems with light gun peripherals active. The code
responsible for removing crosshair sprites was dereferencing a null
pointer. This cleanup now occurs in Screen::quit() when unloading.
2021-11-10 18:31:51 +00:00
invertego cc6ab702c0 mia: prefer known rom extensions in zip archives
Instead of just reading the first file from a zip archive, prefer files
that match known rom extensions. Extra files such as readmes should not
interfere with the loading of the game.
2021-11-10 11:08:33 +00:00
Luke Usher d633b92a57 v125 2021-11-09 20:20:53 +00:00
Luke Usher 579be2bb04 ms: pass "set VDPbuffer" tests in VDPTEST 2021-11-09 11:45:19 +00:00
Luke Usher 89db771c73 gg: add Mickey Mouse no Castle Illusion (Japan) to game database 2021-11-09 11:00:35 +00:00
invertego 33b95510cb sh2: fix several instructions, default to recompiler
Fix several correctness bugs in SH2 instructions in both the interpreter and
recompiler and switch the default implementation from the cached interpreter to
the recompiler. This brings the 32x games BC Racers and Zaxxon's
Motherbase 2000 to playable status. Fixed instructions:

Interpreter
- ADDC Rm,Rn
- ADDV Rm,Rn

Recompiler
- MAC.W @Rm+,@Rn+

Both
- CMP/STR Rm,Rn
- MOV.B Rm,@-Rn
- MOV.W Rm,@-Rn
- MOV.L Rm,@-Rn
- NEGC Rm,Rn
2021-11-09 08:50:44 +00:00
encoded-byte 31eee5f6f1 fc: pinout support for konami vrc7 2021-11-05 16:25:13 +00:00
encoded-byte 7ba1c2925d mia: add fc konami vrc2/4/6 to database 2021-11-05 16:25:13 +00:00
encoded-byte dbc1b4ccc6 fc: support for konami vrc2 variant 2021-11-05 16:25:13 +00:00
encoded-byte a3a1969775 fc: always check for ram presence
Some games without ram try to read this space, better be safe
2021-11-05 16:25:13 +00:00
encoded-byte ab67ec12cd fc: refactor of jaleco boards 2021-11-03 06:41:59 +00:00
encoded-byte 02cb1268e4 fc: support for jaleco jf13 board 2021-11-03 06:41:59 +00:00
encoded-byte 50994a2c60 fc: support for jaleco fc05 board 2021-11-03 06:41:59 +00:00
encoded-byte 9e099e9b15 fc: remove unneeded test
No need to test rom if board don't support ram
2021-11-03 06:41:59 +00:00
encoded-byte 16cbd8b149 fc: support for jaleco jf17/19 boards 2021-11-03 06:41:59 +00:00
encoded-byte 5491a6a9c8 fc: fix jaleco jf14 banking 2021-11-03 06:41:59 +00:00
encoded-byte d0a14c682d fc: support for irem if12 board 2021-11-01 13:01:30 +00:00
encoded-byte 4eb4b6cdb7 fc: support for irem lrog017 board 2021-11-01 13:01:30 +00:00
encoded-byte 9bc59159d0 fc: check for ram and simplify banking 2021-11-01 13:01:30 +00:00
encoded-byte f518f04ffa mia: database for fc 2021-11-01 13:01:30 +00:00
encoded-byte 2ce06bc4d1 fc: support for irem h3001 board 2021-11-01 13:01:30 +00:00
encoded-byte a3285b6959 fc: support for irem g101 board 2021-11-01 13:01:30 +00:00
jbo_85 8a7ac00d03 snes: Fix 'D-Port B+O+S' test in SPC7110 Check Program as reported in issue #228, this bug was introduced after the huge rewrite to the type system in v117 2021-10-27 08:15:35 +01:00
encoded-byte 38cf1b0f8e fc: fix address 2021-10-26 22:57:57 +01:00
encoded-byte d26ffccc9f fc: add chip type to manifest 2021-10-26 22:57:57 +01:00
encoded-byte 9ee4afd17f fc: support for taito x1-017 board 2021-10-26 22:57:57 +01:00
encoded-byte 61dbb64999 fc: support for taito x1-005 board 2021-10-26 22:57:57 +01:00
encoded-byte 2d5e1ba552 fc: support for taito tc0690 board 2021-10-26 22:57:57 +01:00
encoded-byte a31b0a7768 fc: support for taito tc0190 board 2021-10-26 22:57:57 +01:00
TascoDLX 7c9ff4ba6d md: sram support for Super Hydlide (J) 2021-10-25 09:14:15 +01:00
TascoDLX 0bc30dd26d md: fix interrupt ack special case 2021-10-25 09:14:15 +01:00
TascoDLX f73032e17f mcd: support for 3-button pad (as default) 2021-10-25 09:14:15 +01:00
TascoDLX 2eee9bcd4e mcd: autodetect disc region based on boot code 2021-10-25 09:14:15 +01:00
TascoDLX e3a3927e95 mcd: report error on unhandled cdd command 2021-10-25 09:14:15 +01:00
TascoDLX f04e55f863 mcd: allow continuous cd playback 2021-10-25 09:14:15 +01:00
TascoDLX 08aecccc5a mcd: clean up & fix graphics render 2021-10-25 09:14:15 +01:00
TascoDLX 3471756c06 mcd: reset timer on write 2021-10-25 09:14:15 +01:00
invertego d40e6e1cd3 n64: increase screen canvas to 576 lines
Certain PAL games require this. Affected games include:
- Michael Owen's WLS 2000
- Perfect Dark
- Top Gear Overdrive
2021-10-25 09:13:14 +01:00
encoded-byte 6a262e01ae fc: support for taito tc0690 board 2021-10-25 07:44:40 +01:00
encoded-byte 2f6a51b3ef fc: support for taito tc0190 board 2021-10-25 07:44:40 +01:00
TascoDLX ee3b764b8e ares: bump serializer version for MD changes 2021-10-22 19:16:39 +01:00
TascoDLX 4216d5ae0a md: improved timing/signaling for z80 busreq 2021-10-22 19:16:39 +01:00
TascoDLX 878efe0a05 md: fixed irq issues: timing, delays, ack 2021-10-22 19:16:39 +01:00
Luke Usher 98aee19e93 fc: support expanded bnrom & uxrom
These mappers are used in various homebrew/unlicenced titles
2021-10-21 20:08:23 +01:00
TascoDLX 7810a05b60 md: improved cpu open bus line accuracy 2021-10-21 15:57:15 +01:00
TascoDLX b83e868a6e md: fix z80 bus access & timing issues 2021-10-21 15:57:15 +01:00
TascoDLX ce3153b10e md: prevent sram from masking out high rom 2021-10-21 15:57:15 +01:00
TascoDLX 259642ae2a m68000: fix disassembly of pc-relative addresses 2021-10-21 15:57:15 +01:00
TascoDLX 30921061c7 m68000: restore TAS instruction for cpus other than MD main 2021-10-21 15:57:15 +01:00
TascoDLX 757da94463 m68000: avoid changing interrupt mask on exception 2021-10-21 15:57:15 +01:00
Sintendo 1c026f5edf libco/aarch64: Optimize co_swap_function
Improves thread-switching performance using the following techniques:

- Interleave stores/loads
- Restore the stack pointer and link register as early as possible
2021-10-19 12:15:56 +01:00
Sintendo 2d90b6343d libco/aarch64: Fix vector register handling
The calling convention specifies that d8-d15 (the lower 64 bits of
vector registers q8-q15) are callee-saved. However, libco was
erroneously saving and restoring general-purpose registers x8-x15
instead.
2021-10-19 12:15:56 +01:00
Giovanni Bajo 4c312ab2af Fix linking on old macOS toolchains
Ares JIT recently started to require the data segment to be executable
(see  https://github.com/ares-emulator/ares/pull/200 for details on why
this was done instead of allocating a new segment via mmap).

To do this, it uses mprotect() (bump_allocator::resize), but it also
needs to tell the linker that the data segment can be upgraded to
executable with -Wl,-segprot,__DATA,rwx,rw (nall/GNUmakefile).
Unfortunately, for quite some time, the clang LD had a bug and didn't
accept that option for the data segment.

The official CI has a newer linker without the bug, but for developers
compiling ares with an old toolchain, this small tool will patch the
binary to mark the data segment as potentially executable.
2021-10-19 11:00:23 +01:00
invertego 55d197fdc3 desktop-ui: fix file/folder browser dialog alignment
Align file/folder dialogs to the settings window when appropriate. The
absence of this alignment was triggering asserts in debug builds.
2021-10-19 10:59:51 +01:00
Kawa 53290b9c71 Add screenshot hotkey
As requested in #233
2021-10-17 22:55:49 +01:00
invertego 764a881d36 recompiler: on macOS allow data segment to be executable
Override the data segment's maximum protection from rw to rwx, allowing
mprotect() to be called on recompiler code blocks to make them
executable.
2021-10-17 12:38:14 +01:00
invertego a10e089100 ci: use MSYS2 on Windows, add Windows/Clang build
This change upgrades GCC from the version preinstalled by GitHub Actions
(8.1.0) to the latest MSYS2 version (10.3.0). It also adds a
Windows/Clang build configuration, though the release job still
grabs the GCC build.
2021-10-16 18:59:50 +01:00
TascoDLX 590ec4cb32 Various fixes to CD session handling 2021-10-16 18:59:14 +01:00
TascoDLX 30d143092d Tweak & fix generation of CD subchannel data 2021-10-16 18:59:14 +01:00
TascoDLX 2dbd5a43a0 Improved CD CUEsheet loading to handle PREGAPs & POSTGAPs 2021-10-16 18:59:14 +01:00
Shideravan 08c013dc46 Update README.md 2021-10-15 16:39:36 +01:00
Shideravan 0d169fe641 Adding the ares logo
The resource comes directly from https://ares-emulator.github.io/
This ends the editions in the readme page.
2021-10-15 16:39:36 +01:00
638 changed files with 186529 additions and 8976 deletions

No files matched your search

+34 -10
View File
@@ -11,27 +11,51 @@ jobs:
matrix:
program:
- ares
os:
platform:
- name: windows
version: latest
os: windows-latest
compiler: g++
shell: 'msys2 {0}'
msystem: mingw64
msys-env: x86_64
- name: windows-clang
os: windows-latest
compiler: clang++
shell: 'msys2 {0}'
msystem: clang64
msys-env: clang-x86_64
- name: macos
version: latest
os: macos-latest
compiler: clang++
shell: sh
- name: ubuntu
version: latest
runs-on: ${{ matrix.os.name }}-${{ matrix.os.version }}
os: ubuntu-latest
compiler: g++
shell: sh
name: ${{ matrix.program }}-${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
defaults:
run:
shell: ${{ matrix.platform.shell }}
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
if: matrix.os.name == 'ubuntu'
- name: Install MSYS2 Dependencies
if: matrix.platform.shell == 'msys2 {0}'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.platform.msystem }}
install: make mingw-w64-${{ matrix.platform.msys-env }}-toolchain
- name: Install Linux Dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgtksourceview2.0-dev libgtk2.0-dev libao-dev libopenal-dev
- uses: actions/checkout@v2
- name: Make
run: make -j4 -C desktop-ui build=optimized local=false lto=true
run: make -j4 -C desktop-ui build=optimized local=false lto=true compiler=${{ matrix.platform.compiler }}
- name: Upload
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.program }}-${{ matrix.os.name }}
name: ${{ matrix.program }}-${{ matrix.platform.name }}
path: desktop-ui/out/*
+30
View File
@@ -0,0 +1,30 @@
name: 'Split Subtrees'
on:
push:
branches:
- master
tags:
- '*'
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
jobs:
packages_split:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package:
- nall
- hiro
- ruby
- libco
steps:
- uses: actions/checkout@v2
- if: "!startsWith(github.ref, 'refs/tags/')"
uses: "symplify/[email protected]"
with:
package_directory: '${{ matrix.package }}'
repository_organization: 'ares-emulator'
repository_name: '${{ matrix.package }}'
user_name: "Luke Usher"
user_email: "[email protected]"
+2
View File
@@ -0,0 +1,2 @@
obj/
out/
+6
View File
@@ -0,0 +1,6 @@
all: desktop
desktop:
$(MAKE) -C desktop-ui
.PHONY: all desktop
+133
View File
@@ -1,3 +1,4 @@
----------------------------------------------------------------------
ares
Copyright (c) 2004-2021 ares team, Near et al
@@ -13,3 +14,135 @@ 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.
----------------------------------------------------------------------
----------------------------------------------------------------------
Stack-less Just-In-Time compiler
Copyright Zoltan Herczeg ([email protected]). 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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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 COPYRIGHT
HOLDER(S) 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.
----------------------------------------------------------------------
----------------------------------------------------------------------
MAME
Copyright (c) 1997-2021 MAMEdev and contributors
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.
3. Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT
HOLDER 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.
----------------------------------------------------------------------
----------------------------------------------------------------------
libchdr
Copyright Romain Tisserand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of the <organization> 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 COPYRIGHT HOLDERS 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 <COPYRIGHT HOLDER> 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
----------------------------------------------------------------------
----------------------------------------------------------------------
LZMA SDK is placed in the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute
the original LZMA SDK code, either in source code form or as a compiled binary,
for any purpose, commercial or non-commercial, and by any means.
----------------------------------------------------------------------
----------------------------------------------------------------------
zlib
(C) 1995-2017 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
[email protected] [email protected]
If you use the zlib library in a product, we would appreciate *not* receiving
lengthy legal documents to sign. The sources are provided for free but without
warranty of any kind. The library has been entirely written by Jean-loup
Gailly and Mark Adler; it does not include third-party code.
If you redistribute modified sources, we would appreciate that you include in
the file ChangeLog history information documenting your changes. Please read
the FAQ for more information on the distribution of modified source versions.
----------------------------------------------------------------------
+2 -2
View File
@@ -1,4 +1,4 @@
# ares
![ares logo](https://ares-emulator.github.io/images/logo.png)
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/higan-emu/ares/blob/master/LICENSE)
@@ -9,7 +9,7 @@ Official Releases
-----------------
Official releases are available from
[the ares website](https://ares-emulator.github.io).
[the ares website](https://ares-emu.net).
Nightly Builds
--------------
+4
View File
@@ -27,6 +27,10 @@ ifeq ($(profile),performance)
flags += -DPROFILE_PERFORMANCE
endif
ifneq ($(filter $(cores),a26),)
include $(ares.path)/a26/GNUmakefile
endif
ifneq ($(filter $(cores),fc),)
include $(ares.path)/fc/GNUmakefile
endif
+15
View File
@@ -0,0 +1,15 @@
ares.components += mos6502
ares.objects += ares-a26-system
ares.objects += ares-a26-controller
ares.objects += ares-a26-cartridge
ares.objects += ares-a26-cpu
ares.objects += ares-a26-tia
ares.objects += ares-a26-riot
$(object.path)/ares-a26-system.o: $(ares.path)/a26/system/system.cpp
$(object.path)/ares-a26-controller.o: $(ares.path)/a26/controller/controller.cpp
$(object.path)/ares-a26-cartridge.o: $(ares.path)/a26/cartridge/cartridge.cpp
$(object.path)/ares-a26-cpu.o: $(ares.path)/a26/cpu/cpu.cpp
$(object.path)/ares-a26-tia.o: $(ares.path)/a26/tia/tia.cpp
$(object.path)/ares-a26-riot.o: $(ares.path)/a26/riot/riot.cpp
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <ares/ares.hpp>
#include <component/processor/mos6502/mos6502.hpp>
namespace ares::Atari2600 {
#include <ares/inline.hpp>
auto enumerate() -> vector<string>;
auto load(Node::System& node, string name) -> bool;
struct Region {
static inline auto NTSC() -> bool;
static inline auto PAL() -> bool;
static inline auto SECAM() -> bool;
};
#include <a26/controller/controller.hpp>
#include <a26/system/system.hpp>
#include <a26/cartridge/cartridge.hpp>
#include <a26/cpu/cpu.hpp>
#include <a26/tia/tia.hpp>
#include <a26/riot/riot.hpp>
}
+58
View File
@@ -0,0 +1,58 @@
struct Atari16k : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
Memory::Writable<n8> ram;
n2 bank;
bool hasRam;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
if(address == 0x1ff6) bank = 0;
if(address == 0x1ff7) bank = 1;
if(address == 0x1ff8) bank = 2;
if(address == 0x1ff9) bank = 3;
if(address.bit(12)) {
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
return rom.read((bank * 0x1000) + (address & 0xfff));
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
if(address == 0x1ff6) bank = 0;
if(address == 0x1ff7) bank = 1;
if(address == 0x1ff8) bank = 2;
if(address == 0x1ff9) bank = 3;
if(address >= 0x1000 && address <= 0x107f) {
hasRam = true;
ram.write(address & 0x7f, data);
return true;
}
return false;
}
auto power() -> void override {
bank = 0;
hasRam = 0;
ram.allocate(128);
}
auto serialize(serializer& s) -> void override {
s(bank);
s(hasRam);
s(ram);
}
};
+66
View File
@@ -0,0 +1,66 @@
struct Atari32k : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
Memory::Writable<n8> ram;
n3 bank;
bool hasRam;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
if(address == 0x1ff4) bank = 0;
if(address == 0x1ff5) bank = 1;
if(address == 0x1ff6) bank = 2;
if(address == 0x1ff7) bank = 3;
if(address == 0x1ff8) bank = 4;
if(address == 0x1ff9) bank = 5;
if(address == 0x1ffa) bank = 6;
if(address == 0x1ffb) bank = 7;
if(address.bit(12)) {
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
return rom.read((bank * 0x1000) + (address & 0xfff));
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
if(address == 0x1ff4) bank = 0;
if(address == 0x1ff5) bank = 1;
if(address == 0x1ff6) bank = 2;
if(address == 0x1ff7) bank = 3;
if(address == 0x1ff8) bank = 4;
if(address == 0x1ff9) bank = 5;
if(address == 0x1ffa) bank = 6;
if(address == 0x1ffb) bank = 7;
if(address >= 0x1000 && address <= 0x107f) {
hasRam = true;
ram.write(address & 0x7f, data);
return true;
}
return false;
}
auto power() -> void override {
bank = 0;
hasRam = 0;
ram.allocate(128);
}
auto serialize(serializer& s) -> void override {
s(bank);
s(hasRam);
s(ram);
}
};
+54
View File
@@ -0,0 +1,54 @@
struct Atari8k : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
Memory::Writable<n8> ram;
n1 bank;
bool hasRam;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
if(address == 0x1ff8) bank = 0;
if(address == 0x1ff9) bank = 1;
if(address.bit(12)) {
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
return rom.read((bank * 0x1000) + (address & 0xfff));
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
if(address == 0x1ff8) bank = 0;
if(address == 0x1ff9) bank = 1;
if(address >= 0x1000 && address <= 0x107f) {
hasRam = true;
ram.write(address & 0x7f, data);
return true;
}
return false;
}
auto power() -> void override {
bank = 0;
hasRam = 0;
ram.allocate(128);
}
auto serialize(serializer& s) -> void override {
s(bank);
s(hasRam);
s(ram);
}
};
+37
View File
@@ -0,0 +1,37 @@
namespace Board {
#include "linear.cpp"
#include "atari8k.cpp"
#include "atari16k.cpp"
#include "atari32k.cpp"
#include "commavid.cpp"
#include "parker-bros.cpp"
#include "tigervision.cpp"
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
if(auto fp = pak->read(name)) {
memory.allocate(fp->size());
memory.load(fp);
return true;
}
return false;
}
auto Interface::load(Memory::Writable<n8>& memory, string name) -> bool {
if(auto fp = pak->read(name)) {
memory.allocate(fp->size());
memory.load(fp);
return true;
}
return false;
}
auto Interface::save(Memory::Writable<n8>& memory, string name) -> bool {
if(auto fp = pak->write(name)) {
memory.save(fp);
return true;
}
return false;
}
}
+23
View File
@@ -0,0 +1,23 @@
namespace Board {
struct Interface {
VFS::Pak pak;
Interface(Cartridge& cartridge) : cartridge(cartridge) {}
virtual ~Interface() = default;
virtual auto load() -> void {}
virtual auto save() -> void {}
virtual auto unload() -> void {}
virtual auto read(n16 address) -> n8 { return 0xff; }
virtual auto write(n16 address, n8 data) -> bool { return false; }
virtual auto power() -> void {}
virtual auto serialize(serializer&) -> void {}
auto load(Memory::Readable<n8>&, string name) -> bool;
auto load(Memory::Writable<n8>&, string name) -> bool;
auto save(Memory::Writable<n8>&, string name) -> bool;
Cartridge& cartridge;
};
}
+41
View File
@@ -0,0 +1,41 @@
struct Commavid : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
Memory::Writable<n8> ram;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
if(address.bit(12)) {
if(address >= 0x1000 && address <= 0x13ff) return ram.read(address & 0x3ff);
return rom.read(address & 0xfff);
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
if(address >= 0x1400 && address <= 0x17ff) {
ram.write(address & 0x3ff, data);
return true;
}
return false;
}
auto power() -> void override {
ram.allocate(1_KiB);
}
auto serialize(serializer& s) -> void override {
s(ram);
}
};
+45
View File
@@ -0,0 +1,45 @@
struct Linear : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
Memory::Writable<n8> ram;
bool hasRam;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
if(address.bit(12)) {
if(hasRam && address >= 0x1080 && address <= 0x10ff) return ram.read(address & 0x7f);
return rom.read(address & 0xfff);
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
if(address >= 0x1000 && address <= 0x107f) {
hasRam = true;
ram.write(address & 0x7f, data);
return true;
}
return false;
}
auto power() -> void override {
hasRam = 0;
ram.allocate(128);
}
auto serialize(serializer& s) -> void override {
s(hasRam);
s(ram);
}
};
+51
View File
@@ -0,0 +1,51 @@
struct ParkerBros : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
n3 banks[3];
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
bankswitch(address);
if(address.bit(12)) {
if(address >= 0x1000 && address <= 0x13ff) return rom.read((banks[0] * 0x400) + (address & 0x3ff));
if(address >= 0x1400 && address <= 0x17ff) return rom.read((banks[1] * 0x400) + (address & 0x3ff));
if(address >= 0x1800 && address <= 0x1bff) return rom.read((banks[2] * 0x400) + (address & 0x3ff));
if(address >= 0x1c00 && address <= 0x1fff) return rom.read(( 7 * 0x400) + (address & 0x3ff));
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
bankswitch(address);
return false;
}
auto bankswitch(n16 address) -> void {
if(address >= 0x1fe0 && address <= 0x1fe7) banks[0] = address - 0x1fe0;
if(address >= 0x1fe8 && address <= 0x1fef) banks[1] = address - 0x1fe8;
if(address >= 0x1ff0 && address <= 0x1ff7) banks[2] = address - 0x1ff0;
}
auto power() -> void override {
banks[0] = 0;
banks[1] = 1;
banks[2] = 2;
}
auto serialize(serializer& s) -> void override {
s(banks[0]);
s(banks[1]);
s(banks[2]);
}
};
+42
View File
@@ -0,0 +1,42 @@
struct Tigervision : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
n8 bank;
n8 nextBank;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {
}
auto unload() -> void override {
}
auto read(n16 address) -> n8 override {
if(address.bit(12)) {
if(bank != nextBank) bank = nextBank;
if(address <= 0x17ff) return rom.read((bank * 0x800) + (address & 0x7ff));
return rom.read((rom.size() - 0x800) + (address & 0x7ff));
}
return 0xff;
}
auto write(n16 address, n8 data) -> bool override {
if(address.bit(12) && bank != nextBank) bank = nextBank;
if(address <= 0x3f) nextBank = data;
return false;
}
auto power() -> void override {
bank = 0;
nextBank = 0;
}
auto serialize(serializer& s) -> void override {
s(bank);
s(nextBank);
}
};
+66
View File
@@ -0,0 +1,66 @@
#include <a26/a26.hpp>
namespace ares::Atari2600 {
Cartridge& cartridge = cartridgeSlot.cartridge;
#include "board/board.cpp"
#include "slot.cpp"
#include "serialization.cpp"
auto Cartridge::allocate(Node::Port parent) -> Node::Peripheral {
return node = parent->append<Node::Peripheral>(string{system.name(), " Cartridge"});
}
auto Cartridge::connect() -> void {
if(!node->setPak(pak = platform->pak(node))) return;
information = {};
information.title = pak->attribute("title");
information.region = pak->attribute("region");
information.board = pak->attribute("board");
if(information.board == "Linear") board = new Board::Linear{*this};
if(information.board == "Atari8k") board = new Board::Atari8k{*this};
if(information.board == "Atari16k") board = new Board::Atari16k{*this};
if(information.board == "Atari32k") board = new Board::Atari32k{*this};
if(information.board == "Commavid") board = new Board::Commavid{*this};
if(information.board == "ParkerBros8k") board = new Board::ParkerBros{*this};
if(information.board == "Tigervision") board = new Board::Tigervision{*this};
if(!board) board = new Board::Interface{*this};
board->pak = pak;
board->load();
power();
}
auto Cartridge::disconnect() -> void {
if(!node || !board) return;
board->unload();
board->pak.reset();
board.reset();
node.reset();
}
auto Cartridge::save() -> void {
if(!node) return;
if(board) board->save();
}
auto Cartridge::power() -> void {
if(board) board->power();
}
auto Cartridge::read(n16 address) -> n8 {
if(!node) return 0xff;
if(board) return board->read(address);
return 0xff;
}
auto Cartridge::write(n16 address, n8 data) -> bool {
if(!node) return false;
if(board) return board->write(address, data);
return false;
}
}
+36
View File
@@ -0,0 +1,36 @@
struct Cartridge;
#include "board/board.hpp"
struct Cartridge {
Node::Peripheral node;
VFS::Pak pak;
auto title() const -> string { return information.title; }
auto region() const -> string { return information.region; }
//cartridge.cpp
auto allocate(Node::Port) -> Node::Peripheral;
auto connect() -> void;
auto disconnect() -> void;
auto save() -> void;
auto power() -> void;
auto read(n16 address) -> n8;
auto write(n16 address, n8 data) -> bool;
//serialization.cpp
auto serialize(serializer&) -> void;
unique_pointer<Board::Interface> board;
//private:
struct Information {
string title;
string region;
string board;
} information;
};
#include "slot.hpp"
extern Cartridge& cartridge;
+3
View File
@@ -0,0 +1,3 @@
auto Cartridge::serialize(serializer& s) -> void {
if(board) s(*board);
}
+18
View File
@@ -0,0 +1,18 @@
CartridgeSlot cartridgeSlot{"Cartridge Slot"};
CartridgeSlot::CartridgeSlot(string name) : name(name) {
}
auto CartridgeSlot::load(Node::Object parent) -> void {
port = parent->append<Node::Port>(name);
port->setFamily(system.name());
port->setType("Cartridge");
port->setAllocate([&](auto name) { return cartridge.allocate(port); });
port->setConnect([&] { return cartridge.connect(); });
port->setDisconnect([&] { return cartridge.disconnect(); });
}
auto CartridgeSlot::unload() -> void {
cartridge.disconnect();
port = {};
}
+13
View File
@@ -0,0 +1,13 @@
struct CartridgeSlot {
Node::Port port;
Cartridge cartridge;
//slot.cpp
CartridgeSlot(string name);
auto load(Node::Object) -> void;
auto unload() -> void;
const string name;
};
extern CartridgeSlot cartridgeSlot;
+8
View File
@@ -0,0 +1,8 @@
#include <a26/a26.hpp>
namespace ares::Atari2600 {
#include "port.cpp"
#include "gamepad/gamepad.cpp"
}
+11
View File
@@ -0,0 +1,11 @@
struct Controller {
Node::Peripheral node;
virtual ~Controller() = default;
virtual auto read() -> n8 { return 0xff; }
virtual auto write(n8 data) -> void {}
};
#include "port.hpp"
#include "gamepad/gamepad.hpp"
+37
View File
@@ -0,0 +1,37 @@
Gamepad::Gamepad(Node::Port parent) {
node = parent->append<Node::Peripheral>("Gamepad");
up = node->append<Node::Input::Button>("Up");
down = node->append<Node::Input::Button>("Down");
left = node->append<Node::Input::Button>("Left");
right = node->append<Node::Input::Button>("Right");
fire = node->append<Node::Input::Button>("Fire");
}
auto Gamepad::read() -> n8 {
platform->input(up);
platform->input(down);
platform->input(left);
platform->input(right);
platform->input(fire);
if(!(up->value() & down->value())) {
yHold = 0, upLatch = up->value(), downLatch = down->value();
} else if(!yHold) {
yHold = 1, swap(upLatch, downLatch);
}
if(!(left->value() & right->value())) {
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
} else if(!xHold) {
xHold = 1, swap(leftLatch, rightLatch);
}
n8 data = 0xff;
data.bit(0) = !upLatch;
data.bit(1) = !downLatch;
data.bit(2) = !leftLatch;
data.bit(3) = !rightLatch;
data.bit(4) = !fire->value();
return data;
}
+19
View File
@@ -0,0 +1,19 @@
struct Gamepad : Controller {
Node::Input::Button up;
Node::Input::Button down;
Node::Input::Button left;
Node::Input::Button right;
Node::Input::Button fire;
Gamepad(Node::Port);
auto read() -> n8 override;
private:
b1 yHold;
b1 upLatch;
b1 downLatch;
b1 xHold;
b1 leftLatch;
b1 rightLatch;
};
+28
View File
@@ -0,0 +1,28 @@
ControllerPort controllerPort1{"Controller Port 1"};
ControllerPort controllerPort2{"Controller Port 2"};
ControllerPort::ControllerPort(string name) : name(name) {
}
auto ControllerPort::load(Node::Object parent) -> void {
port = parent->append<Node::Port>(name);
port->setFamily(system.name());
port->setType("Controller");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(name); });
port->setSupported({"Gamepad"});
}
auto ControllerPort::unload() -> void {
device = {};
port = {};
}
auto ControllerPort::allocate(string name) -> Node::Peripheral {
if(name == "Gamepad") device = new Gamepad(port);
if(device) return device->node;
return {};
}
auto ControllerPort::serialize(serializer& s) -> void {
}
+20
View File
@@ -0,0 +1,20 @@
struct ControllerPort {
Node::Port port;
unique_pointer<Controller> device;
//port.cpp
ControllerPort(string name);
auto load(Node::Object) -> void;
auto unload() -> void;
auto allocate(string name) -> Node::Peripheral;
auto read() -> n8 { if(device) return device->read(); return 0xff; }
auto write(n8 data) -> void { if(device) return device->write(data); }
auto serialize(serializer&) -> void;
const string name;
};
extern ControllerPort controllerPort1;
extern ControllerPort controllerPort2;
+42
View File
@@ -0,0 +1,42 @@
#include <a26/a26.hpp>
namespace ares::Atari2600 {
CPU cpu;
#include "memory.cpp"
#include "timing.cpp"
#include "debugger.cpp"
#include "serialization.cpp"
auto CPU::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("CPU");
debugger.load(node);
}
auto CPU::unload() -> void {
debugger = {};
node = {};
}
auto CPU::main() -> void {
debugger.instruction();
instruction();
}
auto CPU::step(u32 clocks) -> void {
Thread::step(clocks);
Thread::synchronize();
}
auto CPU::power(bool reset) -> void {
MOS6502::BCD = 1;
MOS6502::power();
Thread::create(system.frequency() / 3, {&CPU::main, this});
PC.byte(0) = readBus(0xfffc);
PC.byte(1) = readBus(0xfffd);
io = {};
}
}
+48
View File
@@ -0,0 +1,48 @@
struct CPU : MOS6502, Thread {
Node::Object node;
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
auto instruction() -> void;
struct Tracer {
Node::Debugger::Tracer::Instruction instruction;
} tracer;
} debugger;
//cpu.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
auto main() -> void;
auto step(u32 clocks) -> void;
auto power(bool reset) -> void;
//memory.cpp
auto readBus(n16 address) -> n8;
auto writeBus(n16 address, n8 data) -> void;
auto readIO(n16 address) -> n8;
auto writeIO(n16 address, n8 data) -> void;
auto readDebugger(n16 address) -> n8 override;
auto serialize(serializer&) -> void;
//timing.cpp
auto read(n16 address) -> n8 override;
auto write(n16 address, n8 data) -> void override;
auto lastCycle() -> void override;
auto nmi(n16& vector) -> void override;
auto rdyLine(bool) -> void;
//protected:
struct IO {
n1 rdyLine = 1;
} io;
};
extern CPU cpu;
+12
View File
@@ -0,0 +1,12 @@
auto CPU::Debugger::load(Node::Object parent) -> void {
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "CPU");
tracer.instruction->setAddressBits(16);
}
auto CPU::Debugger::instruction() -> void {
if(tracer.instruction->enabled() && tracer.instruction->address(cpu.PC)) {
tracer.instruction->notify(cpu.disassembleInstruction(), cpu.disassembleContext());
}
}
+40
View File
@@ -0,0 +1,40 @@
inline auto CPU::readBus(n16 address) -> n8 {
address &= 0x1fff;
n8 data = cartridge.read(address);
if(address.bit(12) == 0 && address.bit(7) == 0) {
return tia.read(address & 0xf);
}
if(address.bit(12) == 0 && address.bit(9) == 0 && address.bit(7) == 1) {
return riot.readRam(address & 0x7f);
}
if(address.bit(12) == 0 && address.bit(9) == 1 && address.bit(7) == 1) {
return riot.readIo((address - 0x80) & 0x1f);
}
return data;
}
inline auto CPU::writeBus(n16 address, n8 data) -> void {
address &= 0x1fff;
cartridge.write(address, data);
if(address.bit(12) == 0 && address.bit(7) == 0) {
return tia.write(address & 0x3f, data);
}
if(address.bit(12) == 0 && address.bit(9) == 0 && address.bit(7) == 1) {
return riot.writeRam(address & 0x7f, data);
}
if(address.bit(12) == 0 && address.bit(9) == 1 && address.bit(7) == 1) {
return riot.writeIo((address - 0x80) & 0x1f, data);
}
}
auto CPU::readDebugger(n16 address) -> n8 {
return readBus(address);
}
+5
View File
@@ -0,0 +1,5 @@
auto CPU::serialize(serializer& s) -> void {
MOS6502::serialize(s);
Thread::serialize(s);
s(io.rdyLine);
}
+30
View File
@@ -0,0 +1,30 @@
auto CPU::read(n16 address) -> n8 {
if(io.rdyLine == 0) {
while (io.rdyLine == 0) {
step(1);
}
MDR = readBus(address);
return MDR;
}
step(1);
MDR = readBus(address);
return MDR;
}
auto CPU::write(n16 address, n8 data) -> void {
step(1);
writeBus(address, MDR = data);
}
auto CPU::lastCycle() -> void {
}
auto CPU::nmi(n16& vector) -> void {
}
auto CPU::rdyLine(bool line) -> void {
io.rdyLine = line;
}
+87
View File
@@ -0,0 +1,87 @@
auto RIOT::readRam(n8 address) -> n8 {
return ram[address];
}
auto RIOT::writeRam(n8 address, n8 data) -> void {
ram[address] = data;
}
auto RIOT::readIo(n8 address) -> n8 {
switch(address) {
case 0x00: return readPortA();
case 0x01: return port[0].direction;
case 0x02: return readPortB();
case 0x03: return port[1].direction;
case 0x04: return timer.counter;
}
debug(unimplemented, "[RIOT] IO read ", hex(address));
return 0xff;
}
auto RIOT::writeIo(n8 address, n8 data) -> void {
switch(address) {
case 0x00: writePortA(data); return;
case 0x01: port[0].direction = data; return;
case 0x02: writePortB(data); return;
case 0x03: port[1].direction = data; return;
case 0x14: case 0x15: case 0x16: case 0x17: {
const n16 intervals[] = {1, 8, 64, 1024};
timer.counter = data;
timer.interval = intervals[address - 0x14];
timer.reload = timer.interval;
return; }
}
debug(unimplemented, "[RIOT] IO write ",hex(address), " = ", hex(data));
}
auto RIOT::readPortA() -> n8 {
n8 data;
// TODO: Handle when configured as outputs
data.bit(0, 3) = controllerPort2.read().bit(0, 4);
data.bit(4, 7) = controllerPort1.read().bit(0, 4);
return data;
}
auto RIOT::writePortA(n8 data) -> void {
port[0].data = data;
}
auto RIOT::readPortB() -> n8 {
system.controls.poll();
n8 data;
for(auto n : range(8)) if(port[1].direction.bit(n)) { data.bit(n) = port[1].data.bit(n); }
// Atari hard-wires these bits as input
data.bit(0) = !system.controls.reset->value();
data.bit(1) = !system.controls.select->value();
// These are toggle switches; flip the values on the rising edge
if(system.controls.tvType->value() && !tvTypeLatch) {
tvType = !tvType;
}
tvTypeLatch = system.controls.tvType->value();
if(system.controls.leftDifficulty->value() && !leftDifficultyLatch) {
leftDifficulty = !leftDifficulty;
}
leftDifficultyLatch = system.controls.leftDifficulty->value();
if(system.controls.rightDifficulty->value() && !rightDifficultyLatch) {
rightDifficulty = !rightDifficulty;
}
rightDifficultyLatch = system.controls.rightDifficulty->value();
data.bit(3) = tvType;
data.bit(6) = leftDifficulty;
data.bit(7) = rightDifficulty;
return data;
}
auto RIOT::writePortB(n8 data) -> void {
port[1].data = data;
}
+51
View File
@@ -0,0 +1,51 @@
#include <a26/a26.hpp>
namespace ares::Atari2600 {
RIOT riot;
#include "io.cpp"
#include "serialization.cpp"
auto RIOT::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("RIOT");
ram.allocate(128);
}
auto RIOT::unload() -> void {
ram.reset();
node.reset();
}
auto RIOT::main() -> void {
decrementTimer();
step(1);
}
auto RIOT::decrementTimer() -> void {
if(--timer.interval == 0) {
if(--timer.counter == 0xff) {
timer.reload = 1;
}
timer.interval = timer.reload;
}
}
auto RIOT::step(u32 clocks) -> void {
Thread::step(clocks);
Thread::synchronize();
}
auto RIOT::power(bool reset) -> void {
Thread::create(system.frequency() / 3, {&RIOT::main, this});
timer = {};
port[0] = {.data = 0xff};
port[1] = {.data = 0xff};
leftDifficulty = 1;
rightDifficulty = 1;
tvType = 1;
}
}
+48
View File
@@ -0,0 +1,48 @@
struct RIOT : Thread {
Node::Object node;
Memory::Writable<n8> ram;
//riot.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
auto main() -> void;
auto step(u32 clocks) -> void;
auto power(bool reset) -> void;
auto decrementTimer() -> void;
//io.cpp
auto readRam(n8 address) -> n8;
auto writeRam(n8 address, n8 data) -> void;
auto readIo(n8 address) -> n8;
auto writeIo(n8 address, n8 data) -> void;
auto readPortA() -> n8;
auto readPortB() -> n8;
auto writePortA(n8 data) -> void;
auto writePortB(n8 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
struct {
u8 counter;
n16 interval;
n16 reload;
} timer;
struct Port {
n8 data;
n8 direction;
} port[2];
n1 leftDifficulty;
n1 leftDifficultyLatch;
n1 rightDifficulty;
n1 rightDifficultyLatch;
n1 tvType;
n1 tvTypeLatch;
};
extern RIOT riot;
+21
View File
@@ -0,0 +1,21 @@
//serialization.cpp
auto RIOT::serialize(serializer& s) -> void {
Thread::serialize(s);
s(ram);
s(timer.counter);
s(timer.interval);
s(timer.reload);
for(auto n : range(2)) {
s(port[n].data);
s(port[n].direction);
}
s(leftDifficulty);
s(leftDifficultyLatch);
s(rightDifficulty);
s(rightDifficultyLatch);
s(tvType);
s(tvTypeLatch);
}
+17
View File
@@ -0,0 +1,17 @@
auto System::Controls::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("Controls");
reset = node->append<Node::Input::Button>("Reset");
select = node->append<Node::Input::Button>("Select");
leftDifficulty = node->append<Node::Input::Button>("Left Difficulty");
rightDifficulty = node->append<Node::Input::Button>("Right Difficulty");
tvType = node->append<Node::Input::Button>("TV Type");
}
auto System::Controls::poll() -> void {
platform->input(reset);
platform->input(select);
platform->input(leftDifficulty);
platform->input(rightDifficulty);
platform->input(tvType);
}
+46
View File
@@ -0,0 +1,46 @@
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
serializer s;
u32 signature = SerializerSignature;
char version[16] = {};
char description[512] = {};
memory::copy(&version, (const char*)SerializerVersion, SerializerVersion.size());
s(signature);
s(synchronize);
s(version);
s(description);
serialize(s, synchronize);
return s;
}
auto System::unserialize(serializer& s) -> bool {
u32 signature = 0;
bool synchronize = true;
char version[16] = {};
char description[512] = {};
s(signature);
s(synchronize);
s(version);
s(description);
if(signature != SerializerSignature) return false;
if(string{version} != SerializerVersion) return false;
if(synchronize) power(/* reset = */ false);
serialize(s, synchronize);
return true;
}
auto System::serialize(serializer& s, bool synchronize) -> void {
scheduler.setSynchronize(synchronize);
s(cartridge);
s(cpu);
s(tia);
s(riot);
s(controllerPort1);
s(controllerPort2);
}
+105
View File
@@ -0,0 +1,105 @@
#include <a26/a26.hpp>
namespace ares::Atari2600 {
auto enumerate() -> vector<string> {
return {
"[Atari] Atari 2600 (NTSC)",
"[Atari] Atari 2600 (PAL)",
"[Atari] Atari 2600 (SECAM)",
};
}
auto load(Node::System& node, string name) -> bool {
if(!enumerate().find(name)) return false;
return system.load(node, name);
}
Random random;
Scheduler scheduler;
System system;
#include "controls.cpp"
#include "serialization.cpp"
auto System::game() -> string {
if(cartridge.node) {
return cartridge.title();
}
return "(no cartridge connected)";
}
auto System::run() -> void {
scheduler.enter();
}
auto System::load(Node::System& root, string name) -> bool {
if(node) unload();
information = {};
if(name.find("NTSC")) {
information.name = "Atari 2600";
information.region = Region::NTSC;
information.frequency = 3579546;
}
if(name.find("PAL")) {
information.name = "Atari 2600";
information.region = Region::PAL;
information.frequency = 3546894;
}
if(name.find("SECAM")) {
information.name = "Atari 2600";
information.region = Region::SECAM;
information.frequency = 3546894;
}
node = Node::System::create(information.name);
node->setGame({&System::game, this});
node->setRun({&System::run, this});
node->setPower({&System::power, this});
node->setSave({&System::save, this});
node->setUnload({&System::unload, this});
node->setSerialize({&System::serialize, this});
node->setUnserialize({&System::unserialize, this});
root = node;
scheduler.reset();
controls.load(node);
riot.load(node);
cpu.load(node);
tia.load(node);
cartridgeSlot.load(node);
controllerPort1.load(node);
controllerPort2.load(node);
return true;
}
auto System::save() -> void {
if(!node) return;
cartridge.save();
}
auto System::unload() -> void {
if(!node) return;
save();
riot.unload();
cpu.unload();
tia.unload();
cartridgeSlot.unload();
controllerPort1.unload();
controllerPort2.unload();
node = {};
}
auto System::power(bool reset) -> void {
for(auto& setting : node->find<Node::Setting::Setting>()) setting->setLatch();
random.entropy(Random::Entropy::Low);
cartridge.power();
riot.power(reset);
cpu.power(reset);
tia.power(reset);
scheduler.power(cpu);
}
}
+53
View File
@@ -0,0 +1,53 @@
extern Random random;
struct System {
Node::System node;
struct Controls {
Node::Object node;
Node::Input::Button reset;
Node::Input::Button select;
Node::Input::Button leftDifficulty;
Node::Input::Button rightDifficulty;
Node::Input::Button tvType;
//controls.cpp
auto load(Node::Object) -> void;
auto poll() -> void;
} controls;
enum class Region : u32 { NTSC, PAL, SECAM };
auto name() const -> string { return information.name; }
auto region() const -> Region { return information.region; }
auto frequency() const -> u32 { return information.frequency; }
//system.cpp
auto game() -> string;
auto run() -> void;
auto load(Node::System& node, string name) -> bool;
auto save() -> void;
auto unload() -> void;
auto power(bool reset) -> void;
//serialization.cpp
auto serialize(bool synchronize) -> serializer;
auto unserialize(serializer&) -> bool;
private:
struct Information {
string name = "Atari 2600";
Region region = Region::NTSC;
u32 frequency = Constants::Colorburst::NTSC;
} information;
//serialization.cpp
auto serialize(serializer&, bool synchronize) -> void;
};
extern System system;
auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; }
auto Region::PAL() -> bool { return system.region() == System::Region::PAL; }
auto Region::SECAM() -> bool { return system.region() == System::Region::SECAM; }
+64
View File
@@ -0,0 +1,64 @@
auto TIA::runAudio() -> void {
if(io.hcounter == 9 || io.hcounter == 81) {
audio[0].phase0();
audio[1].phase0();
}
if(io.hcounter == 37 || io.hcounter == 149) {
auto a = audio[0].phase1();
auto b = audio[1].phase1();
double output = 0;
output += volume[a];
output += volume[b];
stream->frame(output / 2.0);
}
}
auto TIA::AudioChannel::phase0() -> void {
if (enable) {
switch (control.bit(0, 1)) {
case 0x00: case 0x01: pulseCounterPaused = 0; break;
case 0x02: pulseCounterPaused = noiseCounter.bit(1, 4) != 1; break;
case 0x03: pulseCounterPaused = !noiseCounter.bit(0); break;
}
noiseFeedback = (noiseCounter.bit(2) ^ noiseCounter.bit(0)) || noiseCounter == 0;
if(control.bit(0, 2) == 0) {
noiseFeedback = !(control.bit(2, 3)) ||
!(noiseCounter || (pulseCounter != 0x0a)) ||
(pulseCounter.bit(0) ^ noiseCounter.bit(0));
}
}
enable = divCounter == frequency;
if (divCounter == frequency || divCounter == 0x1f) {
divCounter = 0;
return;
}
divCounter++;
}
auto TIA::AudioChannel::phase1() -> u8 {
if(!enable) return pulseCounter.bit(0) * volume;
pulseFeedback = 0;
switch(control.bit(2, 3)) {
case 0x00: pulseFeedback = pulseCounter != 0x0a && control.bit(0, 2) && (pulseCounter.bit(1) ^ pulseCounter.bit(0)); break;
case 0x01: pulseFeedback = !pulseCounter.bit(3); ; break;
case 0x02: pulseFeedback = !noiseCounter.bit(0); ; break;
case 0x03: pulseFeedback = !(pulseCounter.bit(1) || !pulseCounter.bit(1, 3)); ; break;
}
noiseCounter >>= 1;
if (noiseFeedback) noiseCounter.bit(4) = 1;
if(!pulseCounterPaused) {
pulseCounter = ~(pulseCounter >> 1) & 7;
if (pulseFeedback) pulseCounter.bit(3) = 1;
}
return pulseCounter.bit(0) * volume;
}
+42
View File
@@ -0,0 +1,42 @@
auto TIA::color(n32 color) -> n64 {
u64 ntscColors[128] = {
0x0000'0000'0000ull, 0x4040'4040'4040ull, 0x6c6c'6c6c'6c6cull, 0x9090'9090'9090ull, 0xb0b0'b0b0'b0b0ull, 0xc8c8'c8c8'c8c8ull, 0xdcdc'dcdc'dcdcull, 0xecec'ecec'ececull,
0x4444'4444'0000ull, 0x6464'6464'1010ull, 0x8484'8484'2424ull, 0xa0a0'a0a0'3434ull, 0xb8b8'b8b8'4040ull, 0xd0d0'd0d0'5050ull, 0xe8e8'e8e8'5c5cull, 0xfcfc'fcfc'6868ull,
0x7070'2828'0000ull, 0x8484'4444'1414ull, 0x9898'5c5c'2828ull, 0xacac'7878'3c4cull, 0xbcbc'8c8c'4c4cull, 0xcccc'a0a0'5c5cull, 0xdcdc'b4b4'6868ull, 0xecec'c8c8'7878ull,
0x8484'1818'0000ull, 0x9898'3434'1818ull, 0xacac'5050'3030ull, 0xc0c0'6868'4848ull, 0xd0d0'8080'5c5cull, 0xe0e0'9494'7070ull, 0xecec'a8a8'8080ull, 0xfcfc'bcbc'9494ull,
0x8888'0000'0000ull, 0x9c9c'2020'2020ull, 0xb0b0'3c3c'3c3cull, 0xc0c0'5858'5858ull, 0xd0d0'7070'7070ull, 0xe0e0'8888'8888ull, 0xecec'a0a0'a0a0ull, 0xfcfc'b4b4'b4b4ull,
0x7878'0000'5c5cull, 0x8c8c'2020'7474ull, 0xa0a0'3c3c'8888ull, 0xb0b0'5858'9c9cull, 0xc0c0'7070'b0b0ull, 0xd0d0'8484'c0c0ull, 0xdcdc'9c9c'd0d0ull, 0xecec'b0b0'e0e0ull,
0x4848'0000'7878ull, 0x6060'2020'9090ull, 0x7878'3c3c'a4a4ull, 0x8c8c'5858'b8b8ull, 0xa0a0'7070'ccccull, 0xb4b4'8484'dcdcull, 0xc4c4'9c9c'ececull, 0xd4d4'b0b0'fcfcull,
0x1414'0000'8484ull, 0x3030'2020'9898ull, 0x4c4c'3c3c'acacull, 0x6868'5858'c0c0ull, 0x7c7c'7070'd0d0ull, 0x9494'8888'e0e0ull, 0xa8a8'a0a0'ececull, 0xbcbc'b4b4'fcfcull,
0x0000'0000'8888ull, 0x1c1c'2020'9c9cull, 0x3838'4040'b0b0ull, 0x5050'5c5c'c0c0ull, 0x6868'7474'd0d0ull, 0x7c7c'8c8c'e0e0ull, 0x9090'a4a4'ececull, 0xa4a4'b8b8'fcfcull,
0x0000'1818'7c7cull, 0x1c1c'3838'9090ull, 0x3838'5454'a8a8ull, 0x5050'7070'bcbcull, 0x6868'8888'ccccull, 0x7c7c'9c9c'dcdcull, 0x9090'b4b4'ececull, 0xa4a4'c8c8'fcfcull,
0x0000'2c2c'5c5cull, 0x1c1c'4c4c'7878ull, 0x3838'6868'9090ull, 0x5050'8484'acacull, 0x6868'9c9c'c0c0ull, 0x7c7c'b4b4'd4d4ull, 0x9090'cccc'e8e8ull, 0xa4a4'e0e0'fcfcull,
0x0000'3c3c'2c2cull, 0x1c1c'5c5c'4848ull, 0x3838'7c7c'6464ull, 0x5050'9c9c'8080ull, 0x6868'b4b4'9494ull, 0x7c7c'd0d0'acacull, 0x9090'e4e4'c0c0ull, 0xa4a4'fcfc'd4d4ull,
0x0000'3c3c'0000ull, 0x2020'5c5c'2020ull, 0x4040'7c7c'4040ull, 0x5c5c'9c9c'5c5cull, 0x7474'b4b4'7474ull, 0x8c8c'd0d0'8c8cull, 0xa4a4'e4e4'a4a4ull, 0xb8b8'fcfc'b8b8ull,
0x1414'3838'0000ull, 0x3434'5c5c'1c1cull, 0x5050'7c7c'3838ull, 0x6c6c'9898'5050ull, 0x8484'b4b4'6868ull, 0x9c9c'cccc'7c7cull, 0xb4b4'e4e4'9090ull, 0xc8c8'fcfc'a4a4ull,
0x2c2c'3030'0000ull, 0x4c4c'5050'1c1cull, 0x6868'7070'3434ull, 0x8484'8c8c'4c4cull, 0x9c9c'a8a8'6464ull, 0xb4b4'c0c0'7878ull, 0xcccc'd4d4'8888ull, 0xe0e0'ecec'9c9cull,
0x4444'2828'0000ull, 0x6464'4848'1818ull, 0x8484'6868'3030ull, 0xa0a0'8484'4444ull, 0xb8b8'9c9c'5858ull, 0xd0d0'b4b4'6c6cull, 0x8e8c'cccc'7c7cull, 0xfcfc'c0c0'8c8cull,
};
u64 palColors[128] = {
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
0x8080'5858'0000ull, 0x9494'7070'2020ull, 0xa8a8'8484'3c3cull, 0xbcbc'9c9c'5858ull, 0xcccc'acac'7070ull, 0xdcdc'c0c0'8484ull, 0xecec'd0d0'9c9cull, 0xfcfc'e0e0'b0b0ull,
0x4444'5c5c'0000ull, 0x5c5c'7878'2020ull, 0x7474'9090'3c3cull, 0x8c8c'acac'5858ull, 0xa0a0'c0c0'7070ull, 0xb0b0'd4d4'8484ull, 0xc4c4'e8e8'9c9cull, 0xd4d4'fcfc'b0b0ull,
0x7070'3434'0000ull, 0x8888'5050'2020ull, 0xa0a0'6868'3c3cull, 0xb4b4'8484'5858ull, 0xc8c8'9898'7070ull, 0xdcdc'acac'8484ull, 0xecec'c0c0'9c9cull, 0xfcfc'd4d4'b0b0ull,
0x0000'6464'1414ull, 0x2020'8080'3434ull, 0x3c3c'9898'5050ull, 0x5858'b0b0'6c6cull, 0x7070'c4c4'8484ull, 0x8484'd8d8'9c9cull, 0x9c9c'e8e8'b4b4ull, 0xb0b0'fcfc'c8c8ull,
0x7070'0000'1414ull, 0x8888'2020'3434ull, 0xa0a0'3c3c'5050ull, 0xb4b4'5858'6c6cull, 0xc8c8'7070'8484ull, 0xdcdc'8484'9c9cull, 0xecec'9c9c'b4b4ull, 0xfcfc'b0b0'c8c8ull,
0x0000'5c5c'5c5cull, 0x2020'7474'7474ull, 0x3c3c'8c8c'8c8cull, 0x5858'a4a4'a4a4ull, 0x7070'b8b8'b8b8ull, 0x8484'c8c8'c8c8ull, 0x9c9c'dcdc'dcdcull, 0xb0b0'ecec'ececull,
0x7070'0000'5c5cull, 0x8484'2020'7474ull, 0x9494'3c3c'8888ull, 0xa8a8'5858'9c9cull, 0xb4b4'7070'b0b0ull, 0xc4c4'8484'c0c0ull, 0xd0d0'9c9c'd0d0ull, 0xe0e0'b0b0'e0e0ull,
0x0000'3c3c'7070ull, 0x1c1c'5858'8888ull, 0x3838'7474'a0a0ull, 0x5050'8c8c'b4b4ull, 0x6868'a4a4'c8c8ull, 0x7c7c'b8b8'dcdcull, 0x9090'cccc'ececull, 0xa4a4'e0e0'fcfcull,
0x5858'0000'7070ull, 0x6c6c'2020'8888ull, 0x8080'3c3c'a0a0ull, 0x9494'5858'b4b4ull, 0xa4a4'7070'c8c8ull, 0xb4b4'8484'dcdcull, 0xc4c4'9c9c'ececull, 0xd4d4'b0b0'fcfcull,
0x0000'2020'7070ull, 0x1c1c'3c3c'8888ull, 0x3838'5858'a0a0ull, 0x5050'7474'b4b4ull, 0x6868'8888'c8c8ull, 0x7c7c'a0a0'dcdcull, 0x9090'b4b4'ececull, 0xa4a4'c8c8'fcfcull,
0x3c3c'0000'8080ull, 0x5454'2020'9494ull, 0x6c6c'3c3c'a8a8ull, 0x8080'5858'bcbcull, 0x9494'7070'ccccull, 0xa8a8'8484'dcdcull, 0xb8b8'9c9c'ececull, 0xc8c8'b0b0'fcfcull,
0x0000'0000'8888ull, 0x2020'2020'9c9cull, 0x3c3c'3c3c'b0b0ull, 0x5858'5858'c0c0ull, 0x7070'7070'd0d0ull, 0x8484'8484'e0e0ull, 0x9c9c'9c9c'ececull, 0xb0b0'b0b0'fcfcull,
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
0x0000'0000'0000ull, 0x2828'2828'2828ull, 0x5050'5050'5050ull, 0x7474'7474'7474ull, 0x9494'9494'9494ull, 0xb4b4'b4b4'b4b4ull, 0xd0d0'd0d0'd0d0ull, 0xecec'ecec'ececull,
};
return Region::PAL() ? palColors[color & 0x7f] : ntscColors[color & 0x7f];
}
+147
View File
@@ -0,0 +1,147 @@
auto TIA::read(n8 address) -> n8 {
n8 value;
switch(address) {
case 0x00: value.bit(7) = collision.M0P1; value.bit(6) = collision.M0P0; return value; // CXM0P
case 0x01: value.bit(7) = collision.M1P0; value.bit(6) = collision.M1P1; return value; // CXM1P
case 0x02: value.bit(7) = collision.P0PF; value.bit(6) = collision.P0BL; return value; // CX0FB
case 0x03: value.bit(7) = collision.P1PF; value.bit(6) = collision.P1BL; return value; // CX1FB
case 0x04: value.bit(7) = collision.M0PF; value.bit(6) = collision.M0BL; return value; // CXM0FB
case 0x05: value.bit(7) = collision.M1PF; value.bit(6) = collision.M1BL; return value; // CXM1FB
case 0x06: value.bit(7) = collision.BLPF; return value; // CXBLPF
case 0x07: value.bit(7) = collision.P0P1; value.bit(6) = collision.M0M1; return value; // CXPPMM
case 0x0c: value.bit(7) = controllerPort1.read().bit(4); return value; // INPT4
case 0x0d: value.bit(7) = controllerPort2.read().bit(4); return value; // INPT5
}
debug(unimplemented, "[TIA] read ", hex(address));
return 0xff;
}
auto TIA::write(n8 address, n8 data) -> void {
switch(address) {
case 0x00: vsync(data.bit(1)); return; // VSYNC
case 0x01: writeQueue.add(address, data, 1); return; // VBLANK
case 0x02: cpu.rdyLine(0); return; // WSYNC
case 0x03: io.hcounter = 0; return; // RSYNC
case 0x04: player[0].size = data.bit(0,2); missile[0].size = data.bit(4, 5); return; // NUSIZ1
case 0x05: player[1].size = data.bit(0,2); missile[1].size = data.bit(4, 5); return; // NUSIZ1
case 0x06: io.p0Color = data.bit(1, 7); return; // COLUP0
case 0x07: io.p1Color = data.bit(1, 7); return; // COLUP1
case 0x08: io.fgColor = data.bit(1, 7); return; // COLUPF
case 0x09: io.bgColor = data.bit(1, 7); return; // COLUBK
case 0x0a: ctrlpf(data); return; // CTRLPF
case 0x0b: writeQueue.add(address, data, 1); return; // REFP0
case 0x0c: writeQueue.add(address, data, 1); return; // REFP1
case 0x0d: writeQueue.add(address, data, 6); return; // PF0
case 0x0e: writeQueue.add(address, data, 6); return; // PF1
case 0x0f: writeQueue.add(address, data, 6); return; // PF2
case 0x10: resp(0); return; // RESP0
case 0x11: resp(1); return; // RESP1
case 0x12: resm(0); return; // RESM0
case 0x13: resm(1); return; // RESM1
case 0x14: resbl(); return; // RESBL
case 0x15: audio[0].control = data; return; // AUDC0
case 0x16: audio[1].control = data; return; // AUDC1
case 0x17: audio[0].frequency = data; return; // AUDF0
case 0x18: audio[1].frequency = data; return; // AUDF1
case 0x19: audio[0].volume = data; return; // AUDV0
case 0x1a: audio[1].volume = data; return; // AUDV1
case 0x1b: writeQueue.add(address, data, 3); return; // GRP0
case 0x1c: writeQueue.add(address, data, 3); return; // GRP1
case 0x1d: writeQueue.add(address, data, 1); return; // ENAM0
case 0x1e: writeQueue.add(address, data, 1); return; // ENAM1
case 0x1f: writeQueue.add(address, data, 1); return; // ENAB1
case 0x20: writeQueue.add(address, data, 2); return; // HMP0
case 0x21: writeQueue.add(address, data, 2); return; // HMP1
case 0x22: writeQueue.add(address, data, 2); return; // HMM0
case 0x23: writeQueue.add(address, data, 2); return; // HMM1
case 0x24: writeQueue.add(address, data, 2); return; // HMBL
case 0x25: player[0].delay = data.bit(0); return; // VDELP0
case 0x26: player[1].delay = data.bit(0); return; // VDELP1
case 0x27: ball.delay = data.bit(0); return; // VDELBL
case 0x28: resmp(0, data); return; // RESMP0
case 0x29: resmp(1, data); return; // RESMP1
case 0x2a: writeQueue.add(address, data, 6); return; // HMOVE
case 0x2b: writeQueue.add(address, data, 2); return; // HMCLR
case 0x2c: cxclr(); return; // CXCLR
}
debug(unimplemented, "[TIA] write ",hex(address), " = ", hex(data));
}
auto TIA::vsync(n1 state) -> void {
if(io.vsync && !state) {
io.vcounter = 0;
frame();
}
io.vsync = state;
}
auto TIA::ctrlpf(n8 data) -> void {
playfield.mirror = data.bit(0);
playfield.scoreMode = data.bit(1);
playfield.priority = data.bit(2);
ball.size = data.bit(4, 5);
}
auto TIA::cxclr() -> void {
collision.M0P0 = 0;
collision.M0P1 = 0;
collision.M1P0 = 0;
collision.M1P1 = 0;
collision.P0PF = 0;
collision.P0BL = 0;
collision.P1PF = 0;
collision.P1BL = 0;
collision.M0PF = 0;
collision.M0BL = 0;
collision.M1PF = 0;
collision.M1BL = 0;
collision.BLPF = 0;
collision.P0P1 = 0;
collision.M0M1 = 0;
}
auto TIA::hmove() -> void {
for(auto& p : player) p.position = (p.position - p.offset + 160) % 160;
for(auto& m : missile) m.position = (m.position - m.offset + 160) % 160;
ball.position = (ball.position - ball.offset + 160) % 160;
io.hmoveTriggered = io.vcounter;
}
auto TIA::hmclr() -> void {
for(auto& p : player) p.offset = 0;
for(auto& m : missile) m.offset = 0;
ball.offset = 0;
}
auto TIA::grp(n1 index, n8 data) -> void {
player[index].graphics[0] = data;
player[!index].graphics[1] = player[!index].graphics[0];
if(index) ball.enable[1] = ball.enable[0];
}
auto TIA::resp(n1 index) -> void {
player[index].position = (io.hcounter < 68 ? 3 : io.hcounter - 68 + 8) % 160;
}
auto TIA::resm(n1 index) -> void {
missile[index].position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7) % 160;
}
auto TIA::resmp(n1 index, n8 data) -> void {
missile[index].reset = data.bit(1);
if(missile[index].reset) {
auto offset = 3;
if(player[index].size == 5) offset = 6;
if(player[index].size == 7) offset = 10;
missile[index].position = player[index].position + offset;
}
}
auto TIA::resbl() -> void {
ball.position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7);
}
+85
View File
@@ -0,0 +1,85 @@
//serialization.cpp
auto TIA::serialize(serializer& s) -> void {
Thread::serialize(s);
for(auto n : range(writeQueue.maxItems)) {
s(writeQueue.items[n].active);
s(writeQueue.items[n].address);
s(writeQueue.items[n].data);
s(writeQueue.items[n].delay);
}
s(io.vcounter);
s(io.hcounter);
s(io.hmoveTriggered);
s(io.vsync);
s(io.vblank);
s(io.bgColor);
s(io.p0Color);
s(io.p1Color);
s(io.fgColor);
s(playfield.graphics);
s(playfield.mirror);
s(playfield.scoreMode);
s(playfield.priority);
for(auto n : range(2)) {
s(player[n].graphics[0]);
s(player[n].graphics[1]);
s(player[n].reflect);
s(player[n].size);
s(player[n].offset);
s(player[n].position);
s(player[n].delay);
}
for(auto n : range(2)) {
s(missile[n].enable);
s(missile[n].reset);
s(missile[n].size);
s(missile[n].offset);
s(missile[n].position);
}
for(auto n : range(2)) {
s(ball.enable[0]);
s(ball.enable[1]);
s(ball.size);
s(ball.offset);
s(ball.position);
}
s(collision.M0P0);
s(collision.M0P1);
s(collision.M1P0);
s(collision.M1P1);
s(collision.P0PF);
s(collision.P0BL);
s(collision.P1PF);
s(collision.P1BL);
s(collision.M0PF);
s(collision.M0BL);
s(collision.M1PF);
s(collision.M1BL);
s(collision.BLPF);
s(collision.P0P1);
s(collision.M0M1);
for(auto n : range(26)) {
s(volume[n]);
}
for(auto n : range(2)) {
s(audio[n].enable);
s(audio[n].divCounter);
s(audio[n].noiseCounter);
s(audio[n].noiseFeedback);
s(audio[n].pulseCounter);
s(audio[n].pulseCounterPaused);
s(audio[n].pulseFeedback);
s(audio[n].volume);
s(audio[n].control);
s(audio[n].frequency);
}
}
+173
View File
@@ -0,0 +1,173 @@
#include <a26/a26.hpp>
namespace ares::Atari2600 {
TIA tia;
#include "audio.cpp"
#include "color.cpp"
#include "io.cpp"
#include "serialization.cpp"
#include "write-queue.cpp"
auto TIA::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("TIA");
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
screen->colors(1 << 7, {&TIA::color, this});
screen->setSize(160, vlines());
screen->setScale(2.0, 1.0);
screen->setAspect(1.0, 1.0);
screen->setViewport(0, 0, 160, vlines());
stream = node->append<Node::Audio::Stream>("Audio");
stream->setChannels(1);
stream->setFrequency(31'403);
stream->addHighPassFilter( 20.0, 1);
stream->addLowPassFilter (2840.0, 1);
}
auto TIA::unload() -> void {
screen->quit();
node->remove(screen);
screen.reset();
node.reset();
}
auto TIA::main() -> void {
scanline();
io.vcounter++;
io.hmoveTriggered = 255;
}
auto TIA::scanline() -> void {
cpu.rdyLine(1);
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
auto x = io.hcounter - 68;
if(x >= 0) pixel(x);
writeQueue.step();
runAudio();
step();
}
}
auto TIA::pixel(n8 x) -> void {
auto output = screen->pixels().data() + (io.vcounter * 160);
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
output[x] = 0;
return;
}
output[x] = io.bgColor;
auto pf = runPlayfield(x);
auto bl = runBall(x);
auto p0 = runPlayer(x, 0);
auto p1 = runPlayer(x, 1);
auto m0 = runMissile(x, 0);
auto m1 = runMissile(x, 1);
if(!playfield.priority && (pf || bl)) output[x] = io.fgColor;
if(p1 || m1) output[x] = io.p1Color;
if(p0 || m0) output[x] = io.p0Color;
if(playfield.priority && (pf || bl)) output[x] = io.fgColor;
// Update Collision
if(m0 && p1) collision.M0P1 = 1;
if(m0 && p0) collision.M0P0 = 1;
if(m1 && p0) collision.M1P0 = 1;
if(m1 && p1) collision.M1P1 = 1;
if(p0 && pf) collision.P0PF = 1;
if(p0 && bl) collision.P0BL = 1;
if(p1 && pf) collision.P1PF = 1;
if(p1 && bl) collision.P1BL = 1;
if(m0 && pf) collision.M0PF = 1;
if(m0 && bl) collision.M0BL = 1;
if(m1 && pf) collision.M1PF = 1;
if(m1 && bl) collision.M1BL = 1;
if(bl && pf) collision.BLPF = 1;
if(p0 && p1) collision.P0P1 = 1;
if(m0 && m1) collision.M0M1 = 1;
}
auto TIA::runPlayfield(n8 x) -> n1 {
auto pos = x >> 2;
if(!playfield.mirror || pos < 20) return playfield.graphics.bit(pos % 20);
return playfield.graphics.bit(19 - (pos % 20));
}
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
auto& player = this->player[index];
auto position = player.position;
// Handle player stretch capability
auto width = 8;
if(player.size == 5) width = 16;
if(player.size == 7) width = 32;
// Handle repeat capability
auto repeat = 1;
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
if(player.size == 3 || player.size == 6) repeat = 3;
auto spacing = 8;
if(player.size == 2 || player.size == 6) spacing = 24;
if(player.size == 4 ) spacing = 56;
for(int i = 0; i < repeat; i++) {
if (x >= position && x < position + width) {
auto bit = player.reflect ? (x - position) / (width / 8) : 7 - ((x - position) / (width / 8));
return player.graphics[player.delay].bit(bit);
}
position = (position + (spacing + width)) % 160;
}
return 0;
}
auto TIA::runMissile(n8 x, n1 index) -> n1 {
auto& missile = this->missile[index];
if (!missile.enable || missile.reset) return 0;
const int missileSizes[4] = {1, 2, 4, 8};
return x >= missile.position && x < missile.position + missileSizes[missile.size];
}
auto TIA::runBall(n8 x) -> n1 {
if(!ball.enable[ball.delay]) return 0;
const int ballSizes[4] = {1, 2, 4, 8};
return x >= ball.position && x < ball.position + ballSizes[ball.size];
}
auto TIA::frame() -> void {
screen->frame();
scheduler.exit(Event::Frame);
}
auto TIA::step(u32 clocks) -> void {
Thread::step(clocks);
Thread::synchronize();
}
auto TIA::power(bool reset) -> void {
Thread::create(system.frequency(), {&TIA::main, this});
screen->power();
io = {};
playfield = {};
for(auto& p : player) p = {};
for(auto& m : missile) m = {};
ball = {};
collision = {};
for(auto n : range(writeQueue.maxItems)) writeQueue.items[n] = {};
for(u32 level : range(15)) {
volume[level] = pow(2, level * -2.0 / 6.0);
}
volume[15] = 0;
}
}
+147
View File
@@ -0,0 +1,147 @@
struct TIA : Thread {
Node::Object node;
Node::Video::Screen screen;
Node::Audio::Stream stream;
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
//tia.cpp
auto load(Node::Object) -> void;
auto unload() -> void;
auto main() -> void;
auto step(u32 clocks = 1) -> void;
auto power(bool reset) -> void;
auto frame() -> void;
auto scanline() -> void;
auto pixel(n8 x) -> void;
auto runPlayfield(n8 x) -> n1;
auto runBall(n8 x) -> n1;
auto runPlayer(n8 x, n1 index) -> n1;
auto runMissile(n8 x, n1 index) -> n1;
auto runCollision() -> void;
//io.cpp
auto read(n8 address) -> n8;
auto write(n8 address, n8 data) -> void;
auto vsync(n1 state) -> void;
auto ctrlpf(n8 data) -> void;
auto cxclr() -> void;
auto hmove() -> void;
auto hmclr() -> void;
auto grp(n1 index, n8 data) -> void;
auto resp(n1 index) -> void;
auto resm(n1 index) -> void;
auto resmp(n1 index, n8 data) -> void;
auto resbl() -> void;
// audio.cpp
auto runAudio() -> void;
//color.cpp
auto color(n32) -> n64;
//serialization.cpp
auto serialize(serializer&) -> void;
// write-queue.cpp
struct WriteQueue {
inline auto add(u8 address, n8 data, i8 delay) -> void;
inline auto step() -> void;
struct QueueItem {
inline auto commit() -> void;
n1 active;
i8 address;
n8 data;
i8 delay;
};
QueueItem items[16];
const int maxItems = 16;
} writeQueue;
struct {
u9 vcounter;
u8 hcounter;
u8 hmoveTriggered;
n1 vsync;
n1 vblank;
n7 bgColor;
n7 p0Color;
n7 p1Color;
n7 fgColor;
} io;
struct {
n20 graphics;
n1 mirror;
n1 scoreMode;
n1 priority;
} playfield;
struct {
n8 graphics[2];
n1 reflect;
n3 size;
i4 offset;
n8 position;
n1 delay;
} player[2];
struct {
n1 enable;
n1 reset;
n2 size;
i4 offset;
n8 position;
} missile[2];
struct {
n1 enable[2];
n2 size;
i4 offset;
n1 delay;
n8 position;
} ball;
struct {
n1 M0P0;
n1 M0P1;
n1 M1P0;
n1 M1P1;
n1 P0PF;
n1 P0BL;
n1 P1PF;
n1 P1BL;
n1 M0PF;
n1 M0BL;
n1 M1PF;
n1 M1BL;
n1 BLPF;
n1 P0P1;
n1 M0M1;
} collision;
struct AudioChannel {
auto phase0() -> void;
auto phase1() -> u8;
n1 enable;
n8 divCounter;
n8 noiseCounter;
n1 noiseFeedback;
n8 pulseCounter;
n1 pulseCounterPaused;
n1 pulseFeedback;
n4 volume;
n4 control;
n5 frequency;
} audio[2];
f64 volume[16];
};
extern TIA tia;
+46
View File
@@ -0,0 +1,46 @@
auto TIA::WriteQueue::add(u8 address, n8 data, i8 delay) -> void {
for(auto n : range(maxItems)) {
if(!items[n].active) {
items[n].address = address;
items[n].data = data;
items[n].delay = delay;
items[n].active = 1;
return;
}
}
}
auto TIA::WriteQueue::step() -> void {
auto& items = tia.writeQueue.items;
for(auto n : range(maxItems)) {
if(items[n].active && --items[n].delay == 0) {
items[n].commit();
items[n].active = 0;
}
}
}
auto TIA::WriteQueue::QueueItem::commit() -> void {
switch(address) {
case 0x01: tia.io.vblank = data.bit(1); return; // VBLANK
case 0x0b: tia.player[0].reflect = data.bit(3); return; // REFP0
case 0x0c: tia.player[1].reflect = data.bit(3); return; // REFP1
case 0x0d: tia.playfield.graphics.bit(0, 3) = data.bit(4, 7); return; // PF0
case 0x0e: for(auto n : range(8)) tia.playfield.graphics.bit(4 + n) = data.bit(7 - n); return; // PF1
case 0x0f: tia.playfield.graphics.bit(12, 19) = data; return; // PF2
case 0x1b: tia.grp(0, data); return; // GRP0
case 0x1c: tia.grp(1, data); return; // GRP1
case 0x1d: tia.missile[0].enable = data.bit(1); return; // ENAM0
case 0x1e: tia.missile[1].enable = data.bit(1); return; // ENAM1
case 0x1f: tia.ball.enable[0] = data.bit(1); return; // ENABL
case 0x20: tia.player[0].offset = data.bit(4, 7); return; // HMP0
case 0x21: tia.player[1].offset = data.bit(4, 7); return; // HMP1
case 0x22: tia.missile[0].offset = data.bit(4, 7); return; // HMM0
case 0x23: tia.missile[1].offset = data.bit(4, 7); return; // HMM1
case 0x24: tia.ball.offset = data.bit(4, 7); return; // HMBL
case 0x2a: tia.hmove(); return; // HMOVE
case 0x2b: tia.hmclr(); return; // HMCLR
}
debug(unimplemented, "[TIA] writeQueue: ",hex(address), " = ", hex(data));
}
+5 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <libco/libco.h>
#include <sljit.h>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
@@ -37,16 +38,16 @@ using namespace nall;
namespace ares {
static const string Name = "ares";
static const string Version = "124";
static const string Version = "129";
static const string Copyright = "ares team, Near";
static const string License = "ISC";
static const string LicenseURI = "https://opensource.org/licenses/ISC";
static const string Website = "ares-emulator.github.io";
static const string WebsiteURI = "https://ares-emulator.github.io";
static const string Website = "ares-emu.net";
static const string WebsiteURI = "https://ares-emu.net/";
//incremented only when serialization format changes
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
static const string SerializerVersion = "123.1";
static const string SerializerVersion = "129";
namespace VFS {
using Pak = shared_pointer<vfs::directory>;
+22 -16
View File
@@ -1,16 +1,22 @@
#include <ares/ares.hpp>
namespace ares::Memory {
alignas(4096) u8 fixedBuffer[1_GiB];
FixedAllocator::FixedAllocator() {
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
}
auto FixedAllocator::get() -> bump_allocator& {
static FixedAllocator allocator;
return allocator._allocator;
}
}
#include <ares/ares.hpp>
namespace ares::Memory {
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
//stub for unsupported platforms
FixedAllocator::FixedAllocator() {
}
#else
alignas(4096) u8 fixedBuffer[128_MiB];
FixedAllocator::FixedAllocator() {
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
}
#endif
auto FixedAllocator::get() -> bump_allocator& {
static FixedAllocator allocator;
return allocator._allocator;
}
}
@@ -27,8 +27,8 @@ struct Instruction : Tracer {
for(auto& history : _history) history = ~0;
}
auto address(u32 address) -> bool {
address &= (1ull << _addressBits) - 1; //mask upper bits of address
auto address(u64 address) -> bool {
address &= ~0ull >> (64 - _addressBits); //mask upper bits of address
_address = address;
address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
@@ -55,9 +55,9 @@ struct Instruction : Tracer {
//mark an already-executed address as not executed yet for trace masking.
//call when writing to executable RAM to support self-modifying code.
auto invalidate(u32 address) -> void {
auto invalidate(u64 address) -> void {
if(unlikely(_mask && updateMasks())) {
address &= (1ull << _addressBits) - 1;
address &= ~0ull >> (64 - _addressBits);
address >>= _addressMask;
_masks[address >> 3] &= ~(1 << (address & 7));
}
@@ -104,7 +104,7 @@ struct Instruction : Tracer {
protected:
auto updateMasks() -> bool {
auto size = 1ull << _addressBits >> _addressMask >> 3;
auto size = 1ull << (_addressBits - _addressMask - 3);
if(!_mask || !size) return _masks.reset(), false;
if(_masks.size() == size) return true;
_masks.reset();
+1
View File
@@ -36,6 +36,7 @@ auto Screen::main(uintptr_t) -> void {
auto Screen::quit() -> void {
_kill = true;
_thread.join();
_sprites.reset();
}
auto Screen::power() -> void {
+1
View File
@@ -16,6 +16,7 @@ struct Platform {
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
virtual auto event(Event) -> void {}
virtual auto log(string_view message) -> void {}
virtual auto status(string_view message) -> void {}
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
virtual auto audio(Node::Audio::Stream) -> void {}
virtual auto input(Node::Input::Input) -> void {}
+6 -2
View File
@@ -1,6 +1,10 @@
build := stable
nall.path := ../../../nall
include $(nall.path)/GNUmakefile
all:
sourcery resource.bml resource.cpp resource.hpp
clean:
rm resource.cpp
rm resource.hpp
$(call delete,resource.cpp)
$(call delete,resource.hpp)
+2 -1
View File
@@ -84,7 +84,8 @@ inline auto Thread::synchronize() -> void {
template<typename... P>
inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
//switching to another thread does not guarantee it will catch up before switching back.
while(thread.clock() < clock()) {
//make sure not to switch to threads that were destroyed during synchronization
while(thread.clock() < clock() && thread.handle()) {
//disable synchronization for auxiliary threads during scheduler synchronization.
//synchronization can begin inside of this while loop.
if(scheduler.synchronizing()) break;
+1 -1
View File
@@ -74,7 +74,7 @@ auto M24C::write() -> void {
case Mode::Device:
if(counter <= 8) {
device = device << 1 | data();
} else if(!select()) {
} else if(select() != Acknowledge) {
mode = Mode::Standby;
} else if(device & 1) {
mode = Mode::Read;
@@ -50,7 +50,7 @@ template<u32 Size> auto M68000::_address(EffectiveAddress& ea) -> string {
if(ea.mode == 2) return {_addressRegister(AddressRegister{ea.reg})};
if(ea.mode == 5) return {"$", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L)};
if(ea.mode == 6) return {"$", hex(_readIndex(read(AddressRegister{ea.reg})), 6L)};
if(ea.mode == 7) return {"$", hex((i16)_readPC<Word>(), 6L)};
if(ea.mode == 7) { auto imm = _readPC<Word>(); return {"$", imm >= 0x8000 ? hex((i16)imm, 6L, 'f') : hex((i16)imm, 6L, '0') }; }
if(ea.mode == 8) return {"$", hex(_readPC<Long>(), 6L)};
if(ea.mode == 9) return {"$", hex(_pc + (i16)_readPC(), 6L)};
if(ea.mode == 10) return {"$", hex(_readIndex(_pc), 6L)};
@@ -65,7 +65,7 @@ template<u32 Size> auto M68000::_effectiveAddress(EffectiveAddress& ea) -> strin
if(ea.mode == 4) return {"-(", _addressRegister(AddressRegister{ea.reg}), ")"};
if(ea.mode == 5) return {"($", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L), ")"};
if(ea.mode == 6) return {"($", hex(_readIndex(read(AddressRegister{ea.reg})), 6L), ")"};
if(ea.mode == 7) return {"($", hex((i16)_readPC<Word>(), 6L), ")"};
if(ea.mode == 7) { auto imm = _readPC<Word>(); return {"($", imm >= 0x8000 ? hex((i16)imm, 6L, 'f') : hex((i16)imm, 6L, '0'), ")"}; }
if(ea.mode == 8) return {"($", hex(_readPC<Long>(), 6L), ")"};
if(ea.mode == 9) return {"($", hex(_readDisplacement(_pc), 6L), ")"};
if(ea.mode == 10) return {"($", hex(_readIndex(_pc), 6L), ")"};
@@ -414,6 +414,7 @@ M68000::M68000() {
for(n3 mode : range(8))
for(n3 reg : range(8)) {
auto opcode = pattern("1011 ---+ 11-- ----") | areg << 9 | mode << 3 | reg << 0;
if(mode == 7 && reg >= 5) continue;
AddressRegister with{areg};
EffectiveAddress from{mode, reg};
@@ -1256,7 +1257,7 @@ M68000::M68000() {
for(n3 mode : range(8))
for(n3 reg : range(8)) {
auto opcode = pattern("0100 1010 ++-- ----") | mode << 3 | reg << 0;
if(mode == 7 && reg >= 2) continue;
if(mode == 1 || mode == 7 && reg >= 2) continue;
EffectiveAddress from{mode, reg};
bind(opcode | 0 << 6, TST<Byte>, from);
@@ -332,14 +332,20 @@ auto M68000::instructionCHK(DataRegister compare, EffectiveAddress maximum) -> v
r.z = clip<Word>(target) == 0;
r.n = sign<Word>(target) < 0;
if(r.n) return exception(Exception::BoundsCheck, Vector::BoundsCheck);
if(r.n) {
prefetched();
return exception(Exception::BoundsCheck, Vector::BoundsCheck);
}
auto result = (n64)target - source;
r.c = sign<Word>(result >> 1) < 0;
r.v = sign<Word>((target ^ source) & (target ^ result)) < 0;
r.z = clip<Word>(result) == 0;
r.n = sign<Word>(result) < 0;
if(r.n == r.v && !r.z) return exception(Exception::BoundsCheck, Vector::BoundsCheck);
if(r.n == r.v && !r.z) {
prefetched();
return exception(Exception::BoundsCheck, Vector::BoundsCheck);
}
prefetch();
}
@@ -390,22 +396,30 @@ template<u32 Size> auto M68000::instructionCMPM(EffectiveAddress from, Effective
}
auto M68000::instructionDBCC(n4 test, DataRegister with) -> void {
auto displacement = extension<Word>();
if(condition(test)) {
idle(4);
} else {
idle(2);
r.pc -= 2;
if(!condition(test)) { // cc false
auto disp = sign<Word>(r.irc);
// predict branch
r.pc += disp;
prefetch();
// decrement register
n16 result = read<Word>(with);
write<Word>(with, result - 1);
if(result) {
idle(2);
r.pc -= 4;
r.pc += sign<Word>(displacement);
prefetch();
// branch taken
prefetch();
return;
} else {
idle(2);
// branch not taken
r.pc -= disp;
}
} else { // cc true
idle(2);
r.pc += 2;
}
prefetch();
prefetch();
}
auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
@@ -413,6 +427,7 @@ auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
n32 divisor = read<Word>(from) << 16, odivisor = divisor;
if(divisor == 0) {
prefetched();
return exception(Exception::DivisionByZero, Vector::DivisionByZero);
}
@@ -489,6 +504,7 @@ auto M68000::instructionDIVU(EffectiveAddress from, DataRegister with) -> void {
n32 divisor = read<Word>(from) << 16;
if(divisor == 0) {
prefetched();
return exception(Exception::DivisionByZero, Vector::DivisionByZero);
}
@@ -1262,7 +1278,7 @@ auto M68000::instructionSWAP(DataRegister with) -> void {
auto M68000::instructionTAS(EffectiveAddress with) -> void {
n32 data;
if(with.mode == DataRegisterDirect) {
if(lockable() || with.mode == DataRegisterDirect) {
data = read<Byte, Hold>(with);
prefetch();
write<Byte>(with, data | 0x80);
+22 -7
View File
@@ -50,21 +50,36 @@ auto M68000::supervisor() -> bool {
auto M68000::exception(u32 exception, u32 vector, u32 priority) -> void {
r.stop = false;
idle(10); //todo: not accurate
auto pc = r.pc;
auto sr = readSR();
// register setup (+6 cyc)
idle(6);
n32 pc = r.pc - 4;
n32 sr = readSR();
if(!r.s) swap(r.a[7], r.sp);
r.i = priority;
r.s = 1;
r.t = 0;
push<Long>(pc - 4);
push<Word>(sr);
// push pc low (+4 cyc)
push<Word>(pc & 0x0000ffff);
// external interrupt handling
if(exception == Exception::Interrupt) {
// IACK vector number acquisition (+4 cyc normal or +10 to +18 cyc autovectored)
// & justify vector number (+4 cyc)
idle(16+4); // assuming autovector cycles (approximated for Megadrive)
r.i = priority;
}
// push pc high & sr (+8 cyc)
push<Long>(sr << 16 | pc >> 16);
// read vector address (+8 cyc)
r.pc = read<Long>(vector << 2);
// todo: if the vector address read causes an address error, this exception should be escalated
// prefetch (+8 cyc)
prefetch();
if(exception == Exception::Interrupt) idle(2); // dead cycles (+2 cyc)
prefetch();
}
+4 -2
View File
@@ -9,8 +9,10 @@ struct M68000 {
virtual auto wait(u32 clocks) -> void = 0;
virtual auto read(n1 upper, n1 lower, n24 address, n16 data = 0) -> n16 = 0;
virtual auto write(n1 upper, n1 lower, n24 address, n16 data) -> void = 0;
virtual auto lockable() -> bool { return true; }
auto ird() const -> n16 { return r.ird; }
auto irc() const -> n16 { return r.irc; }
enum : bool { User, Supervisor };
enum : u32 { Byte, Word, Long };
@@ -70,8 +72,8 @@ struct M68000 {
M68000();
auto power() -> void;
auto supervisor() -> bool;
auto exception(u32 exception, u32 vector, u32 priority = 7) -> void;
auto interrupt(u32 vector, u32 priority = 7) -> void;
auto exception(u32 exception, u32 vector, u32 priority = 0) -> void;
auto interrupt(u32 vector, u32 priority = 0) -> void;
//registers.cpp
struct DataRegister {
@@ -57,19 +57,24 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
switch(auto opcode = readDebugger(pc)) {
op(0x00, "brk", immediate)
op(0x01, "ora", indirectX)
op(0x04, "nop", zeroPage)
op(0x05, "ora", zeroPage)
op(0x06, "asl", zeroPage)
op(0x08, "php", implied)
op(0x09, "ora", immediate)
op(0x0a, "asl", implied)
op(0x0c, "nop", absolute)
op(0x0d, "ora", absolute)
op(0x0e, "asl", absolute)
op(0x10, "bpl", branch)
op(0x11, "ora", indirectY)
op(0x14, "nop", zeroPageX)
op(0x15, "ora", zeroPageX)
op(0x16, "asl", zeroPageX)
op(0x18, "clc", implied)
op(0x19, "ora", absoluteY)
op(0x1a, "nop", implied)
op(0x1c, "nop", absoluteX)
op(0x1d, "ora", absoluteX)
op(0x1e, "asl", absoluteX)
op(0x20, "jsr", absolute)
@@ -85,14 +90,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0x2e, "rol", absolute)
op(0x30, "bmi", branch)
op(0x31, "and", indirectY)
op(0x34, "nop", zeroPageX)
op(0x35, "and", zeroPageX)
op(0x36, "rol", zeroPageX)
op(0x38, "sec", implied)
op(0x39, "and", absoluteY)
op(0x3a, "nop", implied)
op(0x3c, "nop", absoluteX)
op(0x3d, "and", absoluteX)
op(0x3e, "rol", absoluteX)
op(0x40, "rti", implied)
op(0x41, "eor", indirectX)
op(0x44, "nop", zeroPage)
op(0x45, "eor", zeroPage)
op(0x46, "lsr", zeroPage)
op(0x48, "pha", implied)
@@ -103,14 +112,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0x4e, "lsr", absolute)
op(0x50, "bvc", branch)
op(0x51, "eor", indirectY)
op(0x54, "nop", zeroPageX)
op(0x55, "eor", zeroPageX)
op(0x56, "lsr", zeroPageX)
op(0x58, "cli", implied)
op(0x59, "eor", absoluteY)
op(0x5a, "nop", implied)
op(0x5c, "nop", absoluteX)
op(0x5d, "eor", absoluteX)
op(0x5e, "lsr", absoluteX)
op(0x60, "rts", implied)
op(0x61, "adc", indirectX)
op(0x64, "nop", zeroPage)
op(0x65, "adc", zeroPage)
op(0x66, "ror", zeroPage)
op(0x68, "pla", implied)
@@ -121,17 +134,23 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0x6e, "ror", absolute)
op(0x70, "bvs", branch)
op(0x71, "adc", indirectY)
op(0x74, "nop", zeroPageX)
op(0x75, "adc", zeroPageX)
op(0x76, "ror", zeroPageX)
op(0x78, "sei", implied)
op(0x79, "adc", absoluteY)
op(0x7a, "nop", implied)
op(0x7c, "nop", absoluteX)
op(0x7d, "adc", absoluteX)
op(0x7e, "ror", absoluteX)
op(0x80, "nop", immediate)
op(0x81, "sta", indirectX)
op(0x82, "nop", immediate)
op(0x84, "sty", zeroPage)
op(0x85, "sta", zeroPage)
op(0x86, "stx", zeroPage)
op(0x88, "dey", implied)
op(0x89, "nop", immediate)
op(0x8a, "txa", implied)
op(0x8c, "sty", absolute)
op(0x8d, "sta", absolute)
@@ -170,6 +189,7 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0xbe, "ldx", absoluteY)
op(0xc0, "cpy", immediate)
op(0xc1, "cmp", indirectX)
op(0xc2, "nop", immediate)
op(0xc4, "cpy", zeroPage)
op(0xc5, "cmp", zeroPage)
op(0xc6, "dec", zeroPage)
@@ -181,14 +201,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0xce, "dec", absolute)
op(0xd0, "bne", branch)
op(0xd1, "cmp", indirectY)
op(0xd4, "nop", zeroPageX)
op(0xd5, "cmp", zeroPageX)
op(0xd6, "dec", zeroPageX)
op(0xd8, "cld", implied)
op(0xd9, "cmp", absoluteY)
op(0xda, "nop", implied)
op(0xdc, "nop", absoluteX)
op(0xdd, "cmp", absoluteX)
op(0xde, "dec", absoluteX)
op(0xe0, "cpx", immediate)
op(0xe1, "sbc", indirectX)
op(0xe2, "nop", immediate)
op(0xe4, "cpx", zeroPage)
op(0xe5, "sbc", zeroPage)
op(0xe6, "inc", zeroPage)
@@ -200,10 +224,13 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0xee, "inc", absolute)
op(0xf0, "beq", branch)
op(0xf1, "sbc", indirectY)
op(0xf4, "nop", zeroPageX)
op(0xf5, "sbc", zeroPageX)
op(0xf6, "inc", zeroPageX)
op(0xf8, "sed", implied)
op(0xf9, "sbc", absoluteY)
op(0xfa, "nop", implied)
op(0xfc, "nop", absoluteX)
op(0xfd, "sbc", absoluteX)
op(0xfe, "inc", absoluteX)
@@ -20,19 +20,24 @@ auto MOS6502::instruction() -> void {
switch(code) {
op(0x00, Break)
op(0x01, IndirectXRead, fp(ORA), A)
op(0x04, NoOperationZeroPage)
op(0x05, ZeroPageRead, fp(ORA), A)
op(0x06, ZeroPageModify, fp(ASL))
op(0x08, PushP)
op(0x09, Immediate, fp(ORA), A)
op(0x0a, Implied, fp(ASL), A)
op(0x0c, NoOperationAbsolute)
op(0x0d, AbsoluteRead, fp(ORA), A)
op(0x0e, AbsoluteModify, fp(ASL))
op(0x10, Branch, N == 0)
op(0x11, IndirectYRead, fp(ORA), A)
op(0x14, NoOperationZeroPage, X)
op(0x15, ZeroPageRead, fp(ORA), A, X)
op(0x16, ZeroPageModify, fp(ASL), X)
op(0x18, Clear, C)
op(0x19, AbsoluteRead, fp(ORA), A, Y)
op(0x1a, NoOperation)
op(0x1c, NoOperationAbsolute, X)
op(0x1d, AbsoluteRead, fp(ORA), A, X)
op(0x1e, AbsoluteModify, fp(ASL), X)
op(0x20, CallAbsolute)
@@ -48,14 +53,18 @@ auto MOS6502::instruction() -> void {
op(0x2e, AbsoluteModify, fp(ROL))
op(0x30, Branch, N == 1)
op(0x31, IndirectYRead, fp(AND), A)
op(0x34, NoOperationZeroPage, X)
op(0x35, ZeroPageRead, fp(AND), A, X)
op(0x36, ZeroPageModify, fp(ROL), X)
op(0x38, Set, C)
op(0x39, AbsoluteRead, fp(AND), A, Y)
op(0x3a, NoOperation)
op(0x3c, NoOperationAbsolute, X)
op(0x3d, AbsoluteRead, fp(AND), A, X)
op(0x3e, AbsoluteModify, fp(ROL), X)
op(0x40, ReturnInterrupt)
op(0x41, IndirectXRead, fp(EOR), A)
op(0x44, NoOperationZeroPage)
op(0x45, ZeroPageRead, fp(EOR), A)
op(0x46, ZeroPageModify, fp(LSR))
op(0x48, Push, A)
@@ -66,14 +75,18 @@ auto MOS6502::instruction() -> void {
op(0x4e, AbsoluteModify, fp(LSR))
op(0x50, Branch, V == 0)
op(0x51, IndirectYRead, fp(EOR), A)
op(0x54, NoOperationZeroPage, X)
op(0x55, ZeroPageRead, fp(EOR), A, X)
op(0x56, ZeroPageModify, fp(LSR), X)
op(0x58, Clear, I)
op(0x59, AbsoluteRead, fp(EOR), A, Y)
op(0x5a, NoOperation)
op(0x5c, NoOperationAbsolute, X)
op(0x5d, AbsoluteRead, fp(EOR), A, X)
op(0x5e, AbsoluteModify, fp(LSR), X)
op(0x60, ReturnSubroutine)
op(0x61, IndirectXRead, fp(ADC), A)
op(0x64, NoOperationZeroPage)
op(0x65, ZeroPageRead, fp(ADC), A)
op(0x66, ZeroPageModify, fp(ROR))
op(0x68, Pull, A)
@@ -84,17 +97,23 @@ auto MOS6502::instruction() -> void {
op(0x6e, AbsoluteModify, fp(ROR))
op(0x70, Branch, V == 1)
op(0x71, IndirectYRead, fp(ADC), A)
op(0x74, NoOperationZeroPage, X)
op(0x75, ZeroPageRead, fp(ADC), A, X)
op(0x76, ZeroPageModify, fp(ROR), X)
op(0x78, Set, I)
op(0x79, AbsoluteRead, fp(ADC), A, Y)
op(0x7a, NoOperation)
op(0x7c, NoOperationAbsolute, X)
op(0x7d, AbsoluteRead, fp(ADC), A, X)
op(0x7e, AbsoluteModify, fp(ROR), X)
op(0x80, NoOperationImmediate)
op(0x81, IndirectXWrite, A)
op(0x82, NoOperationImmediate)
op(0x84, ZeroPageWrite, Y)
op(0x85, ZeroPageWrite, A)
op(0x86, ZeroPageWrite, X)
op(0x88, Implied, fp(DEC), Y)
op(0x89, NoOperationImmediate)
op(0x8a, Transfer, X, A, 1)
op(0x8c, AbsoluteWrite, Y)
op(0x8d, AbsoluteWrite, A)
@@ -133,6 +152,7 @@ auto MOS6502::instruction() -> void {
op(0xbe, AbsoluteRead, fp(LD), X, Y)
op(0xc0, Immediate, fp(CPY), Y)
op(0xc1, IndirectXRead, fp(CMP), A)
op(0xc2, NoOperationImmediate)
op(0xc4, ZeroPageRead, fp(CPY), Y)
op(0xc5, ZeroPageRead, fp(CMP), A)
op(0xc6, ZeroPageModify, fp(DEC))
@@ -144,14 +164,18 @@ auto MOS6502::instruction() -> void {
op(0xce, AbsoluteModify, fp(DEC))
op(0xd0, Branch, Z == 0)
op(0xd1, IndirectYRead, fp(CMP), A)
op(0xd4, NoOperationZeroPage, X)
op(0xd5, ZeroPageRead, fp(CMP), A, X)
op(0xd6, ZeroPageModify, fp(DEC), X)
op(0xd8, Clear, D)
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
op(0xda, NoOperation)
op(0xdc, NoOperationAbsolute, X)
op(0xdd, AbsoluteRead, fp(CMP), A, X)
op(0xde, AbsoluteModify, fp(DEC), X)
op(0xe0, Immediate, fp(CPX), X)
op(0xe1, IndirectXRead, fp(SBC), A)
op(0xe2, NoOperationImmediate)
op(0xe4, ZeroPageRead, fp(CPX), X)
op(0xe5, ZeroPageRead, fp(SBC), A)
op(0xe6, ZeroPageModify, fp(INC))
@@ -163,10 +187,13 @@ auto MOS6502::instruction() -> void {
op(0xee, AbsoluteModify, fp(INC))
op(0xf0, Branch, Z == 1)
op(0xf1, IndirectYRead, fp(SBC), A)
op(0xf4, NoOperationZeroPage, X)
op(0xf5, ZeroPageRead, fp(SBC), A, X)
op(0xf6, ZeroPageModify, fp(INC), X)
op(0xf8, Set, D)
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
op(0xfa, NoOperation)
op(0xfc, NoOperationAbsolute, X)
op(0xfd, AbsoluteRead, fp(SBC), A, X)
op(0xfe, AbsoluteModify, fp(INC), X)
}
@@ -139,6 +139,34 @@ auto MOS6502::instructionNoOperation() -> void {
L idle();
}
auto MOS6502::instructionNoOperationAbsolute() -> void {
n16 absolute = operand();
absolute |= operand() << 8;
L read(absolute);
}
auto MOS6502::instructionNoOperationAbsolute(n8 index) -> void {
n16 absolute = operand();
absolute |= operand() << 8;
idlePageCrossed(absolute, absolute + index);
L read(absolute + index);
}
auto MOS6502::instructionNoOperationImmediate() -> void {
L operand();
}
auto MOS6502::instructionNoOperationZeroPage() -> void {
auto zeroPage = operand();
L load(zeroPage);
}
auto MOS6502::instructionNoOperationZeroPage(n8 index) -> void {
auto zeroPage = operand();
load(zeroPage);
L load(zeroPage + index);
}
auto MOS6502::instructionPull(n8& data) -> void {
idle();
idle();
@@ -70,6 +70,11 @@ struct MOS6502 {
auto instructionJumpAbsolute() -> void;
auto instructionJumpIndirect() -> void;
auto instructionNoOperation() -> void;
auto instructionNoOperationAbsolute() -> void;
auto instructionNoOperationAbsolute(n8 index) -> void;
auto instructionNoOperationImmediate() -> void;
auto instructionNoOperationZeroPage() -> void;
auto instructionNoOperationZeroPage(n8 index) -> void;
auto instructionPull(n8& data) -> void;
auto instructionPullP() -> void;
auto instructionPush(n8& data) -> void;
+25 -12
View File
@@ -1,3 +1,11 @@
auto SH2::Recompiler::invalidate(u32 address) -> void {
auto pool = pools[address >> 8 & 0xffffff];
if(!pool) return;
memory::jitprotect(false);
pool->blocks[address >> 1 & 0x7f] = nullptr;
memory::jitprotect(true);
}
auto SH2::Recompiler::pool(u32 address) -> Pool* {
auto& pool = pools[address >> 8 & 0xffffff];
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
@@ -7,7 +15,9 @@ auto SH2::Recompiler::pool(u32 address) -> Pool* {
auto SH2::Recompiler::block(u32 address) -> Block* {
if(auto block = pool(address)->blocks[address >> 1 & 0x7f]) return block;
auto block = emit(address);
return pool(address)->blocks[address >> 1 & 0x7f] = block;
pool(address)->blocks[address >> 1 & 0x7f] = block;
memory::jitprotect(true);
return block;
}
alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
@@ -25,34 +35,37 @@ alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
return 0;
}
#define CCR mem(sreg(1), offsetof(Registers, CCR))
auto SH2::Recompiler::emit(u32 address) -> Block* {
if(unlikely(allocator.available() < 1_MiB)) {
print("SH2 allocator flush\n");
memory::jitprotect(false);
allocator.release(bump_allocator::zero_fill);
memory::jitprotect(true);
reset();
}
auto block = (Block*)allocator.acquire(sizeof(Block));
block->code = allocator.acquire();
bind({block->code, allocator.available()});
beginFunction(2);
bool hasBranched = 0;
while(true) {
u16 instruction = self.readWord(address);
bool branched = emitInstruction(instruction);
mov(rax, mem64(&self.CCR));
inc(rax);
mov(mem64(&self.CCR), rax);
call(&SH2::instructionEpilogue, &self);
add64(CCR, CCR, imm(1));
call(&SH2::instructionEpilogue);
address += 2;
if(hasBranched || (address & 0xfe) == 0) break; //block boundary
hasBranched = branched;
test(al, al);
jz(imm8(1));
ret();
testJumpEpilog();
}
ret();
jumpEpilog();
memory::jitprotect(false);
block->code = endFunction();
allocator.reserve(size());
return block;
}
#undef CCR
+1 -1
View File
@@ -67,7 +67,7 @@ auto SH2::illegalInstruction() -> void {
push(SR);
push(PC);
jump(readLong(VBR + vector * 4) + 4);
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L));
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
}
auto SH2::illegalSlotInstruction() -> void {
+1 -1
View File
@@ -45,7 +45,7 @@ auto SH2::instruction() -> void {
}
}
auto SH2::instructionEpilogue() -> bool {
auto SH2::instructionEpilogue() -> s32 {
switch(PPM) {
case Branch::Step: PC = PC + 2; return 0;
case Branch::Slot: PC = PC + 2; PPM = Branch::Take; return 0;
+20 -10
View File
@@ -5,9 +5,10 @@ auto SH2::ADD(u32 m, u32 n) -> void {
//ADDC Rm,Rn
auto SH2::ADDC(u32 m, u32 n) -> void {
bool c = (u64)R[n] + R[m] >> 32;
R[n] = R[n] + R[m] + SR.T;
SR.T = c;
u32 a = R[n];
u32 b = a + R[m];
R[n] = b + SR.T;
SR.T = a > b || b > R[n];
}
//ADD #imm,Rn
@@ -17,9 +18,11 @@ auto SH2::ADDI(u32 i, u32 n) -> void {
//ADDV Rm,Rn
auto SH2::ADDV(u32 m, u32 n) -> void {
bool v = ~(R[n] ^ R[m]) & (R[n] ^ R[n] + R[m]) >> 31;
u32 d = ((s32)R[n] < 0);
u32 s = ((s32)R[m] < 0) + d;
R[n] += R[m];
SR.T = v;
u32 r = ((s32)R[n] < 0) + d;
SR.T = s != 1 ? r == 1 : 0;
}
//AND Rm,Rn
@@ -140,7 +143,11 @@ auto SH2::CMPPZ(u32 n) -> void {
//CMP/STR Rm,Rn
auto SH2::CMPSTR(u32 m, u32 n) -> void {
SR.T = bool(R[n] ^ R[m]);
u32 t = R[n] ^ R[m];
SR.T = !(t & 0xff << 24)
|| !(t & 0xff << 16)
|| !(t & 0xff << 8)
|| !(t & 0xff);
}
//DIV0S Rm,Rn
@@ -353,8 +360,9 @@ auto SH2::MOVBLG(u32 d) -> void {
//MOV.B Rm,@-Rn
auto SH2::MOVBM(u32 m, u32 n) -> void {
u32 t = R[m];
R[n] -= 1;
writeByte(R[n], R[m]);
writeByte(R[n], t);
}
//MOV.B @Rm+,Rn
@@ -405,8 +413,9 @@ auto SH2::MOVWLG(u32 d) -> void {
//MOV.W Rm,@-Rn
auto SH2::MOVWM(u32 m, u32 n) -> void {
u32 t = R[m];
R[n] -= 2;
writeWord(R[n], R[m]);
writeWord(R[n], t);
}
//MOV.W @Rm+,Rn
@@ -457,8 +466,9 @@ auto SH2::MOVLLG(u32 d) -> void {
//MOV.L Rm,@-Rn
auto SH2::MOVLM(u32 m, u32 n) -> void {
u32 t = R[m];
R[n] -= 4;
writeLong(R[n], R[m]);
writeLong(R[n], t);
}
//MOV.L @Rm+,Rn
@@ -536,7 +546,7 @@ auto SH2::NEG(u32 m, u32 n) -> void {
auto SH2::NEGC(u32 m, u32 n) -> void {
u32 t = 0 - R[m];
R[n] = t - SR.T;
SR.T = (s32)t >= 0 || t < R[n];
SR.T = 0 < t || t < R[n];
}
//NOP
File diff suppressed because it is too large. Load diff
@@ -133,6 +133,7 @@ auto SH2::WDT::serialize(serializer& s) -> void {
s(rstcsr.rste);
s(rstcsr.wovf);
s(wtcnt);
s(pendingIRQ);
}
auto SH2::UBC::serialize(serializer& s) -> void {
+37 -5
View File
@@ -3,14 +3,26 @@
namespace ares {
#define SP R[15]
#define SP R[15]
#define R regs.R
#define PC regs.PC
#define PR regs.PR
#define GBR regs.GBR
#define VBR regs.VBR
#define MAC regs.MAC
#define MACL regs.MACL
#define MACH regs.MACH
#define CCR regs.CCR
#define SR regs.SR
#define PPC regs.PPC
#define PPM regs.PPM
#define ET regs.ET
#define ID regs.ID
#include "sh7604/sh7604.cpp"
#include "exceptions.cpp"
#include "instruction.cpp"
#include "instructions.cpp"
#include "cached.cpp"
//#include "recompiler.cpp"
#include "serialization.cpp"
#include "disassembler.cpp"
@@ -30,6 +42,7 @@ auto SH2::power(bool reset) -> void {
SR.M = undefined;
PPC = 0;
PPM = Branch::Step;
ET = 0;
ID = 0;
exceptions = !reset ? ResetCold : ResetWarm;
@@ -47,10 +60,29 @@ auto SH2::power(bool reset) -> void {
cache.power();
if constexpr(Accuracy::Recompiler) {
auto buffer = ares::Memory::FixedAllocator::get().acquire(512_MiB);
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
recompiler.reset();
}
}
#undef SP
#undef R
#undef PC
#undef PR
#undef GBR
#undef VBR
#undef MAC
#undef MACL
#undef MACH
#undef CCR
#undef SR
#undef PPC
#undef PPM
#undef ET
#undef ID
//#include "cached.cpp"
#include "recompiler.cpp"
}
+26 -37
View File
@@ -1,6 +1,6 @@
#pragma once
#include <nall/recompiler/amd64/amd64.hpp>
#include <nall/recompiler/generic/generic.hpp>
//Hitachi SH-2
@@ -16,7 +16,7 @@ struct SH2 {
virtual auto busWriteLong(u32 address, u32 data) -> void = 0;
auto inDelaySlot() const -> bool {
return PPM != Branch::Step;
return regs.PPM != Branch::Step;
}
//sh2.cpp
@@ -39,7 +39,7 @@ struct SH2 {
auto jump(u32 pc) -> void;
auto branch(u32 pc) -> void;
auto delaySlot(u32 pc) -> void;
auto instructionEpilogue() -> bool;
auto instructionEpilogue() -> s32;
auto instruction() -> void;
auto execute(u16 opcode) -> void;
@@ -220,21 +220,23 @@ struct SH2 {
u32 M;
};
u32 R[16]; //general purpose registers
u32 PC; //program counter
u32 PR; //procedure register
u32 GBR; //global base register
u32 VBR; //vector base register
union {
u64 MAC; //multiply-and-accumulate register
struct { u32 order_msb2(MACH, MACL); };
};
u32 CCR; //clock counter register
S32 SR; //status register
u32 PPC; //program counter for delay slots
u32 PPM; //delay slot mode
u32 ET; //exception triggered flag
u32 ID; //interrupts disabled flag
struct Registers {
u32 R[16]; //general purpose registers
u32 PC; //program counter
u32 PR; //procedure register
u32 GBR; //global base register
u32 VBR; //vector base register
union {
u64 MAC; //multiply-and-accumulate register
struct { u32 order_msb2(MACH, MACL); };
};
u32 CCR; //clock counter register
S32 SR; //status register
u32 PPC; //program counter for delay slots
u32 PPM; //delay slot mode
u32 ET; //exception triggered flag
u32 ID; //interrupts disabled flag
} regs;
enum : u32 {
ResetCold = 1 << 0,
@@ -244,14 +246,13 @@ struct SH2 {
};
u32 exceptions = 0; //delayed exception flags
struct Recompiler : recompiler::amd64 {
using recompiler::amd64::call;
struct Recompiler : recompiler::generic {
SH2& self;
Recompiler(SH2& self) : self(self) {}
Recompiler(SH2& self) : self(self), generic(allocator) {}
struct Block {
auto execute(SH2& self) -> void {
((void (*)(u32*, SH2*))code)(&self.R[0], &self);
((void (*)(SH2*, Registers*))code)(&self, &self.regs);
}
u8* code;
@@ -265,25 +266,13 @@ struct SH2 {
for(u32 index : range(1 << 24)) pools[index] = nullptr;
}
auto invalidate(u32 address) -> void {
pool(address)->blocks[address >> 1 & 0x7f] = nullptr;
}
auto invalidate(u32 address) -> void;
auto pool(u32 address) -> Pool*;
auto block(u32 address) -> Block*;
auto emit(u32 address) -> Block*;
auto emitInstruction(u16 opcode) -> bool;
template<typename V, typename... P>
alwaysinline auto call(V (SH2::*function)(P...)) -> void {
static_assert(sizeof...(P) <= 5);
if constexpr(ABI::Windows) {
if constexpr(sizeof...(P) >= 5) mov(dis8(rsp, 0x28), ra5);
if constexpr(sizeof...(P) >= 4) mov(dis8(rsp, 0x20), ra4);
}
mov(ra0, rbp);
call(imm64{function}, rax);
}
auto getSR(reg dst) -> void;
auto setSR(reg src) -> void;
bump_allocator allocator;
Pool* pools[1 << 24];
@@ -22,6 +22,13 @@ print("dreq1\n");
}
}
if(self->wdt.pendingIRQ) {
if(self->SR.I < ipra.wdtip) {
self->interrupt(ipra.wdtip, vcrwdt.witv);
self->wdt.pendingIRQ = 0;
}
}
if(self->sci.pendingTransmitEmptyIRQ) {
if(self->SR.I < iprb.sciip) {
self->interrupt(iprb.sciip, vcrb.stxv);
+16 -7
View File
@@ -675,17 +675,26 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
dmac.drcr[1] = data.bit(0,1);
return;
//WTCSR: watchdog timer control/status register
//WTCSR/WTCNT select
case 0xffff'fe80:
wdt.wtcsr.cks = data.bit(0,2);
wdt.wtcsr.tme = data.bit(5);
wdt.wtcsr.wtit = data.bit(6);
wdt.wtcsr.ovf &= data.bit(7);
wdt.select = data.bit(0,7);
return;
//WTCNT: watchdog timer counter
//WTCSR/WTCNT data
case 0xffff'fe81:
wdt.wtcnt = data.bit(0,7);
//WTCSR: watchdog timer control/status register
if(wdt.select == 0xa5) {
wdt.wtcsr.cks = data.bit(0,2);
wdt.wtcsr.tme = data.bit(5);
wdt.wtcsr.wtit = data.bit(6);
wdt.wtcsr.ovf &= data.bit(7);
if(!wdt.wtcsr.tme) wdt.wtcnt = 0;
}
//WTCNT: watchdog timer counter
if(wdt.select == 0x5a) {
wdt.wtcnt = data.bit(0,7);
}
return;
//RSTCSR: reset control/status register
+10 -2
View File
@@ -112,8 +112,8 @@
n7 fovv; //FRT overflow interrupt vector number
} vcrd;
struct VCRWDT { //vector number setting register WDT
n4 bcmv; //BSC compare match interrupt vector number
n4 witv; //WDT interval interrupt vector number
n7 bcmv; //BSC compare match interrupt vector number
n7 witv; //WDT interval interrupt vector number
} vcrwdt;
} intc;
@@ -210,6 +210,9 @@
//watchdog timer
struct WDT {
//timer.cpp
auto run() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
@@ -225,6 +228,11 @@
n1 wovf; //watchdog timer overflow flag
} rstcsr;
n8 wtcnt; //watchdog timer counter
//internal
n32 counter;
n1 pendingIRQ;
n8 select;
} wdt;
//user break controller
@@ -26,3 +26,18 @@ auto SH2::FRT::run() -> void {
}
}
}
auto SH2::WDT::run() -> void {
static constexpr u32 frequencies[8] = {2, 64, 128, 256, 512, 1024, 2096, 8192};
if(!wtcsr.tme) return;
if(wtcsr.wtit) return; // Watchdog mode currently unsupported
if(++counter >= frequencies[wtcsr.cks]) {
counter -= frequencies[wtcsr.cks];
if(++wtcnt == 0) {
wtcsr.ovf = 1;
pendingIRQ = 1;
}
}
}
+19 -1
View File
@@ -14,7 +14,25 @@ auto TMS9918::Background::run(n8 hoffset, n8 voffset) -> void {
}
auto TMS9918::Background::text1(n8 hoffset, n8 voffset) -> void {
debug(unimplemented, "[TMS9918::Background::text1]");
// Offset by 6px and blank the last 10px to centre the display
i32 x = hoffset - 6;
if (x < 0) return;
if (hoffset > 246) return;
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
nameAddress.bit(10,13) = io.nameTableAddress;
n8 pattern = self.vram.read(nameAddress);
n14 patternAddress;
patternAddress.bit( 0, 2) = voffset.bit(0,2);
patternAddress.bit( 3,10) = pattern;
patternAddress.bit(11,13) = io.patternTableAddress;
n3 index = 7 - (x % 6);
if(self.vram.read(patternAddress).bit(index)) {
output.color = self.dac.io.colorForeground;
}
}
auto TMS9918::Background::graphics1(n8 hoffset, n8 voffset) -> void {
+3
View File
@@ -32,6 +32,9 @@ auto TMS9918::Sprite::setup(n8 voffset) -> void {
}
auto TMS9918::Sprite::run(n8 hoffset, n8 voffset) -> void {
// Text mode does not support sprites
if (self.videoMode() == 1) return;
output = {};
n4 color;
+64
View File
@@ -0,0 +1,64 @@
struct Bandai74161 : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-74161" ) return new Bandai74161(Revision::B74161);
if(id == "BANDAI-74161A") return new Bandai74161(Revision::B74161A);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
B74161,
B74161A,
} revision;
Bandai74161(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
mirror = pak->attribute("mirror") == "vertical";
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n3 bank = (address < 0xc000 ? programBank : (n3)0x7);
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
characterBank = data.bit(0,3);
programBank = data.bit(4,6);
nametableBank = data.bit(7);
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::B74161A) {
return nametableBank << 10 | (n10)address;
}
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(characterBank << 13 | (n13)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
s(nametableBank);
}
n1 mirror;
n3 programBank;
n4 characterBank;
n1 nametableBank;
};
+35 -78
View File
@@ -6,123 +6,80 @@ struct BandaiFCG : Interface {
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
M24C eeprom;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
if(auto fp = pak->read("save.eeprom")) {
eeprom.load(M24C::Type::X24C01);
fp->read({eeprom.memory, eeprom.size()});
}
}
auto save() -> void override {
Interface::save(characterRAM, "save.ram");
if(auto fp = pak->write("save.eeprom")) {
fp->write({eeprom.memory, eeprom.size()});
}
}
auto main() -> void override {
if(irqCounterEnable) {
if(irqEnable) {
if(--irqCounter == 0xffff) {
cpu.irqLine(1);
irqCounterEnable = false;
irqEnable = false;
}
}
tick();
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | address & 0x03ff;
case 1: return address >> 1 & 0x0400 | address & 0x03ff;
case 2: return 0x0000 | address & 0x03ff;
case 3: return 0x0400 | address & 0x03ff;
}
unreachable;
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address >= 0x6000 && address <= 0x7fff) {
data.bit(4) = eeprom.read();
return data;
}
if(address & 0x8000) {
n1 region = bool(address & 0x4000);
n4 bank = (region == 0 ? programBank : (n4)0x0f);
return programROM.read(bank << 14 | (n14)address);
}
return data;
if(address < 0x8000) return data;
n4 bank = (address < 0xc000 ? programBank : (n4)0x0f);
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x6000) {
switch(address & 0xf) {
case 0x0: characterBank[0] = data; break;
case 0x1: characterBank[1] = data; break;
case 0x2: characterBank[2] = data; break;
case 0x3: characterBank[3] = data; break;
case 0x4: characterBank[4] = data; break;
case 0x5: characterBank[5] = data; break;
case 0x6: characterBank[6] = data; break;
case 0x7: characterBank[7] = data; break;
case 0x8: programBank = data.bit(0,3); break;
case 0x9: mirror = data.bit(0,1); break;
case 0xa:
cpu.irqLine(0);
irqCounterEnable = data.bit(0);
irqCounter = irqLatch;
break;
case 0xb: irqLatch.byte(0) = data; break;
case 0xc: irqLatch.byte(1) = data; break;
case 0xd:
eeprom.clock = data.bit(5);
eeprom.data = data.bit(6);
eeprom.write();
break;
}
switch(address & 0xe00f) {
case 0x6000: characterBank[0] = data; break;
case 0x6001: characterBank[1] = data; break;
case 0x6002: characterBank[2] = data; break;
case 0x6003: characterBank[3] = data; break;
case 0x6004: characterBank[4] = data; break;
case 0x6005: characterBank[5] = data; break;
case 0x6006: characterBank[6] = data; break;
case 0x6007: characterBank[7] = data; break;
case 0x6008: programBank = data.bit(0,3); break;
case 0x6009: mirror = data.bit(0,1); break;
case 0x600a:
irqEnable = data.bit(0);
cpu.irqLine(0);
break;
case 0x600b: irqCounter.byte(0) = data; break;
case 0x600c: irqCounter.byte(1) = data; break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | (n10)address;
case 1: return address >> 1 & 0x0400 | (n10)address;
case 2: return 0x0000 | (n10)address;
case 3: return 0x0400 | (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
address = characterBank[address >> 10] << 10 | (n10)address;
if(characterROM) return characterROM.read(address);
if(characterRAM) return characterRAM.read(address);
return data;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
address = characterBank[address >> 10] << 10 | (n10)address;
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
eeprom.power();
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(eeprom);
s(characterBank);
s(programBank);
s(mirror);
s(irqCounterEnable);
s(irqEnable);
s(irqCounter);
s(irqLatch);
}
n8 characterBank[8];
n4 programBank;
n2 mirror;
n1 irqCounterEnable;
n1 irqEnable;
n16 irqCounter;
n16 irqLatch;
};
@@ -0,0 +1,69 @@
struct BandaiKaraoke : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-KARAOKE") return new BandaiKaraoke;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> optionROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(optionROM, "option.rom");
Interface::load(characterRAM, "character.ram");
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000) {
data.bit(0) = 1; //A button
data.bit(1) = 1; //B button
//data.bit(2) = 1; //1-bit ADC
return data;
}
if(address < 0xc000) {
if(!optionEnable) return programROM.read(programBank << 14 | (n14)address);
if(optionROM) return optionROM.read(programBank << 14 | (n14)address);
return data;
}
return programROM.read(0xf << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,3);
optionEnable = !data.bit(4);
mirror = data.bit(5);
}
auto addressCIRAM(n32 address) -> n32 {
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterRAM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
return characterRAM.write(address, data);
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(programBank);
s(optionEnable);
s(mirror);
}
n4 programBank;
n1 optionEnable;
n1 mirror;
};
+139
View File
@@ -0,0 +1,139 @@
struct BandaiLZ93D50 : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-LZ93D50") return new BandaiLZ93D50;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
M24C eeprom;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
if(auto fp = pak->read("save.eeprom")) {
if(fp->size() == 128) eeprom.load(M24C::Type::X24C01);
if(fp->size() == 256) eeprom.load(M24C::Type::M24C02);
if(eeprom) fp->read({eeprom.memory, eeprom.size()});
}
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
Interface::save(characterRAM, "character.ram");
if(auto fp = pak->write("save.eeprom")) {
if(eeprom) fp->write({eeprom.memory, eeprom.size()});
}
}
auto main() -> void override {
if(irqEnable) {
if(--irqCounter == 0xffff) {
cpu.irqLine(1);
irqEnable = false;
}
}
tick();
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000) {
if(programRAM && ramEnable) return programRAM.read((n13)address);
if(eeprom) data.bit(4) = eeprom.read();
return data;
}
n5 bank = (address < 0xc000 ? (n5)programBank : (n5)0xf);
if(characterRAM) bank.bit(4) = bool(programOuterBank);
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000) {
if(programRAM && ramEnable) return programRAM.write((n13)address, data);
}
switch(address & 0x800f) {
case 0x8000: characterBank[0] = data; programOuterBank.bit(0) = data.bit(0); break;
case 0x8001: characterBank[1] = data; programOuterBank.bit(1) = data.bit(0); break;
case 0x8002: characterBank[2] = data; programOuterBank.bit(2) = data.bit(0); break;
case 0x8003: characterBank[3] = data; programOuterBank.bit(3) = data.bit(0); break;
case 0x8004: characterBank[4] = data; break;
case 0x8005: characterBank[5] = data; break;
case 0x8006: characterBank[6] = data; break;
case 0x8007: characterBank[7] = data; break;
case 0x8008: programBank = data.bit(0,3); break;
case 0x8009: mirror = data.bit(0,1); break;
case 0x800a:
irqEnable = data.bit(0);
irqCounter = irqLatch;
cpu.irqLine(0);
break;
case 0x800b: irqLatch.byte(0) = data; break;
case 0x800c: irqLatch.byte(1) = data; break;
case 0x800d:
if(eeprom) {
eeprom.clock = data.bit(5);
eeprom.data = data.bit(6);
eeprom.write();
}
ramEnable = data.bit(5);
break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | (n10)address;
case 1: return address >> 1 & 0x0400 | (n10)address;
case 2: return 0x0000 | (n10)address;
case 3: return 0x0400 | (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
if(characterRAM) return characterRAM.read(address);
address = characterBank[address >> 10] << 10 | (n10)address;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
eeprom.power();
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(characterRAM);
s(eeprom);
s(characterBank);
s(programBank);
s(programOuterBank);
s(mirror);
s(ramEnable);
s(irqEnable);
s(irqCounter);
s(irqLatch);
}
n8 characterBank[8];
n4 programBank;
n4 programOuterBank;
n2 mirror;
n1 ramEnable;
n1 irqEnable;
n16 irqCounter;
n16 irqLatch;
};
+76
View File
@@ -0,0 +1,76 @@
struct BandaiOeka : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-OEKA") return new BandaiOeka;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto addressTest(n32 address) -> void {
if((characterAddress >> 12) != 2 && (address >> 12) == 2) {
characterBank.bit(0,1) = address >> 8 & 3;
}
characterAddress = address;
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address == 0x2006) {
if(++characterLatch) addressTest(data << 8);
}
if(address < 0x8000) return;
programBank = data.bit(0,1);
characterBank.bit(2) = data.bit(2);
}
auto addressCHR(n32 address) const -> n32 {
n3 bank = (address < 0x1000) ? characterBank : n3(characterBank & 4 | 3);
return bank << 12 | (n12)address;
}
auto addressCIRAM(n32 address) const -> n32 {
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
addressTest(address);
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterRAM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
addressTest(address);
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
return characterRAM.write(addressCHR(address), data);
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(mirror);
s(programBank);
s(characterBank);
s(characterLatch);
s(characterAddress);
}
n1 mirror;
n2 programBank;
n3 characterBank;
n1 characterLatch;
n32 characterAddress;
};
+44 -6
View File
@@ -1,8 +1,17 @@
namespace Board {
#include "bandai-74161.cpp"
#include "bandai-fcg.cpp"
#include "jaleco-jf.cpp"
#include "jaleco-jf11-jf14.cpp"
#include "bandai-karaoke.cpp"
#include "bandai-lz93d50.cpp"
#include "bandai-oeka.cpp"
#include "colordreams-74x377.cpp"
#include "gtrom.cpp"
#include "jaleco-jf05.cpp"
#include "jaleco-jf11.cpp"
#include "jaleco-jf16.cpp"
#include "jaleco-jf17.cpp"
#include "jaleco-jf23.cpp"
#include "konami-vrc1.cpp"
#include "konami-vrc2.cpp"
#include "konami-vrc3.cpp"
@@ -13,7 +22,6 @@ namespace Board {
#include "hvc-axrom.cpp"
#include "hvc-bnrom.cpp"
#include "hvc-cnrom.cpp"
#include "hvc-dxrom.cpp"
#include "hvc-exrom.cpp"
#include "hvc-fmr.cpp"
#include "hvc-fxrom.cpp"
@@ -24,20 +32,36 @@ namespace Board {
#include "hvc-sxrom.cpp"
#include "hvc-txrom.cpp"
#include "hvc-uxrom.cpp"
#include "irem-g101.cpp"
#include "irem-h3001.cpp"
#include "irem-lrog017.cpp"
#include "irem-tam-s1.cpp"
#include "mlt-action52.cpp"
#include "namco-118.cpp"
#include "namco-163.cpp"
#include "namco-340.cpp"
#include "sunsoft-1.cpp"
#include "sunsoft-2.cpp"
#include "sunsoft-3.cpp"
#include "sunsoft-4.cpp"
#include "sunsoft-5b.cpp"
#include "taito-tc0190.cpp"
#include "taito-tc0690.cpp"
#include "taito-x1-005.cpp"
#include "taito-x1-017.cpp"
auto Interface::create(string board) -> Interface* {
Interface* p = nullptr;
if(!p) p = Bandai74161::create(board);
if(!p) p = BandaiFCG::create(board);
if(!p) p = BandaiKaraoke::create(board);
if(!p) p = BandaiLZ93D50::create(board);
if(!p) p = BandaiOeka::create(board);
if(!p) p = ColorDreams_74x377::create(board);
if(!p) p = GTROM::create(board);
if(!p) p = HVC_AxROM::create(board);
if(!p) p = HVC_BNROM::create(board);
if(!p) p = HVC_CNROM::create(board);
if(!p) p = HVC_DxROM::create(board);
if(!p) p = HVC_ExROM::create(board);
if(!p) p = HVC_FMR::create(board);
if(!p) p = HVC_FxROM::create(board);
@@ -48,9 +72,15 @@ auto Interface::create(string board) -> Interface* {
if(!p) p = HVC_SxROM::create(board);
if(!p) p = HVC_TxROM::create(board);
if(!p) p = HVC_UxROM::create(board);
if(!p) p = IremG101::create(board);
if(!p) p = IremH3001::create(board);
if(!p) p = IremLROG017::create(board);
if(!p) p = IremTAMS1::create(board);
if(!p) p = JalecoJF::create(board);
if(!p) p = Jaleco_JF11_JF14::create(board);
if(!p) p = JalecoJF05::create(board);
if(!p) p = JalecoJF11::create(board);
if(!p) p = JalecoJF16::create(board);
if(!p) p = JalecoJF17::create(board);
if(!p) p = JalecoJF23::create(board);
if(!p) p = KonamiVRC1::create(board);
if(!p) p = KonamiVRC2::create(board);
if(!p) p = KonamiVRC3::create(board);
@@ -58,11 +88,19 @@ auto Interface::create(string board) -> Interface* {
if(!p) p = KonamiVRC5::create(board);
if(!p) p = KonamiVRC6::create(board);
if(!p) p = KonamiVRC7::create(board);
if(!p) p = MltAction52::create(board);
if(!p) p = Namco118::create(board);
if(!p) p = Namco163::create(board);
if(!p) p = Namco340::create(board);
if(!p) p = Sunsoft1::create(board);
if(!p) p = Sunsoft2::create(board);
if(!p) p = Sunsoft3::create(board);
if(!p) p = Sunsoft4::create(board);
if(!p) p = Sunsoft5B::create(board);
if(!p) p = TaitoTC0190::create(board);
if(!p) p = TaitoTC0690::create(board);
if(!p) p = TaitoX1005::create(board);
if(!p) p = TaitoX1017::create(board);
if(!p) p = new Interface;
return p;
}
@@ -0,0 +1,66 @@
struct ColorDreams_74x377 : Interface {
static auto create(string id) -> Interface* {
if(id == "COLORDREAMS-74*377") return new ColorDreams_74x377();
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,1);
characterBank = data.bit(4,7);
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
address = characterBank << 13 | (n13)address;
if(characterROM) return characterROM.read(address);
if(characterRAM) return characterRAM.read(address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
address = characterBank << 13 | (n13)address;
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(programBank);
s(characterBank);
s(mirror);
}
n2 programBank;
n4 characterBank;
n1 mirror;
};
+63
View File
@@ -0,0 +1,63 @@
struct GTROM : Interface {
static auto create(string id) -> Interface* {
if(id == "GTROM" ) return new GTROM();
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> characterRAM;
Memory::Writable<n8> videoRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterRAM, "character.ram");
videoRAM.allocate(16_KiB);
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x5000 && address <= 0x5fff) {
programBank = data.bit(0, 3);
characterBank = data.bit(4);
videoBank = data.bit(5);
return;
}
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return videoRAM.read(videoBank << 13 | (n13)address);
if(characterRAM) return characterRAM.read(characterBank << 13 | (n13)address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return videoRAM.write(videoBank << 13 | (n13)address, data);
return characterRAM.write(characterBank << 13 | (n13)address, data);
}
auto power() -> void override {
programBank = 0;
characterBank = 0;
videoBank = 0;
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(videoRAM);
s(programBank);
s(characterBank);
s(videoBank);
}
n4 programBank;
n1 characterBank;
n1 videoBank;
};
+2 -2
View File
@@ -26,7 +26,7 @@ struct HVC_BNROM : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,1);
programBank = data;
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -57,5 +57,5 @@ struct HVC_BNROM : Interface {
}
n1 mirror; //0 = horizontal, 1 = vertical
n2 programBank;
n8 programBank;
};
+31 -10
View File
@@ -1,6 +1,8 @@
struct HVC_CNROM : Interface {
static auto create(string id) -> Interface* {
if(id == "HVC-CNROM") return new HVC_CNROM;
if(id == "HVC-CNROM" ) return new HVC_CNROM(Revision::CNROM);
if(id == "HVC-CNROM-SEC") return new HVC_CNROM(Revision::CNROMS);
if(id == "HVC-CPROM" ) return new HVC_CNROM(Revision::CPROM);
return nullptr;
}
@@ -8,11 +10,20 @@ struct HVC_CNROM : Interface {
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
enum class Revision : u32 {
CNROM,
CNROMS,
CPROM,
} revision;
HVC_CNROM(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
key = pak->attribute("chip/key").natural();
}
auto save() -> void override {
@@ -26,7 +37,8 @@ struct HVC_CNROM : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
characterBank = data.bit(0,1);
characterBank = data & programROM.read((n15)address);
characterEnable = (data == key);
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -34,10 +46,16 @@ struct HVC_CNROM : Interface {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
if(revision == Revision::CPROM) {
n2 bank = (address < 0x1000) ? (n2)0 : characterBank;
return characterRAM.read(bank << 12 | (n12)address);
}
if(revision == Revision::CNROMS) {
if(!characterEnable) return 0xff;
return characterROM.read((n13)address);
}
address = characterBank << 13 | (n13)address;
if(characterROM) return characterROM.read(address);
if(characterRAM) return characterRAM.read(address);
return data;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
@@ -45,19 +63,22 @@ struct HVC_CNROM : Interface {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
address = characterBank << 13 | (n13)address;
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
if(revision == Revision::CPROM) {
n2 bank = (address < 0x1000) ? (n2)0 : characterBank;
return characterRAM.write(bank << 12 | (n12)address, data);
}
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(mirror);
s(key);
s(characterBank);
s(characterEnable);
}
n1 mirror; //0 = horizontal, 1 = vertical
n8 key;
n2 characterBank;
n1 characterEnable;
};
-94
View File
@@ -1,94 +0,0 @@
struct HVC_DxROM : Interface {
static auto create(string id) -> Interface* {
if(id == "HVC-DEROM" ) return new HVC_DxROM(Revision::DEROM);
if(id == "HVC-DE1ROM") return new HVC_DxROM(Revision::DE1ROM);
if(id == "HVC-DRROM" ) return new HVC_DxROM(Revision::DRROM);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterCIRAM;
enum class Revision : u32 {
DEROM,
DE1ROM,
DRROM,
} revision;
HVC_DxROM(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterCIRAM, "character.ram");
}
auto save() -> void override {
Interface::save(characterCIRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
switch(address) {
case 0x8000 ... 0x9fff: return programROM.read(programBank[0] << 13 | (n13)address);
case 0xa000 ... 0xbfff: return programROM.read(programBank[1] << 13 | (n13)address);
case 0xc000 ... 0xdfff: return programROM.read(0x0e << 13 | (n13)address);
case 0xe000 ... 0xffff: return programROM.read(0x0f << 13 | (n13)address);
}
return data;
}
auto writePRG(n32 address, n8 data) -> void override {
switch(address & 0xe001) {
case 0x8000:
bankSelect = data.bit(0,2);
break;
case 0x8001:
switch(bankSelect) {
case 0: characterBank[0] = data & ~1; break;
case 1: characterBank[1] = data & ~1; break;
case 2: characterBank[2] = data; break;
case 3: characterBank[3] = data; break;
case 4: characterBank[4] = data; break;
case 5: characterBank[5] = data; break;
case 6: programBank[0] = data; break;
case 7: programBank[1] = data; break;
}
break;
}
}
auto readCHR(n32 address, n8 data) -> n8 override {
switch(address) {
case 0x0000 ... 0x07ff: return characterROM.read(characterBank[0] << 10 | (n11)address);
case 0x0800 ... 0x0fff: return characterROM.read(characterBank[1] << 10 | (n11)address);
case 0x1000 ... 0x13ff: return characterROM.read(characterBank[2] << 10 | (n10)address);
case 0x1400 ... 0x17ff: return characterROM.read(characterBank[3] << 10 | (n10)address);
case 0x1800 ... 0x1bff: return characterROM.read(characterBank[4] << 10 | (n10)address);
case 0x1c00 ... 0x1fff: return characterROM.read(characterBank[5] << 10 | (n10)address);
case 0x2000 ... 0x27ff: return ppu.readCIRAM((n11)address);
case 0x2800 ... 0x2fff: return characterCIRAM.read((n11)address);
}
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
switch(address) {
case 0x2000 ... 0x27ff: return ppu.writeCIRAM((n11)address, data);
case 0x2800 ... 0x2fff: return characterCIRAM.write((n11)address, data);
}
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
s(bankSelect);
s(programBank);
s(characterBank);
}
n3 bankSelect;
n4 programBank[2];
n6 characterBank[6];
};
+7 -3
View File
@@ -31,8 +31,9 @@ struct HVC_HKROM : Interface { //MMC6
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
if(irqDelay == 0) {
if(irqCounter == 0) {
irqCounter = irqLatch;
} else if(--irqCounter == 0) {
irqCounter = irqLatch + 1;
}
if(--irqCounter == 0) {
if(irqEnable) irqLine = 1;
}
}
@@ -64,6 +65,7 @@ struct HVC_HKROM : Interface { //MMC6
}
auto writePRG(n32 address, n8 data) -> void override {
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
if(address < 0x7000) return;
if(address < 0x8000) {
@@ -179,6 +181,7 @@ struct HVC_HKROM : Interface { //MMC6
s(irqEnable);
s(irqDelay);
s(irqLine);
s(characterLatch);
s(characterAddress);
}
@@ -194,7 +197,8 @@ struct HVC_HKROM : Interface { //MMC6
n8 irqLatch;
n8 irqCounter;
n1 irqEnable;
n1 irqDelay;
n8 irqDelay;
n1 irqLine;
n1 characterLatch;
n16 characterAddress;
};
+11 -3
View File
@@ -69,7 +69,13 @@ struct HVC_SxROM : Interface { //MMC1
HVC_SxROM(Revision revision) : revision(revision) {}
auto load() -> void override {
auto chip = pak->attribute("chip");
chipRevision = ChipRevision::MMC1B2;
if(chip == "MMC1" ) chipRevision = ChipRevision::MMC1;
if(chip == "MMC1A" ) chipRevision = ChipRevision::MMC1A;
if(chip == "MMC1B1") chipRevision = ChipRevision::MMC1B1;
if(chip == "MMC1B3") chipRevision = ChipRevision::MMC1B3;
if(chip == "MMC1C" ) chipRevision = ChipRevision::MMC1C;
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
@@ -130,7 +136,7 @@ struct HVC_SxROM : Interface { //MMC1
if(revision == Revision::SNROM) {
if(characterBank[0].bit(4)) return data;
}
if(ramDisable) return 0x00;
if(!programRAM || ramDisable) return 0x00;
return programRAM.read(addressProgramRAM(address));
}
@@ -146,7 +152,7 @@ struct HVC_SxROM : Interface { //MMC1
if(revision == Revision::SNROM) {
if(characterBank[0].bit(4)) return;
}
if(ramDisable) return;
if(!programRAM || ramDisable) return;
return programRAM.write(addressProgramRAM(address), data);
}
@@ -180,7 +186,9 @@ struct HVC_SxROM : Interface { //MMC1
break;
case 3:
programBank = shiftValue.bit(0,3);
ramDisable = shiftValue.bit(4);
if(chipRevision > ChipRevision::MMC1A) {
ramDisable = shiftValue.bit(4);
}
break;
}
}
+44 -6
View File
@@ -65,8 +65,9 @@ struct HVC_TxROM : Interface { //MMC3
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
if(irqDelay == 0) {
if(irqCounter == 0) {
irqCounter = irqLatch;
} else if(--irqCounter == 0) {
irqCounter = irqLatch + 1;
}
if(--irqCounter == 0) {
if(irqEnable) irqLine = 1;
}
}
@@ -79,7 +80,7 @@ struct HVC_TxROM : Interface { //MMC3
if(address < 0x6000) return data;
if(address < 0x8000) {
if(!ramEnable) return data;
if(!ramEnable || !programRAM) return data;
return programRAM.read((n13)address);
}
@@ -95,10 +96,11 @@ struct HVC_TxROM : Interface { //MMC3
}
auto writePRG(n32 address, n8 data) -> void override {
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
if(address < 0x6000) return;
if(address < 0x8000) {
if(!ramEnable || !ramWritable) return;
if(!ramEnable || !ramWritable || !programRAM) return;
return programRAM.write((n13)address, data);
}
@@ -163,20 +165,54 @@ struct HVC_TxROM : Interface { //MMC3
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::TKSROM || revision == Revision::TLSROM) {
address.bit(12) = 0;
if(characterMode == 0) {
if(address <= 0x27ff) return characterBank[0].bit(7) << 10 | (n10)address;
if(address <= 0x2fff) return characterBank[1].bit(7) << 10 | (n10)address;
} else {
if(address <= 0x23ff) return characterBank[2].bit(7) << 10 | (n10)address;
if(address <= 0x27ff) return characterBank[3].bit(7) << 10 | (n10)address;
if(address <= 0x2bff) return characterBank[4].bit(7) << 10 | (n10)address;
if(address <= 0x2fff) return characterBank[5].bit(7) << 10 | (n10)address;
}
unreachable;
}
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
irqTest(address);
if(revision == Revision::TVROM) {
if(address < 0x2000) return characterROM.read(addressCHR(address));
address.bit(12) = 0;
if(address < 0x2800) return ppu.readCIRAM((n11)address);
return characterRAM.read((n11)address);
}
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
if(characterROM) return characterROM.read(addressCHR(address));
if(revision == Revision::TQROM) {
address = addressCHR(address);
if(address < 0x10000) return characterROM.read(address);
return characterRAM.read(address);
}
if(characterRAM) return characterRAM.read(addressCHR(address));
return data;
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
irqTest(address);
if(revision == Revision::TVROM) {
if(address < 0x2000) return;
address.bit(12) = 0;
if(address < 0x2800) return ppu.writeCIRAM((n11)address, data);
return characterRAM.write((n11)address, data);
}
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
if(revision == Revision::TQROM) {
address = addressCHR(address);
if(address < 0x10000) return;
return characterRAM.write(address, data);
}
if(characterRAM) return characterRAM.write(addressCHR(address), data);
}
@@ -201,6 +237,7 @@ struct HVC_TxROM : Interface { //MMC3
s(irqEnable);
s(irqDelay);
s(irqLine);
s(characterLatch);
s(characterAddress);
}
@@ -217,5 +254,6 @@ struct HVC_TxROM : Interface { //MMC3
n1 irqEnable;
n8 irqDelay;
n1 irqLine;
n1 characterLatch;
n16 characterAddress;
};
+14 -5
View File
@@ -1,7 +1,9 @@
struct HVC_UxROM : Interface {
static auto create(string id) -> Interface* {
if(id == "HVC-UNROM") return new HVC_UxROM(Revision::UNROM);
if(id == "HVC-UOROM") return new HVC_UxROM(Revision::UOROM);
if(id == "HVC-UNROM" ) return new HVC_UxROM(Revision::UNROM);
if(id == "HVC-UNROMA") return new HVC_UxROM(Revision::UNROMA);
if(id == "HVC-UN1ROM") return new HVC_UxROM(Revision::UN1ROM);
if(id == "HVC-UOROM" ) return new HVC_UxROM(Revision::UOROM);
return nullptr;
}
@@ -11,6 +13,8 @@ struct HVC_UxROM : Interface {
enum class Revision : u32 {
UNROM,
UNROMA,
UN1ROM,
UOROM,
} revision;
@@ -29,13 +33,18 @@ struct HVC_UxROM : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n4 bank = (address < 0xc000 ? programBank : (n4)0xf);
n8 bank;
switch(address >> 14 & 1) {
case 0: bank = (revision == Revision::UNROMA ? (n8)0x00 : programBank); break;
case 1: bank = (revision == Revision::UNROMA ? programBank : (n8)0xff); break;
}
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,3);
programBank = data;
if(revision == Revision::UN1ROM) programBank >>= 2;
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -66,5 +75,5 @@ struct HVC_UxROM : Interface {
}
n1 mirror; //0 = horizontal, 1 = vertical
n4 programBank;
n8 programBank;
};
+89
View File
@@ -0,0 +1,89 @@
struct IremG101 : Interface {
static auto create(string id) -> Interface* {
if(id == "IREM-G101" ) return new IremG101(Revision::G101);
if(id == "IREM-G101A") return new IremG101(Revision::G101A);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
G101,
G101A,
} revision;
IremG101(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000 && !programRAM) return data;
if(address < 0x8000) return programRAM.read((n13)address);
n5 bank;
switch(address >> 13 & 3) {
case 0: bank = (programMode == 0 ? programBank[0] : (n5)0x1e); break;
case 1: bank = programBank[1]; break;
case 2: bank = (programMode == 1 ? programBank[0] : (n5)0x1e); break;
case 3: bank = 0x1f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000 && !programRAM) return;
if(address < 0x8000) return programRAM.write((n13)address, data);
switch(address & 0xf000) {
case 0x8000: programBank[0] = data.bit(0,4); break;
case 0xa000: programBank[1] = data.bit(0,4); break;
case 0xb000: characterBank[address & 7] = data; break;
case 0x9000:
if(revision == Revision::G101) {
mirror = data.bit(0);
programMode = data.bit(1);
}
break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::G101A) return (n10)address;
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
n8 bank = characterBank[address >> 10 & 7];
return characterROM.read(bank << 10 | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(programBank);
s(characterBank);
s(programMode);
s(mirror);
}
n5 programBank[2];
n8 characterBank[8];
n1 programMode;
n1 mirror;
};
+104
View File
@@ -0,0 +1,104 @@
struct IremH3001 : Interface {
static auto create(string id) -> Interface* {
if(id == "IREM-H3001") return new IremH3001;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
}
auto main() -> void override {
if(irqEnable) {
if(irqCounter && --irqCounter == 0) irqLine = 1;
}
cpu.irqLine(irqLine);
tick();
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = (programMode == 0 ? programBank[0] : (n6)0x3e); break;
case 1: bank = programBank[1]; break;
case 2: bank = (programMode == 1 ? programBank[0] : (n6)0x3e); break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
switch(address) {
case 0x8000: programBank[0] = data.bit(0,5); break;
case 0xa000: programBank[1] = data.bit(0,5); break;
case 0xb000: characterBank[0] = data; break;
case 0xb001: characterBank[1] = data; break;
case 0xb002: characterBank[2] = data; break;
case 0xb003: characterBank[3] = data; break;
case 0xb004: characterBank[4] = data; break;
case 0xb005: characterBank[5] = data; break;
case 0xb006: characterBank[6] = data; break;
case 0xb007: characterBank[7] = data; break;
case 0x9000: programMode = data.bit(7); break;
case 0x9001: mirror = data.bit(6,7); break;
case 0x9003:
irqEnable = data.bit(7);
irqLine = 0;
break;
case 0x9004:
irqCounter = irqLatch;
irqLine = 0;
break;
case 0x9005: irqLatch = irqLatch.bit(0,7) << 0 | data << 8; break;
case 0x9006: irqLatch = irqLatch.bit(8,15) << 8 | data << 0; break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | (n10)address;
case 1: return (n10)address;
case 2: return address >> 1 & 0x0400 | (n10)address;
case 3: return (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
n8 bank = characterBank[address >> 10 & 7];
return characterROM.read(bank << 10 | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programBank);
s(characterBank);
s(programMode);
s(mirror);
s(irqLatch);
s(irqCounter);
s(irqEnable);
s(irqLine);
}
n6 programBank[2];
n8 characterBank[8];
n1 programMode;
n2 mirror;
n16 irqLatch;
n16 irqCounter;
n1 irqEnable;
n1 irqLine;
};
+61
View File
@@ -0,0 +1,61 @@
struct IremLROG017 : Interface {
static auto create(string id) -> Interface* {
if(id == "IREM-LROG017") return new IremLROG017;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,3);
characterBank = data.bit(4,7);
}
auto addressCHR(n32 address) const -> n32 {
if(address < 0x1000) return 1 << 11 | (n11)address;
if(address < 0x1800) return 2 << 11 | (n11)address;
if(address < 0x2000) return 3 << 11 | (n11)address;
if(address < 0x2800) return 0 << 11 | (n11)address;
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address < 0x0800) return characterROM.read(characterBank << 11 | (n11)address);
if(address < 0x2800) return characterRAM.read(addressCHR(address));
if(address < 0x3000) return ppu.readCIRAM((n11)address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address < 0x0800) return;
if(address < 0x2800) return characterRAM.write(addressCHR(address), data);
if(address < 0x3000) return ppu.writeCIRAM((n11)address, data);
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(programBank);
s(characterBank);
}
n4 programBank;
n4 characterBank;
};
+1 -1
View File
@@ -5,7 +5,7 @@ struct IremTAMS1 : Interface {
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> characterROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Loaded 100 of 638 files, more files were not shown because too many files have changed in this diff. Show more