Compare commits

..
305 Commits
Author SHA1 Message Date
Luke Usher 39025af1fe release ares v136 2024-02-23 17:02:48 +00:00
Luke Usher 5cdefb088d desktop-ui: cleanup resource search paths
As well as simplifying the code a little, this also  fixes an oversight where users could not override application resources on macOS.
2024-02-21 17:49:12 +00:00
Giovanni Bajo d0253599a6 n64: fix corner case in FPU exception handling (#1400)
Ares checked each of the two input arguments to three-ops functions
separately, but this is not correct. Conditions causing unimplemented
operation on either input (sNAN, subnormal) take precedence on
conditions causing invalid operation on either input (qNAN).

After this fix, Ares passes n64-systemtest COP1 stress test.
2024-02-20 23:34:38 +00:00
Luke Usher 1e83da0241 md: implement subchannel processing for mcd (CD+G) 2024-02-19 14:19:56 +00:00
Luke Usher ed0a1c8852 cheats: only create a cheat file when cheats are added
This also cleans up empty cheat files on unload.
2024-02-18 08:41:56 +00:00
Luke Usher dd9c728a12 ares: preliminary cheat support
Adds a cheat editor to Tools->Cheats when a game is loaded.

The cheat editor should work for all systems except N64 and PS1: support for these will come later.

The code has currently only been tested for MS, MD and SFC but it is assumed all the other systems are also functioning; the code paths are the same.

Code format is standard patch codes in the format X:Y where X is a memory address and Y is a value to place there; most emulators also support this format.

You can use multiple codes in a single cheat by separating codes with a + sign.

Cheats that require memory comparisons are not currently supported, only replacements

Cheats are saved alongside the roms, eg "Game.sfc" will create "Game.cheats.bml".
2024-02-16 19:01:57 +00:00
Luke Usher 7ec1e1430c fc: reduce overscan cropping similar to ms 2024-02-15 17:35:47 +00:00
Luke Usher 16379aabff ruby: use PRESENTINTERVAL_ONE rather than GetRasterStatus for VBlank with d3d9 2024-02-15 17:34:30 +00:00
Luke Usher 11f24b47c3 ms: adjust overscan cropping, it was a little too agressive 2024-02-15 17:33:51 +00:00
Luke Usher 32f205975c md: add CD PCM RAM to debug memory viewer/editor 2024-02-14 17:45:50 +00:00
png183 21ca9cf76e gba: timer 0 count-up bit cannot be set (#1392)
The count-up bit for timer 0 cannot be set on hardware ([test
ROM](https://github.com/png183/gba-tests/blob/master/timer/timer_check_count_up_presence.gba)).
This PR implements said behaviour in ares, preventing timer 0 from
incorrectly halting if this bit is written.
2024-02-14 10:14:39 +00:00
Giovanni Bajo d32b3ffe45 n64: revert previous commit (add HLE RDRAM initialization) (#1391)
This is actually still required as we still skip RDRAM initialization
anyway and proprietary IPL3 doesn't initialize the RAM size in this
case.
2024-02-14 10:14:20 +00:00
Luke Usher 5864382c67 ps1: various fixes to GPUSTAT register behavior 2024-02-13 22:34:03 +00:00
Luke Usher f392e4f428 libchdr: disable asm optimisation for zstd
Should fix the Ubuntu build.
2024-02-11 19:51:28 +00:00
Luke Usher d0075978f8 libchdr: rollback lzma to 19.00
This version still exists in the repo for libchdr
22.01 fails compilation on many platforms right now
2024-02-11 19:37:46 +00:00
Luke Usher c1e5777dc1 thirdparty: fix compilation after libchdr update 2024-02-11 19:16:41 +00:00
Luke Usher ec6c9fc330 Merge commit '9bc95db1f0c5591dfbcec23c8e326aa6c2bb3b29' 2024-02-11 19:16:32 +00:00
Luke Usher 9bc95db1f0 Squashed 'thirdparty/libchdr/' changes from 9108f34a8..2648c221c
2648c221c Merge pull request #108 from herbymachine/master
f7729588b Update libchdr_huffman.c
26d27ca49 Merge pull request #106 from zakk4223/zstd
d6f59e748 Add zstd codec

git-subtree-dir: thirdparty/libchdr
git-subtree-split: 2648c221c9ce6b49600ed7d2eb45c9e6e593790f
2024-02-11 19:16:32 +00:00
Luke Usher 6d80a8e940 Merge commit '2a0f9651b976a5431510222e04077fd360415d69' 2024-02-11 19:16:30 +00:00
Luke Usher 2a0f9651b9 Squashed 'thirdparty/sljit/' changes from c1cbaf09e..607158516
607158516 Improve serialize interface
dd73cb873 Implement serialization interface
5d2d7cd59 Improve PPC branch support
8b8b6b442 Improve ARM32 branch support
2cfed9ce4 Improve x86, ARM-T2 branch support
6c75e773c Improve riscv branch support
dfeb8cf68 Add extraction macros for operands
dc52b334e Fix typo (#229)
7a821cd71 Add LoongArch float to integer conversion special cases (#226)
cbde0bea6 regex: allow building with a C++ compiler (#224)
40680870e Fix masked shift on arm32
a0643952a Fix sljit_get_register_index on x86-32
80bdff333 Support temporary registers in sljit_get_register_index
4629814f8 riscv: fix getput_arg and load_addr_to_reg (#223)
a26427a6e define float to integer conversion special cases
42128180d build: make code mostly ANSI C89 compatible (#221)
57c212f58 loongarch: fix FTBFS when building pcre2test as part of PCRE2 (#220)
3faacd431 Optimize certain fop1 operations on x86
4f33321c1 Move float/call/simd tests to separate files
230429241 Add another temp register on s390x
fbf9c47f3 Support access to temporary registers
8c5c50463 Implement simd on s390x
02e76fb14 s390x: fix FTBFS with clang (#219)
0bd5147fc Implement simd2 operations

git-subtree-dir: thirdparty/sljit
git-subtree-split: 6071585164d9a38a8890cfd2722ca9d8492528bb
2024-02-11 19:16:29 +00:00
Giovanni Bajo 51a10b0bd0 n64: avoid HLE-ish memory init (#1388)
Not required anymore, possibly for a long time. IPL3 does that, plus
this creates correctness issues with open source IPL3 which *doesn't*
initialize those memory locations.
2024-02-11 11:09:07 +00:00
png183 7e2f490fd0 gba: improve horizontal sprite mosaic (#1387)
Addresses a few cases where the current horizontal sprite mosaic
implementation is incorrect:

- Transparent pixels should be affected by horizontal sprite mosaic
effects
- If the latched pixel does not have the sprite mosaic effect enabled,
the next pixel should always be latched
- Mosaic offset should not be reset to 0 at the start of a new
horizontal mosaic effect

Accounting for these cases improves visual accuracy of sprite mosaic
effects. This is easily visible in test cases such as
[sprite-hmosaic.gba](https://github.com/nba-emu/hw-test/tree/master/ppu/sprite-hmosaic),
and [Tonc's mosaic demo](https://www.coranac.com/projects/#tonc)
(mos_demo.gba, from tonc-bin.zip v1.4.2).
2024-02-11 11:08:41 +00:00
Luke Usher 2063393f20 sfc: fix SGB2 clocking issue
This fixes an issue where the SGB2 would reconfigure to SGB1 clock speeds when writing to the control port.
2024-02-06 00:15:49 +00:00
Giovanni Bajo 4c15ed753e n64: fix PI DMA implementation to handle a newly discovered behavior (#1385)
I've lately discovered that misaligned PI DMA is also affected by RDRAM
writes that must cross RDRAM row boundaries (2048 bytes). It is obvious
in retrospect that the DMA implementation cannot generate a write burst
across different rows, so its internal buffering must be segmented on
RDRAM row boundaries.

The PI DMA testsuite has been updated to cover this edge case as well:
https://github.com/rasky/n64_pi_dma_test

Ares now passes the updated testsuite in full (timing aside, which is
close but not perfect).
2024-02-05 16:14:07 +00:00
Luke Usher 704fc91b9c desktop-ui: 'hide until ready' for Windows only 2024-02-01 18:58:50 +00:00
Luke Usher 757d74bac0 desktop-ui: hide window until startup completes
This also avoids calling inode::exists on every recent game at load time, reducing initial wait time.
2024-02-01 18:40:45 +00:00
Luke Usher ff36889d47 desktop-ui: exclude rumble inputs from poll timeout
Fixes rumble issues in many games, notably, OOT
2024-01-29 13:12:06 +00:00
Luke Usher 3d10cd34d6 ruby: differentiate devices in SDL2 driver 2024-01-28 21:00:36 +00:00
Luke Usher acaa1cabb5 ares: border and overscan overhaul
This updates most pre-32bit systems to have hardware accurate borders and
aspect ratio where known, reasonable estimates are used where not known.

This also means that PAL games will use the correct PAL aspect rather than
using the NTSC pixel aspect ratio.

All TMS9918 VDP based consoles are thought to be be correct, as are Master
System, Mega Drive, and NES, while SNES and PC-Engine are (educated)
estimates.

The overscan option has been reworked to crop all non-desirable edges of the
screen, including borders and areas that often contain garbage data, previously
this option only cropped the bottom 16 lines of active display.

The reworked overscan is not yet implemented for Atari 2600, PC-Engine, N64 or
PlayStation.
2024-01-28 15:38:47 +00:00
png183 e8f38d15f7 gba: reset prefetch buffer on ROM accesses from CPU (#1377)
According to NanoBoyAdvance author fleroviux, the prefetch buffer should
be reset on any ROM accesses from the CPU or DMA. This PR implements
this behaviour for CPU ROM accesses. This allows ares to pass 1760/2020
of the timing tests in the mGBA test suite (up from 1608/2020).

Allowing DMA ROM accesses to clear the prefetch buffer seems to worsen
timings at the moment, possibly due to some other timing issues that
also come into effect in these situations. I've left comments to make a
note of the issue, so that resetting the prefetch buffer on ROM accesses
from DMA can be implemented once it becomes beneficial to do so.
2024-01-27 11:47:44 +00:00
Luke Usher 527aaefef1 ruby: use wglSwapInterval rather than DwmFlush
Despite GFWL, and other sources recommending the DwmFlush approach, actual users are setting better results the old way.
2024-01-25 16:11:57 +00:00
Dragorn421 a938ef8aa8 Fixup *nix SDL info and makefile indentation (#1376)
The readme references `libsdl2-0.0` which does not exist:
https://packages.ubuntu.com/search?suite=default&section=all&arch=any&keywords=libsdl2-0.0&searchon=names

I'm not sure it's even useful mentioning `libsdl2-2.0-0` since
`libsdl2-dev` depends on it. Maybe for distributing ares you would then
know people have to install `libsdl2-2.0-0` I guess

Also added a note that SDL2 input allows using a controller on *nix as I
haven't seen that documented anywhere

And I fixed a random makefile indentation issue that messed with my
head.
2024-01-25 13:37:32 +00:00
Luke Usher 91658b989a ares: bump version to v135 2024-01-23 10:47:43 +00:00
Steveice10 801eaa4088 desktop-ui: Fix broken firmware path setting. (#1374)
* Fix the firmware search path not being saved or loaded from the
settings file.
* Fix `FirmwareSettings::findFirmware` not using the right casing when
locating the firmware directory.
2024-01-22 14:20:27 +00:00
Steveice10 d6b2ca53e2 nall: Account for macOS app bundles in program path. (#1373)
Contains three main changes:
* In `Path::program()`, account for the macOS app bundle, so that
portable config support works with the app bundle.
* Create a new function `Path::resources()` for the resources path,
since `Path::program()` can no longer trivially be used to construct the
bundle resources path (no guarantee what the .app is named). On macOS
this returns the path to "ares.app/Contents/Resources", on other
platforms (in case they use it in the future) it just returns the same
as `Path::program()`.
* Fix an issue where, if Ares is stored in a sub-directory of the
desktop, the path settings UI will display paths next to the program as
relative to the desktop instead of relative to the program directory.
2024-01-22 14:20:14 +00:00
png183 d823641ccc gba: improve ROM to ROM DMA timings (#1372)
When performing a DMA with source and destination addresses both in
cartridge memory, the first write should use sequential access timings.
Implementing this in ares gives more accurate timings for ROM to ROM
DMAs than the previous approach, which instead removed the 2-cycle DMA
starting delay in these cases.
2024-01-22 14:19:45 +00:00
Giovanni Bajo dec675896d n64: reenable isviewer even outside of homebrew mode (#1370)
ISViewer is currently gated by the homebrew mode flag. It seems like
this is not ideal for rom hackers because they would still need a way to
log but homebrew mode is too verbose for them and hide the ISViewer
messages in streams of actual problems in game code that they don't
intend to fix. So restore ISViewer functionality without homebrew mode.
2024-01-22 14:18:53 +00:00
Giovanni Bajo e2caf175d6 nall: make gdb server accept only one connection at a time. (#1369)
This closes the socket server after each successful connection so that
new connections are immediately refused. Before, given that the server
socket was always in listen mode, one additional connection could always
be estabilished at the TCP level, even though the server wouldn't accept
it right away.
2024-01-22 14:18:42 +00:00
Giovanni Bajo 322c99ca75 nall: correctly handle TCP peer disconnections in tcp-socket.cpp (#1365)
Currently, when a client disconnects, the server in Ares fails to notice
and to call the onDisconnect hook, which in turns cause the server to
stop responding (it won't accept a new client, and will never receive
more data from the disconnected one).

This commit fixes it by correctly handling the recv() return code to
detect peer disconnections (return value 0) and any other kind of error
(return value < 0).
2024-01-18 09:27:23 +00:00
png183 b28bb3dbd7 gba: improve save type detection for homebrew (#1367)
Commercial GBA games contain strings in the ROM that correspond to a
save type (e.g. SRAM, flash memory, EEPROM), followed by 3 digits. These
strings are used by ares to detect what save type to use. However, when
running [jsmolka's SRAM and flash memory save
tests](https://github.com/jsmolka/gba-tests/tree/master/save), ares does
not detect a save type, since the programs lack the 3 digits at the end
of the save type string. This PR ignores the trailing digits during save
type detection, since it's likely that many other homebrew programs are
affected by the same issue ([GBATEK even advises developers to not add
digits when including save type
strings](https://problemkaputt.de/gbatek-gba-cart-backup-ids.htm)).
2024-01-18 09:27:02 +00:00
Giovanni Bajo 3f4334e5ff n64: implement undocumented accesses in FPU half-mode (#1364)
Fixes most of the remaining failures in n64-systemtest
2024-01-16 11:59:04 +00:00
png183 99a40e43ef arm7tdmi: fix additional instructions that require offset when reading PC (#1363)
Depending on which cycle they read values from registers, instructions
will read a different PC value. This is handled in ares by adding an
offset to the program counter (r15) value in affected cases. This PR
applies the offset in a few instructions that are affected by this
behaviour, but were previously not covered.
2024-01-13 19:00:42 +00:00
png183 9bbe36d73c arm7tdmi: Improved ldmia and stmia timings in Thumb mode (#1362)
ARM and Thumb mode variants of the ldmia and stmia instructions use a
shared pattern of sequential and nonsequential memory accesses. The
correct pattern was already implemented in ARM mode, and this PR applies
the same pattern to Thumb mode, making the timings of these instructions
more accurate.
2024-01-08 10:46:27 +00:00
Luke Usher 4bae7965f9 ps1: fix broken controller when sideloading ps-exe 2024-01-03 13:46:52 +00:00
jcmandjcm 9e4b3795f1 build: add macOS SDL to thirdparty, add .dylib to bundle (#1345)
### Description

Adds SDL (properly) to Mac builds.

This time, it has been added as a fully fledged optional dependency in
`thirdparty`, à la MoltenVK, and built from source before ares. We build
from source because brew installations are both single-architecture and
not fully portable.

The .dylib is copied inside the app bundle when linking, at the same
stage as MoltenVK.

The initialization of `ares.dylibs` has been moved from ares's Makefile
to the `desktop-ui` Makefile so that it's initialized when building
ruby, which it wasn't previously.

The SDL build is also added to CI, with the same caching methods as used
for MVK.

### Relevant Details

`HEAD` points at SDL 2.28.5 currently, which is the most recent stable
release. This should probably be periodically incremented as
appropriate.

~~SDL is currently built with the macOS deployment target of 10.13.
While SDL's minimum supported macOS version is 10.11, ares CI is using
Xcode 14.2, which has a minimum deployment target of 10.13. I am
currently testing to see if I can get the deployment target lower
without breaking other parts of ares's CI.~~ resolved, per below; SDL
supporting 10.11 is built + bundled.

### Testing

This has been tested to build properly locally and via GitHub CI. The
.dylib is packaged inside the bundle correctly and SDL functions
correctly in my testing, even if the user does not have SDL installed
elsewhere on their system.

This is seeking tests from other Mac users, especially on older
operating systems, to verify that SDL works correctly there.

Co-authored-by: jcm <[email protected]>
2023-12-28 09:24:04 +00:00
shinra-electric a82913c1f9 macOS: Add minimum macOS version and copyright to ares.plist (#1350)
Added the minimum macOS version key, but kept it at 10.9 which is what
macOS currently reports it as.
Now that the key is here, it will be easier to update it in the future. 

Also added the copyright line attributing the ares team and
contributors.
2023-12-28 09:19:47 +00:00
Jasmine Minter b7db1c074b macOS: various UI fixes (#1355)
Some issues I noticed while playing with this on macOS. Took a bit of a
deep dive and attempted to fix them up:
- changing the tracer settings did not seem to change what was logged.
seems to be the row isn't selected when the `onToggle` callback is
called. solution was to instead use the toggled `cell` to get the row
its in. this caused some C++ template issues but removing the `auto`
fixes them all (the alternative being adding `template` to a few method
calls, I should add I'm not a C++ programmer so from my limited
understanding this was the best solution)
- text in text fields were invisible. this was due to
`setForegroundColor` in the macOS backend not properly handling a
0,0,0,0 color. changed to perform the ternary on the `Color` rather than
the `NSColor` (`SystemColor` did not support the ternary)

merry christmas :)
2023-12-28 09:19:24 +00:00
Giovanni Bajo 1478637209 n64: support ROMs bigger than 63.9375 MiB (#1354)
Currently, the n64 core is limited to ROMs whose size is 63.9375 MiB
(also known as 64 MiB - 64 KiB). The reason is twofold: first, only 64
MiB are actually mapped in the PI bus; second, the last 64 KiB are
shadowed by the ISViewer debugging device.

ISViewer addresses sit in the last 64 KiB of the 64th MiB, so when the
ROM is larger than that (for instance, it is exactly 64 MiB), we should
simply disable ISViewer. In the long run, there will be a better
debugging solution which doesn't sit in the PI address space anyway.

Moreover, N64 supports basically ROM of unlimited size, there is no 64
MiB limit. This commit also allows that by simply mapping the whole ROM
into the PI address bus, whatever the actual size is.
2023-12-28 09:17:52 +00:00
Giovanni Bajo 432c4e3b2a z80: add newly discovered behavior of inir/indr/otir/otdr (#1353)
See https://spectrumcomputing.co.uk/forums/viewtopic.php?t=10555
2023-12-21 15:30:24 +00:00
Giovanni Bajo 18c84a83e4 n64: improve PIF HLE emulation by reducing latency (#1351)
When doing PIF HLE emulation, we run the HLE state machine just once per
frame. This is OK in general, but there can be a few situations where
this can cause PIF to miss one command from CPU. For instance, a ROM
might send the PIF boot termination command (0x8) and then shortly after
issue a SI DMA to identify controllers. If the two happen within the
same frame, our HLE emulation would miss the boot termination command.

This commit updates the HLE state machine also after each write to PIF
RAM.
2023-12-17 14:27:57 +00:00
remutro c4bc588082 Correct prefetch order for M68000 ASR instruction (#1347)
Originally found by vmicka72, so credits to them. Documented as issue:
https://github.com/ares-emulator/ares/issues/1275 and confirmed by
TascoDLX.

Since it is easy and straightforward, no sense in letting it hang
around.
2023-12-16 08:21:04 +00:00
Giovanni Bajo ed3083db8a Implement N64 register mirroring (#1344)
Also slightly improve PI latch emulation.
2023-12-14 11:13:55 +00:00
invertego 6d5ef628e8 ci: disable sdl2 on macOS to unbreak the build (#1343)
More work is required to support building universal binaries.
2023-12-12 23:38:21 +00:00
Luke Usher 6e8f4f224a cv: fix the build 2023-12-12 13:47:08 +00:00
png183 4bdeb3727f gba: unmapped i/o fixes (#1342)
Some unmapped I/O registers on the GBA always return 0 when read. This
pull request implements the required behaviour on six registers that
previously used open bus behaviour instead, allowing ares to pass all
remaining I/O read tests in the mGBA test suite.
2023-12-12 12:30:51 +00:00
NoxyMeandLuke Usher 20a3aac83b cv: add "XIn1" and "MegaCart" mappers (#1327)
Added "Xin1" mapper for the ColecoVision, this mapper supports:
- 31-in-1 (v1.1) ( 2013 )
- 63-in-1 (v1.0) ( 2013/0 1)
Both carts are by "Buzz and ZX-81", and i tested all games for both
multis and they all at least boot perfectly fine


And the "MegaCart" mapper, this mapper automaticly triggers for any rom
over 32k that isnt known to be another mapper (like the "xin1" mapper).

it all should be well with all homebrew games that requite the MegaCart
hardware.


If anyone knows of any other ColecoVision carts like these let me know
and I'll try to add them.

---------

Co-authored-by: Luke Usher <[email protected]>
2023-12-11 17:44:19 +00:00
invertego 2950d3fe44 md: various i/o port improvements (#1341)
General fixes:
- model data lines separately from output data latches
- update data lines after control register writes
- distinguish between driven and floating data lines
- remove redundant data latch state from devices
- separate host input polling from data line synthesis

6 button/fighting pad fixes:
- reset timeout only on TH 0->1 transitions
- remain in last phase until timeout (don't wrap)
- read back C/B button state along with M/X/Y/Z

Mega mouse:
- remove erroneous thread synchronize on attach which could crash ares

Affected titles:
- Decap Attack (MD)
- Trouble Shooter/Battle Mania (MD)
- WWF Raw (32X)
2023-12-11 08:28:36 +00:00
jcmandjcm b55c5010f3 ruby: add SDL to macOS (#1313)
### Description
Up to this point, SDL as an input library has not been present in macOS
builds. The plumbing was already in place for macOS to make use of SDL,
but SDL was not actually linked. This PR rectifies that. Seemingly
unnecessary references to the IOKit joypad are also removed from
`sdl.cpp`.

To facilitate adding SDL to ruby for macOS, a stubbed out macOS mouse
driver, `gcmouse.cpp`, has been added, to avoid needing to make numerous
modifications to `sdl.cpp` to work around mouse-related code.
### Motivation
The legacy macOS input uses raw values from IOKit, which are not
available for all supported controllers, and where present, are
uncalibrated and generally not ideal. SDL's input support is much more
advanced comparatively, properly handling and interpolating input from a
wide variety of controllers where raw IOKit input values aren't
sufficient. This should resolve a number of situations where ares won't
recognize input from controllers connected to macOS machines, among
other macOS input-related issues.

### Discussion
I am not super well-acquainted with ares's code and build system, and
it's entirely probable that I missed something here or went about this
not-quite-correctly; I did so as straightforwardly as possible and may
have missed something, for example fallbacks if SDL is not present at
build-time; I am not sure if this is currently handled. I encourage any
and all edits from maintainers if I broke something or went about
something incorrectly.

### Testing?
Tested locally on my Apple Silicon machine on macOS Sonoma. SDL can now
be selected and used successfully as an input driver for all supported
connected controllers.

Co-authored-by: jcm <[email protected]>
2023-12-09 22:03:16 +00:00
invertego f893afca89 nall: ensure utf-8 string encoding in cl builds (#1339)
As a bonus, use the Unicode ellipsis character in the ares UI on all
platforms instead of just macOS. In addition to looking nicer, it will
make regressions in this area very obvious.
2023-12-09 21:46:17 +00:00
invertego 0d131bf4d8 build: improve pkg-config support on windows (#1337)
Allow ares to be more easily built against external dependencies on
Windows outside of MSYS2 and using the MSVC ABI.

For example, dependencies can now acquired via vcpkg by installing the
relevant packages (pkgconf, gtk3, qt5, sdl2) and setting up the
environment appropriately before building.

```
CMD:
set PKG_CONFIG=C:\src\vcpkg\installed\x64-windows\tools\pkgconf\pkgconf.exe
set PKG_CONFIG_PATH=C:\src\vcpkg\installed\x64-windows\lib\pkgconfig

Bash:
export PKG_CONFIG=/c/src/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe
export PKG_CONFIG_PATH=/c/src/vcpkg/installed/x64-windows/lib/pkgconfig
```

Changes:
- hiro: fix building against gtk/qt with cl
- build: improve pkg-config detection and use with cl
- nall: support dynamic crt with clang-msvc/cl
2023-12-08 11:55:43 +00:00
remutro 7e99155f8e Mega Mouse support for 32X + CD (#1336)
Doom Resurrection patch version 3.0 (currently at version 3.1) added
support for the Mega Mouse, as well as making use of Mega CD hardware if
available. When I added mouse support for the Mega CD, the patch for
Doom Resurrection was at version 2.2, and since there was no licensed
32X software that supported the Mega Mouse I declined to add support at
the time. But leave it to homebrew, especially one as comprehensive as
this patch to add support.

This adds Mega Mouse support for both the 32X and 32X/CD combo, so it is
now available for all Mega * consoles.
2023-12-08 11:55:16 +00:00
Giovanni Bajo c95dc1bad7 n64: improve and extend cache coherency checks (#1314)
To make sure to intercept all possible errors, the check are now
performed by the RDRAM module, whenever a RDRAM read/write behind a
cacheline happens.

CPU writes to cache is now tracking dirtyness at the byte level rather
than whole cacheline level, so that hardware accessing memory does not
trigger a false positive for false-shared variables. An initial round of
testing has shown that the check would trigger far too much otherwise.
Error reporting is also much improved to provide more context to analyze
the issue, including tracking the PC at which the hardware DMA was
triggered.

For RSP DMA, we do even more: instead of reporting an issue when area of
a cached memory is DMA into RSP DMEM/IMEM, we actually just mark those
memory locations as tainted, and emit the warning only if RSP later
reads them. This is necessary because it is extremely common for RSP
ucode to read data beyond the actual buffers it cares about, even though
that data is then never accesses.

Some of the warnings issues by Ares have been analyzed on Super Mario 64
and Zelda OOT and in both cases they have been confirmed to as real bugs
in cache management made by the game.

For instance, in Mario 64, the programmers forgot to invalidate the
cache before loading data for the initial Mario head animation, as
reported by Ares now:
```
[unusual] PI DMA writing to RDRAM address 0x390650 which is cached (missing cache invalidation?)
	Cacheline was loaded at CPU PC: 0xffffffff80183b38
	PI DMA started at CPU PC: 0xffffffff80328558
```

The game just happens to work because after loading, the game does
something else and manages to get the cache invalidated by touching
other locations, but otherwise it would be a real bug.

As another example, in Zelda OOT, we get these warnings at boot:

```
[unusual] AI reading from RDRAM address 190820 which is modified in the cache (missing cache writeback?)
    Cacheline was loaded at CPU PC: ffffffff800b54fc
    Cacheline was last written at CPU PC: ffffffff800b5528
    
[unusual] RSP reading from DMEM address fe0 which contains a value which is not cache coherent
    Current RSP PC: d10
    The value read was previously written by RSP DMA from RDRAM address 00199e40
    RSP DMA started at RSP PC: abc
    The relative CPU cacheline was dirty (missing cache writeback?)
    Cacheline was loaded at CPU PC: ffffffff800b5244
    Cacheline was last written at CPU PC: ffffffff800b5264
```

These warnings appear to be real bugs in the audio library. Quoting
Thar0:
```
* AudioHeap_ClearCurrentAiBuffer/AudioHeap_ResetStep wipe the AI buffers with CPU writes and 
  then don't write back the cache, and I guess an AI DMA is either in progress or starts before it can
  be properly flushed. On boot this shouldn't matter as gAudioHeap is BSS and is already zero, but
  if the driver is reset later it might cause some minor issues?
* The RSP cache coherency problems is from loading filters in AudioHeap_LoadLowPassFilter.
  The heap allocator returns a pointer to which the CPU writes the filter data.. but the allocator
  writes back the cache before the CPU writes the data 😂
```

Moreover, this PR also implements all the known SysAD-related CPU
freezes that cause the console to crash. We now pass the
[n64-systemcrash](https://github.com/rasky/n64-systemcrash) testsuite in
full.
2023-12-07 17:14:18 +00:00
invertego b85395fe7b hiro: make gtk2/gtk3/qt5 backends build (again) on windows (#1335) 2023-12-07 16:50:49 +00:00
encoded-byte 91c46dbad4 fc: improve fds timer irq generation (#1333)
Improvements to fds irq generation logic, to match description at
https://www.nesdev.org/wiki/Family_Computer_Disk_System#Timer_IRQ_control_($4022)

- Fixed "i/o enable" register address
- Correctly acknowledge interrupts in writes to $4022 / $4023
- Improve irq generation logic

The changes are based on the discusion on this thread:
https://forums.nesdev.org/viewtopic.php?t=16507

With this pr ares passes all 21 tests on FdsIrqTestsV7 (many failed
before), which can be downloaded at
https://forums.nesdev.org/download/file.php?id=10240

In addition to passing the tests this pr also fixes Kaettekita Mario
Bros. (Japan): this game has 3 random opening animations before
requesting to switch to side B, two of these openings will display a
glitchy screen with old irq logic. This can be tested by just resetting
if the game shows the animation that works.
2023-12-07 16:23:15 +00:00
invertego 6a253b17af nall: define new type traits (#1334)
Instead of directly specializing std traits classes (which would result
in undefined behavior) define our own traits classes that inherit from
the std types.

This allows us to remove a hack in the serializer class and paves the
way for building ares with Qt on Windows.
2023-12-07 16:19:40 +00:00
invertego 4116855924 build: take care of some macOS build warnings (#1332)
- remove -stdlib=libc++ as it caused warnings when applied to C files
and has been the default on macOS for something like a decade
- add -Wno-deprecated-declarations to the command line to silence
warnings about things like sprintf in thirdparty
2023-12-07 16:17:03 +00:00
invertego 1146c0def3 build: remove -fomit-frame-pointer (#1331)
This option is usually enabled by default by GCC/Clang with -O1 and
above. This is not true on some targets like macOS/arm64, and there we
are better off deferring to the compiler anyway.

This became an issue because Xcode 15 shipped with a new linker that, at
least when LTO is enabled, appears to mess up metadata that is used to
unwind the stack during exception handling. Without a frame pointer to
fall back on, unwinding fails completely and causes ares to terminate.
2023-12-06 10:33:34 +00:00
invertego e176d8b3ee build: always pass -arch on macOS (#1325)
Use the arch derived from -dumpmachine if none was specified to make.
Passing -arch seems to be required to get full debug information on
arm64.
2023-12-04 18:44:35 +00:00
Giovanni Bajo 9b5e339cc1 n64: improve SI emulation (#1326)
In addition to approximating the correct values in SI STATUS fields,
this commit also fixes a bug: I/O writes to PIF RAM do trigger an
interrupt just like DMA does.
2023-12-04 18:44:16 +00:00
invertego 8f626e304a ci: build resiliency tweaks (#1322)
Disable "fast fail" so jobs will run to completion even if others fail.
The workflow as a whole will still fail, but the jobs that succeed will
produce downloadable artifacts.

Secondly, allow the "macOS: Import Certificate" step to fail on forks.
This allows CI to function on forks without any other setup, though the
produced macOS builds won't run actually run unless you re-sign them.
2023-12-04 12:02:26 +00:00
remutro bbe9a9b0a4 Make it more obvious that the apply button for drivers needs to be used (#1320)
We frequently get issue reports in GitHub and Discord about driver
settings not being saved. This is due to the fact that changes to
drivers requires using the Apply (formerly Reload) button for changes to
stick, which is different than other options in the settings window.

With this change, the Apply button is grayed out until a different
driver is selected, hopefully making it more obvious that the buttons
need to be used. A small note was also added at the bottom explicitly
stating that they Apply buttons must be used in order to switch/change
updates to driver selection.
2023-12-04 12:02:05 +00:00
invertego c00e402eeb build: enable strict standards conformance with cl (msvc) (#1317)
From the MSVC docs: "You can use the /permissive- compiler option to
specify standards-conforming compiler behavior. This option disables
permissive behaviors, and sets the /Zc compiler options for strict
conformance."

While generally a positive thing, this does make MSVC stricter than
GCC/Clang in some cases. A couple of things popped out:

- nall::multiFactorImage had a busted constructor. I fixed this and did
some other cleanup while I was at it.
- nall::string has an implicit conversion operator to const char*. This
is honestly really ugly and creates ambiguity and allows for all kinds
of surprising silent conversions. Unfortunately, fixing that (by making
it explicit) will require touching hundreds of lines of code so I opted
not to deal with it now. Instead I fixed the much smaller number of
ambigous cases relating to string literals.
2023-12-04 12:01:50 +00:00
invertego 5e6afeaa5e hiro: prevent exception originating from appkit (#1324)
As noted in the Chromium source, the value for
NSTreatUnknownArgumentsAsOpen needs to be of type string despite being a
boolean setting.

If the type is boolean, AppKit throws an exception (which is caught),
but if unwinding fails (observed on some arm64 systems in certain build
configurations when ares is compiled with LTO and -fomit-frame-pointer)
it terminates the whole application.

I've confirmed the behavior the original commit was designed to correct
is still fixed with this change.
2023-12-04 12:01:37 +00:00
Luke Usher a3222abe79 md: remove 32x dreq debug printing 2023-12-04 11:21:32 +00:00
Tomás Domínguez Bolaño b0ee66d4ad fc: use hkrom for mmc6 (#1316)
MMC3 and MMC6 share the same mapper in the iNES format. To disambiguate
MMC6 from the MMC3 mapper the NES 2.0 submapper 004:1 should be used:

https://www.nesdev.org/wiki/MMC6

Code for using the MMC6 was actually commented in mia/medium/famicom.cpp
up to commit bed4ce3659, where it was deleted. This commit
reintroduces this code to use it with the NES 2.0 submapper 004:1. This
adds compatibility with the games "StarTropics" and "Zoda's Revenge:
StarTropics II".
2023-12-02 17:54:09 +00:00
invertego 6567d9dcf0 desktop-ui: destroy timers on system unload (#1315)
Fix two issues caused by disc swap timers:
1. Crash on exit in certain build configurations
2. Crash on system unload shortly after disc swap

Because these timers were simply members of Emulator derived classes
(which are in turn owned by a global variable), they were being
destructed after main() along with other static lifetime objects. This
was problematic because hiro also has global state that the timer
implementation depends on, at least on Windows. Whether this manifested
in a crash depended on a few factors, object destruction order among
them (which is at the mercy of the linker).

The second crash would reliably reproduce simply by initiating a disc
swap and unloading the emulated system (such as by closing the emulator)
within the next 3 seconds.
2023-12-02 14:11:26 +00:00
invertego 9a8e7f8b6e recompiler: allow jitprotect() calls to be nested (#1306)
The following applies only to macOS when JIT page protection is enforced
(namely on Apple silicon).

A sequence of jitprotect(false) calls must now be followed by an equal
number of jitprotect(true) calls to restore executable protection. A
further assumption is that nesting always starts with (false), which is
only logical because you have to write something before you have
anything to execute. This is explicitly checked in debug builds.

The N64 and PS1 recompilers have needed this since commit 8ed4af8 in
order to handle the path taken on recompiler cache flushes. It's subtle,
but emit() will evict all pools and so the second call to pool() from
block() will reallocate the pool and unexpectedly toggle the protection
out from under it.

Technically the SH2 recompiler has needed this since commit 241f0e9
(from 8 months ago!) but that same commit made cache flushes practically
nonexistent, so it's likely not impacting the retail title library.
2023-11-30 09:38:32 +00:00
Giovanni Bajo 1c9d38895d n64: simulate SysAD freezes when accessing non-RDRAM areas via cache (#1299)
The CPU will freeze until power-off if a cached access is performed to a
non RDRAM area. This obviously never happens with games, but it is a not
so uncommon error when doing very low level development on the system.

This patch emulates the freeze for the data cache, and for the
instruction cache when using the interpreter. Implementation for
instruction cache and recompiler is left as a followup.
2023-11-29 21:43:52 +00:00
invertego cd11421d72 recompiler: enable instruction tracing w/recompilers (#1301)
To prevent undue performance losses, the extra codegen only occurs when
tracing is enabled. To support toggling tracing on/off during execution,
the recompiler cache is flushed on setting changes.

Flushing the recompiler cache has its own side effects, but that's
already an issue, and it seemed the least bad way to isolate the impact
of this feature.
2023-11-29 21:43:30 +00:00
remutro 079295e2ca Enable/Disable N64 Expansion Pak (#1302)
Add ability to enable/disable N64 4MB expansion pak. 

Similarly to the video settings window, I've added a Nintendo 64
specific section in the options window, which was mostly empty. Existing
options fall under a generic "Emulator Options" section. Seems like a
reasonable place to add a few console specific options.

Addresses issue: https://github.com/ares-emulator/ares/issues/939
2023-11-29 21:43:20 +00:00
Luke Usher 2a819403db ci: pass local=false and build=optimised to macos builds
Fixes an issue where release builds for macOS were optimised for the compiling machine rather than for distribution.
2023-11-29 16:23:36 +00:00
Luke Usher df031a7d4c desktop-ui: rename FPS to VPS (VBlanks Per Second) to better represent what it is really showing 2023-11-28 21:52:55 +00:00
Luke Usher 0dd3edebe0 desktop-ui: fix an issue where "Deep Black Boost" did not save 2023-11-28 21:25:43 +00:00
Luke Usher f9d38f5daf desktop-ui: rename "Reload" to "Apply" in driver settings to clarify it's function 2023-11-28 21:24:09 +00:00
Luke Usher d381bb4678 v134 2023-11-22 10:49:33 +00:00
LuigiBlood 3392783359 n64: optimize DD RTC checks (#1300) 2023-11-22 00:47:58 +00:00
Giovanni Bajo 67c6bfa4a5 mia: improve CIC detection algorithm (#1296)
Currently, we detect the CIC to emulate by calculating a CRC32 over IPL3
bootcode in ROM, and keeping a database of the known IPL3s. This works
because right now there are only a handful of IPL3 variants (exactly one
per CIC).

In preparation for libdragon releasing an open source IPL3 that could
evolve and be forked an unbounded number of times, we must stop keeping
a database of IPL3s.

This commit changes strategy: it simulates the IPL2 checksum hash over
IPL3 (which is exactly what a real N64 does at boot) and verifies if the
checksum matches that expected by any of the known CIC variants. This
works because open source IPL3 variants must anyway have a checksum that
collides with that of CICs (which must be obtained via a second preimage
attack, bruteforced on GPUs), otherwise they would not work on real
hardware nor on Ares itself (which correctly simulates the boot sequence
and would refuse to boot an "unsigned" IPL3).
2023-11-21 10:34:53 +00:00
invertego 8cea0b8329 ps1: initialize _active member in peripherals (#1298)
Discovered using Clang's undefined behavior sanitizer which detects
non-0/1 values in bools.
2023-11-21 07:50:57 +00:00
invertego 4f7feeafd7 build: fix level 2 msvc warnings (#1297)
Level 2 warnings are considered "significant" and turning them on
revealed one issue that could, in the future, have affected MSVC-ABI
builds using either CL or Clang.

MSVC's C++ name mangling distinguishes between classes and structs, and
throughout the ares codebase there were friend declarations using
"class" to refer to types that were defined with "struct".

I don't see any value in retaining this inconsistency, especially
considering that ares otherwise avoids the "class" keyword (though I
spotted a few uses that crept in from recent commits).
2023-11-20 09:26:35 +00:00
Luke Usher bcb60a6e1b n64: fix an oops in rtc init 2023-11-19 22:22:48 +00:00
Luke Usher af0713858c n64: initialize rtc to unix epoch (timestamp 0) 2023-11-19 22:12:34 +00:00
Adrian Siekierka d3b11e4339 ws: Adjust ROM/SRAM bus timings. (#1212)
Implements port 0x60 bit 1 and 3, a newly-discovered way to change the
cartridge SRAM and I/O bus speed. Also fixes a regression, and adjusts
the ROM bus speed to match a test ROM.

~~Marked as draft as validation on ASWAN ("mono" system) is required.~~
Nobody stepped up for 3 months, so, ehh. It's still more accurate than
it was.
2023-11-19 15:39:00 +00:00
invertego 7faebad713 build: fix level 1 msvc warnings (#1295)
Level 1 warnings are considered "severe" and, unsurprisingly, turning
them on revealed some true bugs.

- file-buffer.hpp: What should have been a zero extend followed by a
complement was instead a complement followed by a zero extend,
inadvertently truncating the file seek offset to 32 bits.
- file-map.cpp: What should have been a nullptr check after
CreateFileMapping() was instead a check for INVALID_HANDLE_VALUE. The
warning pointing me toward this was not technically about the misuse but
rather the redefinition of INVALID_HANDLE_VALUE in nall. I've removed
this and any reference to INVALID_HANDLE_VALUE except for error checks
immediately after calls to the few APIs that return it.
2023-11-19 14:59:02 +00:00
invertego 92c1c2ec44 ruby: queue no more than "latency" samples in wasapi driver (#1292)
The WASAPI driver in ruby is a bit of an oddball. It has a fixed length
circular queue for samples, and before this commit there was no logic to
prevent queuing more than the requested "latency" number of samples or
even to prevent the queue from overflowing entirely.

In practice this wasn't much of an issue when audio or video
synchronization were enabled and therefore bounding the rate of audio
generation. It did not, however, play nice with the fast-forward
feature, and it caused a noticeable pause after returning to normal
speed as the queue drained from whatever level of fullness it had
wrapped around to (effectively random but averaging over half a second).

Now the length of the sample queue is (mostly) bounded to the "latency"
parameter which is more consistent with the other drivers.

Fixes #958
2023-11-18 13:44:36 +00:00
Luke Usher bff2738163 ares: fix copy/past error in i8080 header 2023-11-17 22:18:37 +00:00
LuigiBlood 8a4bf9d82c n64: fix dd rtc skipping december + validity checks to avoid glitchy rtc (#1290)
- add more details to the dd time.rtc format as comments for readability
- fix dd rtc skipping december entirely (wrong comparison)
  - I feel really dumb for this one.
- add validity checks on every rtc second tick to avoid glitchy rtc time
& date
- This also avoids the user to be able to skip the time & date error
into a glitchy rtc by simply restarting.
- add validity check on load if the time is valid or not, if not valid,
destroy the rtc info to get the user to input new rtc info
2023-11-15 22:28:43 +00:00
LuigiBlood 4537145d24 n64: clamp x/y values for n64 mouse (#1289)
Avoid the mouse movement to be jittery especially when it's very fast.
2023-11-15 20:39:33 +00:00
invertego 4544e1df69 desktop-ui: disable color bleed for HD content (#1287)
This effect runs on the CPU, which is expensive for HD content and
incredibly so for UHD content. It doesn't make much sense to apply to
non-SD content, in any case.

Currently this only affects the N64 core in HD and UHD modes.
2023-11-15 11:04:43 +00:00
LuigiBlood 30cc370e40 n64: put dd rtc into a struct + serialize rtc + update serializer version (#1288)
I seperated the RTC into its own struct as it can be easily seperated,
and added serialization for it for save states.
Tested working.
2023-11-15 11:04:23 +00:00
Luke Usher 4a48753ea3 a26: riot timer improvements
Fixes "bang!" demoscene demo running too fast

Most effects in that demo are still broken, however.
2023-11-14 12:28:37 +00:00
Luke Usher 52df41a709 Squashed 'thirdparty/sljit/' changes from bc844c90a..c1cbaf09e
c1cbaf09e x86: avoid clobbering ebx register during CPU detection (#217)
62c91605a cpuid on x86 clobbers memory
856fce15d loongarch: sljit_emit_atomic_load/store implementation using LoongArch V1.10 (#216)
44cee7aa5 allocator: add support for PowerPC in Apple (#214)
e09319736 Improve Windows compatiblity
112fd4119 Add AVX detection
2713dda05 config: use standard interface to detect macOS version (#213)
029327875 Fix addc and subc on arm32
6a718f07d Improve simd lane mov on x86
292bf2e63 The bug is in the generation of a call relative instruction (because this is PIC code). Call relative of 0 will run the next instruction. So call relative is relative to the next instruction, not the current instruction. (#208)
b6f010324 s390x: make sljit_generated_code_size() accurate (#209)
e2c460881 Improve simd lane replicate on x86
73e84319c arm: small improvements for atomics with LSE (#210)
0436b57a6 Improve sljit_emit_simd_extend
ff4c77263 mips: improve FPU detection also for MIPS64 (#205)
6d1376f84 Improve x86 code generation
a443763e4 build: clear warnings with gcc12 and improve Makefile (#191)
d78063a88 ppc: correctly implement SLJIT_REV in 64bit little endian (#193)

git-subtree-dir: thirdparty/sljit
git-subtree-split: c1cbaf09e995d4d2ab87a85b2ed6a27f4e123754
2023-11-14 12:17:40 +00:00
Luke Usher dc385dbe6e Merge commit '52df41a709917bc8d55a5fe8d9848d15ed3941e4' 2023-11-14 12:17:40 +00:00
Luke Usher df8d5a6157 mia: .rom => rom 2023-11-14 00:20:27 +00:00
Luke Usher de1c12737a mia: remove support for loading ".smd" extension
This incorrectly implies that we support the old .smd rom format that used to be standard prior to .bin/.md;

This format is an interleaved format designed for the Super Magic Drive copier and ares does not support it

To prevent confusing users who may sill have legacy .smd files around, I'm removing the extension from mia.
2023-11-13 23:39:01 +00:00
Luke Usher da9c82956a mia allow loading .rom files for MSX/MSX2 2023-11-13 23:39:01 +00:00
hackgrid 4dd837ae41 mia: implement n64 controller config (Advanced Homebrew ROM Header) (#1286)
This PR implements the n64 controller config from the Advanced Homebrew
ROM Header:

https://n64brew.dev/wiki/ROM_Header#Advanced_Homebrew_ROM_Header

Only applies to Controller 1 at the moment, because ares only checks
globally for tpak/cpak/rpak:


https://github.com/ares-emulator/ares/blob/fa0f9a715fc9e14d48220ba2339ca1273b9d5d5b/desktop-ui/emulator/nintendo-64.cpp#L148
2023-11-13 23:34:04 +00:00
LuigiBlood fa0f9a715f n64: emulate disk change status bit behavior correctly + fix 64dd reset + fix DD debugger IO output (#1283) 2023-11-10 10:10:24 +00:00
LuigiBlood 87ae65a094 n64: add 64dd disk swapping + safety checks + change manufacture timestamp (#1280)
This adds:
- 64DD Disk Swapping for both 64DD standalone games and N64 Combo games
(only if a disk is loaded).
- Safety checks to prevent corruptions.
- Change manufacture timestamp and line to differenciate different disk
files from each other to avoid confusion
2023-11-08 20:09:27 +00:00
Luke Usher 0ceaf17be6 desktop-ui: emulator::construct must be called *before* settings are loaded
BIOS settings depend on being able to iterate through Emulators.
2023-11-08 10:46:39 +00:00
LuigiBlood e7760bfc51 n64: Add D64 file format support (#1279)
Adds D64 file format support, which is the official master 64DD data
file format, which is basically NDD but a lot of the useless data
trimmed out and disk formatting data kept to a minimum.

Works in this way:
- Adds *.d64 file extension to the list.
- Redo code that uses clearer variables to keep track of recognized 64DD
disk file format.
- Add a file size check to do a proper format recognition and avoid
errors.
- Additional data validity checks.
- D64 conversion to the internal disk format (I refer to it as MAME
format) by reusing the same algorithm as NDD while ignoring blocks of
data that are not present.
2023-11-07 13:12:22 +00:00
Adrian SiekierkaandLuke Usher c27f369e31 fc: Initial EPSM implementation. (#1274)
More information:
https://www.nesdev.org/wiki/Expansion_Port_Sound_Module

While this is a homebrew device, it has already shipped to consumers in
this manner.

TODO (I'm not sure how to fix these issues myself):

- [x] Switching from EPSM back to another peripheral (None or Family
Keyboard) unloads not just the additional EPSM-provided audio streams,
but seems to break *all* audio from the NES core.
- [x] NES 2.0 cartridges can declare that they wish to have the EPSM
peripheral installed by default (Extended Console Type 0x04), but I'm
not sure how to hook this nicely from the pak data to the expansion port
node.
- [ ] Missing serialization, but this affects the Family Keyboard to
some limited extent already, so...

Not urgent at all. It has shipped to consumers, but the amount of
software using it is still lacking.

---------

Co-authored-by: Luke Usher <[email protected]>
2023-11-07 12:09:15 +00:00
Giovanni Bajo 020410ea0b Add homebrew mode (with a few checks to N64 core) (#1278)
This PR adds a new general setting called "homebrew mode", that can be
used as an umbrella setting for all kind of developer-related aids in
the emulator. This will also allow resource intensives checks to be
added (eg: N64 RDP command list validation).

For now, we hook this to the N64 core only in two ways:

* ISVIewer emulation is now only performed in homebrew mode. This is
obviously not resource intensive, but it would have naturally been an
homebrew mode feature, if it existed at the time. Normally, games should
behave exactly like on a real N64, where there is no ISViewer peripheral
plugged in into the PI bus.
* A new cache coherency check is added to PI DMA. If a DMA is performed
to/from RDRAM locations for which the CPU holds a valid cacheline, a
warning is emitted to the console as the game is probably buggy.

Slightly unrelated, a commit also adds a new "DCache" memory viewer, to
see the memory from the point of view of the CPU (so with data cache
taken into account). This is useful to ROM hackers which don't care
about the physical RDRAM contents but rather the memory contents as
written by the CPU.
2023-11-07 10:53:12 +00:00
Luke Usher 6fbea44d3d ci: install setuptools in macos environment
The latest macOS runner updated the version of python to 3.12.0.

This removes the deprecated distutils core module, requiring the "setuptools" package to be installed to provide it.

distutils is a dependency of MoltenVK.
2023-11-07 10:22:54 +00:00
Luke Usher d2e20f0d05 mia: use SMS mode when loading .sms roms on gamegear 2023-11-07 09:27:44 +00:00
Adrian Siekierka e589f8d9cf wdc65816: fix PLB wrapping in emulation mode (#1277)
>Regarding PLB, it looks like it reads from $200 and snes9x, mesen,
bsnes, and the official CPU manual all got it wrong.

Reproduced and verified on real hardware via
https://github.com/gilyon/snes-tests
2023-10-29 19:16:41 +00:00
remutro d201d6a547 N64 Mia Update - Chameleon Twist 2 (#1272)
Update Mia DB to include US/EU Ids for Chameleon Twist 2. The Japanese
Id starts with NV2 while the other regions have an Id that starts with
N2V.
2023-10-26 13:48:30 +01:00
Luke Usher 7dc11a1c39 ms: check for TMR-SEGA header
Games that lack this header are incompatible with non-japanese systems
2023-10-25 22:15:04 +01:00
Luke Usher 89e0001275 ares: preliminary Intel 8080 cpu implementation + test harness
This implementation was based on our Z80 implementation and as such, has the following quirks:

1. Z80 style opcode mnemonics rather than intel style
2. Has an internal WZ register than a real 8080 lacks
3. May not have fully accurate timing

Despite this, it passes every test in this harness and is correctly detected by CPUTEST.COM as an 8080 rather than a Z80.

The remaining quirks can be addressed as needed
2023-10-25 17:56:09 +01:00
Luke Usher 5b744c324d nall/hiro: shlwapi is a dependancy of nall, not hiro 2023-10-25 11:21:05 +01:00
Luke Usher a47bbea7a2 desktop-ui: surround sg1000a with CORE_SG ifdef 2023-10-25 09:42:13 +01:00
Luke Usher 414c205256 desktop-ui: surround sg1000a with CORE_SG ifdef 2023-10-25 09:40:16 +01:00
Luke Usher f1fb33af8e ms: add color emulation for non-linear blue 2023-10-24 17:52:23 +01:00
Luke Usher 84e3663e80 pce: add color emulation (non-linear rgb)
Palette is derived from the one used in the Mister project
2023-10-24 16:39:26 +01:00
TascoDLX 77bfad91d8 md: refresh & bus timing fixes (#1269)
1. Bus refreshes needed a clean up and readjustment. Timings were
compared against US Model 1 VA3 via Ti_'s test_inst_speed rom. Yellow
values are ok, as they normally skew lower due to refresh. Some reds
still remain (of the regular instruction tests) that appear to be off by
1.
2. Basic implementation of delays incurred on both main 68k and z80.
Delay values are tuned according to Ti_'s misc_test rom tests ("Z80
DELAY ON ROM READ", "M68K DELAY ON Z80 ROM READ"). Hardware tests
indicate that z80 delay may skew lower towards 170, while m68k delay
typically sticks to 460. This change puts ares at ~178 z80 delay & ~461
68k delay. Fixes #255.
3. Trivial readjustment of m68000 interrupt timing. Approx'd autovector
delay changed to 14 cycles, which happens to be the median of the
expected range (10-18).
2023-10-24 09:14:44 +01:00
Luke Usher cd8d84ed5b all: create a system for generic arcade boards 2023-10-23 23:08:21 +01:00
Luke Usher 44b0c2c532 Revert "md: increase synchronizaton between audio related components"
This reverts commit 939617aad9.
2023-10-23 11:33:49 +01:00
Luke Usher 02dcc63431 md: remove extra bus arbitration delays
Commit history shows that these were initially added to get audio working in OverDrive 2, but now it works better without them.

Having these additional delays present caused audio to stop working late in overdrive 2's execution, most likely due to causing extended delays and confusing timing sensitive code.
2023-10-23 11:22:20 +01:00
Luke Usher 864bc6e6d6 md: increase bus arbiter delays for z80->m68k communication 2023-10-23 10:37:09 +01:00
invertego a572a97d10 build: restore rule for ym2612 (#1267) 2023-10-22 08:30:16 +01:00
Luke Usher 939617aad9 md: increase synchronizaton between audio related components 2023-10-21 23:38:13 +01:00
Luke Usher ca6ada73ab ng: use ymfm for ym2610 rather than (badly) hacking together ym2612 with the YM2149 2023-10-21 19:56:05 +01:00
Luke Usher d8e462035d Merge commit 'e457e05c8b14079c1d87d96a47cea6c56dea7796' as 'thirdparty/ymfm' 2023-10-21 19:52:18 +01:00
Luke Usher e457e05c8b Squashed 'thirdparty/ymfm/' content from commit d641a8063
git-subtree-dir: thirdparty/ymfm
git-subtree-split: d641a80631fe1fa5a3689fa5b6def07685772424
2023-10-21 19:52:18 +01:00
Luke Usher da3adb4daa ym2612: update key state on phase tick 2023-10-21 10:37:37 +01:00
Luke Usher 3ebfb4433e Revert "ym2612: immediately update key state on keyOn/keyOff"
While fixing the observed behavior in games, it also fixed things that are known to be broken on hardware; needs further work.

This reverts commit 785edf60c8.
2023-10-21 10:26:09 +01:00
Luke Usher 0bea9d3a2e md: add 32x framebuffer mirror (fix SoulStar X) 2023-10-20 14:48:06 +01:00
Luke Usher 785edf60c8 ym2612: immediately update key state on keyOn/keyOff
Fixes Puyo Puyo Tsuu theme
2023-10-20 12:18:13 +01:00
Luke Usher 62f5c36c17 desktop-ui: rename SG-1000 (Arcade) to "SG-1000 Arcade System"
Fixes an issue where settings get destroyed due to () characters.
2023-10-19 11:28:43 +01:00
Luke Usher ed35f2249b desktop-ui: arcade systems have their own category 2023-10-19 10:50:00 +01:00
Luke Usher 3fa22458f4 sg: fix serialization 2023-10-19 00:39:27 +01:00
Luke Usher 708247ab38 sg: preliminary support for sg1000 based arcade games
Compatible with the current MAME romset at time of writing.

This system only has three games:
  - Champion Boxing [cboxing]
  - Champion Pro Wrestling [chwrestl]
  - Doki Doki Penguin Land [dokidoki]

Known Issues:
  - No support for configuring DIP switches
  - Champion Pro Wrestling [chwrestl] is not working due to encryption
2023-10-19 00:32:33 +01:00
Luke Usher 88a14fb5f2 msx: fix conflicts between rom and scc with KonamiSCC mapper 2023-10-18 20:00:23 +01:00
Luke Usher 5f55b6cf27 md: fix hang in "The Terminator (USA)" 2023-10-18 19:34:44 +01:00
Luke Usher 74e10c45f3 mia: improve system autodetect of cd-rom based games 2023-10-18 19:04:48 +01:00
Luke Usher 5984867fce md: pass "CDC REGS" test in mcd-verificator 2023-10-18 18:43:59 +01:00
Luke Usher dd01cf762b mia: identify FDS bios by copyright string rather than digest
A file is regarded as a Famicom Disk System Bios when

1. It is 8KiB in size, AND
2. it contains the Famicom License DRM/Trademark security string at offset 0xd37
2023-10-18 17:30:15 +01:00
Luke Usher bfa1eeafde md: fix "After Burner Complete (32x)" 2023-10-18 12:58:47 +01:00
Luke Usher 0661e9858c md: 32x performance tuning 2023-10-18 12:51:10 +01:00
Luke Usher 7b433f305f md: fix sh2 thread restart behavior 2023-10-18 10:39:44 +01:00
invertego 1c61fc95e6 md: implement opn2 restart w/out resetting the thread clock (#1260)
Before this change, the OPN2 thread had to play catch-up, generating a
bunch of samples and potentially overflowing the resampler queue in the
audio stream, as observed in Shining Force.
2023-10-18 10:18:33 +01:00
Luke Usher 401df969b0 fc: serialize controller state 2023-10-17 10:45:17 +01:00
Luke Usher 5deec7ede5 Revert "fc: allow "ppu direct color" to function, fixes full palette test roms"
This reverts commit 6a7de62674.
2023-10-10 13:38:00 +01:00
Luke Usher 6a7de62674 fc: allow "ppu direct color" to function, fixes full palette test roms 2023-10-10 09:39:36 +01:00
Luke Usher b7be6612b9 ares: prevent rebuilding the entire project if only version constants change 2023-10-07 18:12:36 +01:00
Luke Usher f976cdc3a8 md: set color bleed width per frame 2023-10-02 08:58:24 +01:00
invertego e369049d0a nall, libco: add support for risc-v arch (#1249)
Tested with qemu-system-riscv64 and Ubuntu as the guest OS.
2023-10-02 08:45:43 +01:00
jcmandjcm 72d8179bf0 Add "Games" app category to bundle .plist on macOS to facilitate Sonoma's Game Mode (#1248)
macOS 14 (Sonoma)'s Game Mode offers some small benefits for ares,
including lower latency for bluetooth peripherals and potential minor
performance gains. The system has various ways of detecting that an
application is a game and enabling game mode, but ares does not
currently engage it. Adding the "Games" app category key value pair to
the bundle .plist is one way of hinting to macOS that Game Mode should
be enaged. In my testing, adding this key to ares's bundle .plist
successfully activates Game Mode when ares is fullscreened.


![image](https://github.com/ares-emulator/ares/assets/6864788/5c5727f9-88da-423e-af19-9fd08ed8eb91)

Co-authored-by: jcm <[email protected]>
2023-09-29 22:14:07 +01:00
invertego 8ed4af8b57 recompiler: avoid overallocation in code buffer (#1247)
sljit performs a single (potentially) oversized executable memory
allocation before generating code. Instead of reserving the requested
amount of space, we can reserve only the used amount and eliminate some
dead space between code blocks.

In some cases sljit may write extra data beyond the generated code.
Because of this, it is no longer safe to assume the remainder of the
code cache is zero initialized. In practice, this assumption only
affected pools which can easily zeroed on demand. This also saves time
on code cache flushes, as we no longer zero the entire cache at once.
2023-09-29 14:14:32 +01:00
Luke Usher bcddb7a68f fc: implement ppu open bus
Passes blargg's test_ppu_read_buffer and ppu_open_bus
2023-09-28 14:00:02 +01:00
Luke Usher 5c9f5419b0 gdb: disable some debug prints 2023-09-28 11:59:03 +01:00
invertego 4b3b78b77e recompiler: more robust code cache allocation (#1242)
- align static code cache to 64 KiB (up from 4 KiB)
- remove dependency on alignas() and associated build workaround
- check for mprotect() failure and fall back on dynamic allocation
- perform dynamic allocation (if needed) at top of main on all platforms
2023-09-28 10:12:39 +01:00
invertego 07c320e55a 32x: maintain code cache allocation across resets (#1243)
Commit 9422ea5905 introduced support for
resetting the 32x SH2 CPUs from the MD 68k CPU, and it also created a
couple of new issues.

1. SH2::power() tries to allocate from a fixed bump allocator, but that
allocator is only reset from M32X::power(), which is not called in
response to a reset register write. In practice this meant we were
falling back on dynamic allocation, creating performance issues when the
allocation is distant from the ares executable.
2. The code cache could be released out from under a running SH2 libco
thread we switched away from to run the 68k thread.

This change fixes #1 and partially addresses #2, but it doesn't address
the issue of an already executing SH2 code block resuming where it left
off after a reset. I don't currently have a test for this case, so I'll
leave it for a later change.
2023-09-28 10:12:17 +01:00
PikaSamus 506820bac7 Modified weave deinterlacing description (#1240)
Corrected horizontal to vertical, and changed comma to semicolon.
2023-09-27 10:18:00 +01:00
invertego 46ddcac339 recompiler: revert "avoid overallocation in code buffer" (#1241)
Turns out the overallocation was masking some bugs in sljit. Will take
this upstream and revert the ares change in the meantime.
2023-09-27 10:08:47 +01:00
invertego 1349d45a51 recompiler: avoid overallocation in code buffer (#1239)
sljit performs a single (potentially) oversized executable memory
allocation before generating code. Instead of reserving the requested
amount of space, we can reserve only the used amount and eliminate some
dead space between code blocks.
2023-09-26 22:22:55 +01:00
Luke Usher ff11f77f92 desktop-ui: revert "enable color bleed by default" 2023-09-26 16:28:11 +01:00
Luke Usher f9c110cd49 desktop-ui: enable color bleed by default 2023-09-26 11:27:57 +01:00
Luke Usher 379091bada md: fix color bleed 2023-09-26 11:25:50 +01:00
Luke Usher ba475a7a59 desktop-ui: expose "color bleed" option to all cores 2023-09-26 10:28:14 +01:00
Luke Usher 06e1ac13ef sfc: separate black level crush from color emulation 2023-09-26 10:18:57 +01:00
Luke Usher 675026ffdd fc: remove gamma adjustment in color emulation 2023-09-26 10:18:39 +01:00
Luke Usher f0449e36ff md: sync every cycle when 32x is not in use
We should strive to fully remove this hack, but we're not there yet for 32X.
2023-09-25 11:00:17 +01:00
Adrian Siekierka 0f265028f2 fc: add self-flashing support to UNROM-512, GTROM (#1237)
Tested with https://github.com/SourMesen/Mesen/issues/753
2023-09-24 14:45:22 +01:00
Adrian Siekierka 0b2544a6cf gdb/server: implmment NoAckMode (#1230)
I have verified that it works with LLDB; please don't merge until
@HailToDodongo verifies it with GDB.
2023-09-19 10:02:26 +01:00
Adrian Siekierka 99afcff4c7 fc: Fully utilize NES 2.0 extended PRG/CHR size variables. (#1229)
This PR may necessitate more third-party verification than usual. It
also allows for detection of the correct MMC5 board based on NES 2.0
header alone.
2023-09-19 10:02:12 +01:00
Luke Usher a652e85069 Squashed 'thirdparty/libchdr/' changes from 54bfb871c..9108f34a8
9108f34a8 Merge pull request #104 from invertego/huff_dec_err
1fb0c62a5 Fix huff_codec_decompress returned error code

git-subtree-dir: thirdparty/libchdr
git-subtree-split: 9108f34a892272f61c3ed3bff4bee728d4c1dd57
2023-09-18 21:30:46 +01:00
Luke Usher 51061e16af Merge commit 'a652e85069d83d8430730fb66fb96a7f31f72389' 2023-09-18 21:30:46 +01:00
Luke Usher 4e66bc0b32 Squashed 'thirdparty/sljit/' changes from aa71425db..bc844c90a
bc844c90a Implement vex prefix encoding on x86
3551015c7 mips: enable SLJIT_HAVE_REV for SLJIT_MIPS_REV >= 2 (#203)
3dcdb2575 mips: allow detection of StatusFR with prctl() (#202)
03d40019b mips: fix little endian (#197)
8dcad370c loongarch: implement sljit_emit_fop2r() (#201)
90df876ec mips: correctly detect valid FPU and support StatusFR=1 in 32bit (#200)
5628f9065 Implement full f32 register access on arm32 and mips32
2e0a7b9b7 Double the range available for register indicies
ce85ea911 mips: fix SLJIT_COPYSIGN in 32bit (#199)
2d177ccdc mips: fix sljit_emit_fcopy() for 32bit (#198)
0aed5d4dd s390x: sanitize warnings with clang 15 (#192)
831ea3ec4 mips: fix implementation of sljit_emit_fset64 in 32bit mips (BE) (#196)
abda1b90f mips: add missing ADJUST_LOCAL_OFFSET for sljit_emit_select (#195)
bf5e9a145 atomic: avoid unused checks if not implemented (#194)
c4dc1c8a7 Implement sign extraction on x86, arm
1eaeac321 arm: fix unaligned REG_PAIR thumb2 operations (#190)
63de0d9f6 arm: restrict NEON/VFPv3 code in sljit_emit_fset{32,64} (#189)
108a4b410 arm: restore emulation for SLJIT_CTZ in armv6 (#187)
abe5001d8 arm: use armv6 compatible instructions for thumb2 in atomic (#186)
0821be0b6 simd: prevent SIGILL with armv6 (#184)
be7b02a41 Better simd checking in the tests
299ae4ae7 Implement element extension on x86, arm
976e5d813 Be explicit about OS version (#179)
351d9afc7 x86: really detect SSE41 (#181)
8a3c0d1d8 x86: correctly detect SSE41 (#180)
dfff60447 Implement simd zero before lane set on x86, arm
0e4cbc9a8 Rename SLJIT_INT_REGISTER to SLJIT_GP_REGISTER
12d36226e simd: avoid unused parameter warnings (#178)
5c15decbf Implement sign extended lane store on x86, arm
44d8136d1 Implement lane replication on x86, arm
31556f80e Implement lane replication on x86, arm
750987e45 Implement simd mov reg on x86, arm
d17bf952d Implement simd source duplication on x86, arm
d9c74c990 Remove MEM prefix from several simd constants
a2c65c3c7 Implement simd lane transfer on x86, arm
38d96705b Fix incorrect branch offset calculation (#177)
87f9e7791 Rework sljit_get_register_index

git-subtree-dir: thirdparty/sljit
git-subtree-split: bc844c90ac79e7d56c04182e010c6aa8e5957b24
2023-09-18 21:30:41 +01:00
Luke Usher 0c6784294c Merge commit '4e66bc0b32f63b6e3ada74f2d85e3dbdc0b3490e' 2023-09-18 21:30:41 +01:00
Luke Usher 2a6454a710 Merge commit '4f5b5da64f07bd730f37ae842d3404a3189ac13f' 2023-09-18 21:30:31 +01:00
Luke Usher 4f5b5da64f Squashed 'ares/n64/vulkan/parallel-rdp/' changes from c38ea4c21..37bf2967e
37bf2967e Remove dead wsi_timing code.
a8ee1ddac Update to afc61e1050a79b9ab7bb53b8880b055324f7b4a2.

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: 37bf2967e3dd18e059cf5563717bb6c88c261f33
2023-09-18 21:30:31 +01:00
invertego 562bcd7b20 build: miscellaneous fixes (#1233)
Fix various recently introduced build issues.

Some background for the string indexing thing: using operator[] to index
nall::string causes some ambiguous overload errors in 32-bit builds.
This isn't the first time it's come up, but since it's so easy to work
around (using operator() instead) I haven't bothered to try to fix it.
2023-09-18 10:13:20 +01:00
Adrian Siekierka db5f7d7867 fc: Fix MMC5 program mode 1. (#1228) 2023-09-17 16:19:57 +01:00
Adrian Siekierka 3e9ee7423d fc: Implement mapper 31, 218. (#1227)
Resolves https://github.com/ares-emulator/ares/issues/751
2023-09-17 16:19:37 +01:00
Stuff is on GitLab 1215b37448 GDB Server (#1223)
This PR adds a generic GDB server to ares with the ability to be used by
all systems.
For now only the N64 systems was implemented here.

Most documentation on how the server/TCP socket works can be found in
comments.
A detailed documentation for ares developers on how to add this to other
systems is included in the `Readme.md` file.

The feature itself was also already tested by a few people on both linux
and windows (with WSL) and seems to be stable now.
2023-09-17 16:19:13 +01:00
Adrian Siekierka 85488a7404 fc: Implement Action53 mapper. 2023-09-16 15:38:35 +01:00
Adrian Siekierka 5eccee8457 fc: Implement UNROM-512 mapper. 2023-09-16 10:49:43 +01:00
Adrian Siekierka 671669b209 v30mz: Fix interrupt return address on division by zero. 2023-09-16 10:38:50 +01:00
Luke Usher fe9a80b9eb readme: update windows build instructions to add sdl2 2023-09-16 08:43:00 +01:00
Adrian Siekierka 4216a0c2c4 v30mz: fix ENTER/PREPARE opcode implementation, timing 2023-09-14 20:40:42 +01:00
Adrian Siekierka 8ba3bd6348 ws: Properly load trimmed cartridge ROMs - starting from the end of the address space. 2023-09-14 20:40:30 +01:00
Luke Usher 546aed6852 sfc: report PAL and NTSC support for SGB1, allowing it to run at 50hz if "Europe" is the users preferred region. 2023-09-02 22:34:09 +01:00
Adrian Siekierka b11ff923b9 ws: fix sound DMA source/length shadowing 2023-09-02 14:28:48 +01:00
Adrian Siekierka 4509471ce2 ws: fix sound DMA readout values when disabled 2023-09-02 10:49:39 +01:00
NoxyMe 72cd3c5a65 Update master-system.cpp 2023-09-01 16:18:37 +01:00
NoxyMe 929ad45380 Cleaned up some stuff 2023-09-01 16:18:37 +01:00
NoxyMe bb67742864 Added K188in1 Mapper and fixed missleading names
Added K188in1 Mapper
Changed "msx.cpp" to "zemina" as lots of MSX ports dont use the mapper and its only used by ports by Zemina.
2023-09-01 16:18:37 +01:00
NoxyMe b439a0d90a Cleaning up code based on LukeUsher's nits. 2023-09-01 16:18:37 +01:00
NoxyMe a96395a1c1 Added 2 Hap in 1 - David-2 ~ Moai-ui bomul 2023-09-01 16:18:37 +01:00
NoxyMe 70675c0644 Removed some leftovers 2023-09-01 16:18:37 +01:00
NoxyMe 950d9a1967 Added Pak4 (4 PAK All Action) and Hicom Mappers to the SMS core. 2023-09-01 16:18:37 +01:00
Adrian Siekierka 2682e929d8 ws: implement sound DMA hold 2023-09-01 16:16:08 +01:00
PikaSamus cd924569d2 Update README.md: Fixed typo
Fixed typo (descendent → descendant)
2023-08-30 20:29:54 +01:00
Adrian Siekierka 0d27a86e01 ws: fix INT_BASE bit order 2023-08-30 17:50:55 +01:00
Adrian Siekierka 38628f9c85 ws: properly implement INT_BASE port 2023-08-30 17:50:55 +01:00
PikaSamus 405ac8289c fc: Lowered saturation
Lowered saturation value from 2.0 to 1.5 to match other implementations of NTSC (Nintendulator, puNES, Nestopia, etc.)
2023-08-29 09:24:57 +01:00
invertego b29fb91777 nall: move cdrom loading to separate thread
The data is still loaded sequentially and accesses will block until the
data at a given address is available. In the worst case (an immediate
read at the end of the last track) things are no faster than before, but
it still feels better because the UI responds immediately to the user's
load request.

This could be further refined by skipping ahead to serve incoming reads.
2023-08-28 10:12:01 +01:00
invertego 9280622d29 nall: fix relative time in pregap subchannel q 2023-08-28 07:53:15 +01:00
Luke Usher 714ecc1322 ruby: use DwmFlush on Windows rather than OpenGL Swap Interval
DwmFlush is implemented by windows at DWM Driver level, and bypasses OpenGL driver bugs, this should lead to a more stable vsync.
2023-08-24 15:51:55 +01:00
Luke Usher 04525a7426 readme: fix missing a26 core 2023-08-23 20:50:22 +01:00
Luke Usher cc3f4543b1 readme: update core list 2023-08-23 20:30:22 +01:00
Luke Usher 2f65f315e1 desktop-ui: fix build on older compiler versions 2023-08-23 16:38:01 +01:00
Luke Usher de57072f83 ci: switch to wpilibsuite mirror of import-signing-certificate
We still make sure to use the same commit id, so that we don't get any unexpected changes in this transition.
2023-08-23 14:57:06 +01:00
invertego 9225847d40 build: prefer clang only on win/macos/bsd
Up until 67d1647d6e, clang was the assumed
compiler on macOS and BSD, and g++ was the default everywhere else.

After that commit, we defaulted to clang on all platforms if it was
found in the user's path. It has since become clear that while a number
of Linux users have clang in their path, many have not installed
critical packages necessary to build C++ software and are completely
unaware of this state of affairs or which packages need installing.

To simplify matters, we will revert to the default of g++ on systems
other than Windows, macOS, and BSD. Additionally, the `verbose` target
will now print the compiler in use, as this is pretty relevant
information for diagnosing build issues.
2023-08-23 10:03:02 +01:00
Luke Usher 6669e3e84b mos6502: make it more obvious that SLO is combined ASL and ORA 2023-08-22 23:07:18 +01:00
Luke Usher 703e2ea49a desktop-ui: add firmware "scan" function
This allows for firmware to be set from a user selectable firmware directory at the click of a single "Scan" button on the firmware configuration screen.
2023-08-22 20:59:08 +01:00
Luke Usher 40a6b8d36a mia: allow atari 2600 to load .bin format roms 2023-08-22 20:59:08 +01:00
Luke Usher d5381e47a2 mos6502: implement more undocumented opcodes 2023-08-22 10:56:58 +01:00
Luke Usher 7ea42e3201 Update issue templates 2023-08-22 10:08:28 +01:00
Luke Usher c7c80d07ee desktop-ui: restore ability to hide emulation cores from the menu 2023-08-22 09:56:58 +01:00
Luke Usher 5faa14b504 a26: check for PAL string in addition to Europe 2023-08-21 15:41:05 +01:00
Luke Usher 1647d3f4ef a26: fix RIOT timer 2023-08-21 14:40:34 +01:00
Luke Usher 62eadd89e0 a26: fix hmove when position < offset 2023-08-20 21:01:09 +01:00
Luke Usher 9fae1a48c2 a26: fix nusiz missile separation 2023-08-20 20:59:01 +01:00
Luke Usher 166856f4dd a26: better relative object positioning 2023-08-20 20:59:01 +01:00
Luke Usher 48e4ee55c0 a26: fix resmp + implement nusiz for missiles 2023-08-20 20:59:01 +01:00
remutro e8b85524d9 Add mouse support in GUI for Mega CD 2023-08-19 21:40:13 +01:00
Luke Usher 3854a1d1e8 a26 more timing/debugging tweaks
1. Fix timing relationship between cpu and tia,

2. Implement latching of current playfield pixel

3. include "current cycle on this scanline" counts in cpu trace output
2023-08-18 12:07:17 +01:00
Luke Usher 090f8765c2 tracer: fix a crash when logging cpu instructions 2023-08-18 10:07:15 +01:00
Luke Usher 8ae96164ef a26: more timing improvements 2023-08-17 20:42:08 +01:00
Luke Usher 5a647e77ad a26: improve cpu timing with regards to wsync 2023-08-17 19:58:24 +01:00
Luke Usher 6c7bdf9c1a ps1: fix timer precision with recompiler 2023-08-17 13:42:01 +01:00
Luke Usher 21a730b037 n64: add is-viewer log channel rather than forced fputc 2023-08-17 13:18:32 +01:00
Luke Usher 5d38bd3744 ares: allow separate toggling of file/stdout per log channel 2023-08-17 12:57:27 +01:00
Luke Usher 3d3838d8b4 ares: refactor trace/notification logging 2023-08-17 01:43:01 +01:00
Luke Usher d1ddbd504a wip: allowing for separate log channels 2023-08-17 00:23:48 +01:00
Luke Usher c88f26230e ps1: various timing tweaks; passes more tests! 2023-08-16 20:06:13 +01:00
Luke Usher ecab4fdca0 ps1: simulate infinite cpu write fifo
This was causing a lot of software to run too slow; can be restored once we properly emulate that fifo.
2023-08-16 17:15:49 +01:00
Luke Usher 3581be3c44 ps1: prevent fastload exe hook from running more than once
Previously, if *any* title jumped to address 0x80030000, it would crash or reboot or worse.
2023-08-16 11:46:28 +01:00
Luke Usher 2950cef605 ps1: perform color modulation in rgb555 space
fixes Silent Hill loading screen
2023-08-16 10:06:03 +01:00
Luke Usher aab3b447e7 ps1: implement rumble/force feedback for DualShock pads 2023-08-15 22:06:38 +01:00
Luke Usher ea665be25a ruby: fix an issue where weak/strong rumble motors were swapped 2023-08-15 22:06:22 +01:00
Luke Usher e8f4554f24 ps1: use q-sub channel to prevent playing data as cd-da 2023-08-15 19:30:19 +01:00
Luke Usher 0ba0e5f85e ps1: update adsr envelopes immediately on write
fixes broken alert sound in Metal Gear Solid
2023-08-15 18:33:41 +01:00
Luke Usher 7dea8162db ps1: support half sample rate xa-adpcm 2023-08-15 14:33:58 +01:00
Luke Usher d4fd326630 md: sega cd disc swapping 2023-08-15 13:57:00 +01:00
Luke Usher cb19cdbf90 ps1: disc swapping support 2023-08-15 13:35:57 +01:00
Luke Usher ec12ccc6ff ps1: fix l3 and r3 on dualshock controllers 2023-08-14 23:08:08 +01:00
Luke Usher 59deec8b45 ps1: partial dual-shock implementaton
Still missing a few commands + no force feedback
but it is enough to play Ape Escape!
2023-08-14 22:56:15 +01:00
Luke Usher ef53e87b31 ps1: log and recover from premature device transmit
Fixes input in most (all?) titles that don't currently work
Requires more research
2023-08-14 21:17:21 +01:00
Luke Usher 631085f1e6 ps1: separate /ack and transfer delays into two counters 2023-08-14 21:16:43 +01:00
Luke Usher 543dec41b8 ps1: improve controller /ack behavior
fixes controller input in Crash 3 and probably more titles
2023-08-14 19:56:55 +01:00
Luke Usher 72ab5278b2 ps1: digital pad should deassert /ack on invalid command 2023-08-14 14:29:53 +01:00
Luke Usher 93937e8603 ruby: preliminary SDL2 audio driver 2023-08-13 18:55:06 +01:00
invertego 8476c880bc ruby: use pkg-config to link SDL2
Instead of assuming pkg-config is available with bash, test for its
presence. This allows the build to function if using bash without
pkg-config or using cmd with pkg-config.

Instead of assuming the sdl2-config is available if pkg-config indicates
that SDL2 is present, just use pkg-config directly to acquire the
compile and link arguments. Again, this allows the build to work with
cmd (since sdl2-config is a bash script) and if your copy of SDL2 just
didn't come with sdl2-config (for example, from vcpkg).
2023-08-11 18:24:27 +01:00
Luke Usher e39efe170b n64: fix tlb tests 2023-08-11 18:24:13 +01:00
Luke Usher 133044afa9 fix build, oops 2023-08-10 21:06:04 +01:00
Luke Usher c78f06e208 n64: fallback to other paks when gb game is not inserted 2023-08-10 20:26:29 +01:00
Luke Usher f5dc3bbd48 n64: enable transfer pack for all known supported games 2023-08-10 20:26:16 +01:00
Luke Usher ea688c7721 n64: preliminary support for the transfer pak; only enabled for Pokemon Stadium for now 2023-08-10 20:08:28 +01:00
Luke Usher 35c8f5f760 ares: update rumble api to support non-binary vibration 2023-08-09 22:18:19 +01:00
Luke Usher 97aa37f040 ruby: another udev oversight 2023-08-09 22:06:40 +01:00
Luke Usher 8752695b4d ruby: fix oversight in udev driver 2023-08-09 21:54:24 +01:00
Luke Usher cea1adcb44 ruby: fix typo in udev driver 2023-08-09 21:47:53 +01:00
Luke Usher 91b6c7ac14 ruby: update rumble api to support setting weak/strong motors independently 2023-08-09 21:45:20 +01:00
Luke Usher 862bfec9e9 ruby: add rumble support to sdl driver 2023-08-09 21:34:51 +01:00
Luke Usher dad3db5498 ruby: fix keyboard on Windows when using SDL driver 2023-08-09 21:17:05 +01:00
invertego f396a9cf60 recompiler: allocate code cache early in main
On Windows and Linux, ares uses a buffer in the bss section for the
recompiler code cache. On macOS this buffer must be dynamically
allocated for security reasons. The distance between the code cache and
the rest of the ares executable has a large influence on performance on
all platforms because there are microarchitecural thresholds beyond
which branches will never be correctly predicted by the CPU.

To make performance more consistent on macOS, we can make this dynamic
allocation sooner, at the top of main(), before the address space has
become too cluttered with allocations made by other code.
2023-08-09 20:10:02 +01:00
Luke Usher beaab1791a build: include pkg-config for package detection on CI 2023-08-09 20:06:44 +01:00
Luke Usher cc6b26c8d7 build: attempt to fix aarch64 windows builds 2023-08-09 20:03:04 +01:00
Luke Usher a5319fdc07 build: attempt to fix aarch64 windows builds 2023-08-09 20:01:35 +01:00
Luke Usher c2c3c0f0f8 ruby: enable SDL2 input driver on all platforms
NOTE: This currently only works for mingw/msys environments on Windows;
building in a Windows CLI/visual studio will currently not use SDL2.
2023-08-09 19:54:37 +01:00
remutro 3c773a90eb Add audio hotkeys 2023-08-08 22:35:52 +01:00
invertego 8574a5879b n64: advance virtual address in recompiler 2023-08-08 08:04:18 +01:00
invertego 3fd03e7a32 n64: improve idle loop detection
We should compare the virtual address, not physical, to the jump target.
This fixes idle loop detection in games like Turok 2 & 3.
2023-08-08 07:15:31 +01:00
invertego 0130187f82 n64: detect idle loops using jump instruction
In addition to detecting the pattern `beq 0,0,<pc>` for idle loops,
detect the pattern `j <pc>`.

From a selection of about 60 games, about half used beq, a quarter used
jump, and a quarter had nontrivial idle loops.

Additionally, this commit returns the step time for idle loop
acceleration to 64 CPU cycles (128 system cycles). This was accidentally
halved in commit ffcce5823c.
2023-08-07 12:29:13 +01:00
invertego fde1f55f42 n64: begin implementing fpu delays
Some instructions should complete faster for simple inputs (notably
mul), but this is not yet implemented. The extra cycle delay imposed by
the next instruction depending on the output of the previous is also not
implemented, as there is currently no framework for emulating pipeline
effects in the main CPU.
2023-08-07 12:29:13 +01:00
invertego 5337a4bfb8 n64: step one less cycle per mul/div instruction
ares already steps one cycle by default per instruction.
2023-08-07 12:29:13 +01:00
invertego 6bf0d1ffcd n64: step just one cycle per instruction in icache 2023-08-07 12:29:13 +01:00
invertego aff3513931 n64: raise vi line interrupt post-increment
The interrupt should be raised while the current line still equals the
comparison value.
2023-08-06 10:07:48 +01:00
Mastergatto 9fb87dc0c9 Add support for system-wide installation of Ares on Linux, this fixes databases and shaders not loading 2023-08-05 20:46:03 +01:00
invertego a10b9ce0e9 n64: implement rdp clock counter 2023-08-05 13:47:07 +01:00
invertego ffcce5823c n64: track cpu clocks at system rate 2023-08-05 13:47:07 +01:00
invertego 78f439dad1 n64: refactor thread step 2023-08-05 13:47:07 +01:00
Luke Usher 251ea7c0de fix build 2023-08-04 10:23:44 +01:00
hackgrid 12b4530f39 desktop-ui: save settings when closing the settings dialogue 2023-08-04 10:00:38 +01:00
kev4cards c4a5fcea9a n64: clamp to octagon edge instead of scale 2023-08-01 09:54:25 +01:00
Giovanni Bajo ef3d784ae4 n64: update after Granite API change 2023-07-31 16:57:52 +01:00
Luke Usher 14552167a2 Squashed 'thirdparty/sljit/' changes from 51538f102..aa71425db
aa71425db Improve info provided about simd
b5f214edb Implement simd load/store on x86, arm
dd683ab67 build: add barebones cmake for Windows (#123)
13d577046 Implement copysign on ppc, mips
9637d82c9 loongarch: implement copysign (#176)
7c78ea2e1 Implement copysign on s390x
9764568d0 Implement copysign on x86, arm, riscv
d2363accc s390x: fix atomic store emulation when offset in higher int (#175)
18fc38fe3 config: allow CPU specific configurations (#174)
818706a80 loongarch: fixes and other cleanup for portability (#171)
db433181a add base support for LoongArch 64bit (#170)

git-subtree-dir: thirdparty/sljit
git-subtree-split: aa71425db565916d1ebaef107df295d66626e963
2023-07-31 16:56:54 +01:00
Luke Usher 8593f3e980 Merge commit '14552167a2a2b7f73cb7037cd18e81cbd97281db' 2023-07-31 16:56:54 +01:00
Luke Usher a617f2160d Squashed 'ares/n64/vulkan/parallel-rdp/' changes from 8da2be35a..c38ea4c21
c38ea4c21 Add missing file.
f0314a231 Update to 860050d2bf187f1dc215a59df3b2c4f01cada33e.

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: c38ea4c2188719798b81337cd74d4cdb19b819da
2023-07-31 16:56:48 +01:00
Luke Usher a18ad47482 Merge commit 'a617f2160da0e9806a95fe2ebc59edb108dc62d6' 2023-07-31 16:56:48 +01:00
Luke Usher e6c0c15114 n64: prevent segfault when parallel-rdp is unsupported by the host gpu 2023-07-31 16:49:22 +01:00
invertego 2cfb4a98ec n64: implement dual issue conflicts from "fake" uses 2023-07-25 08:43:37 +01:00
invertego 8a89b5a53a n64: fix rsp zero register locking 2023-07-25 08:43:37 +01:00
Luke Usher 352f2202e2 ps1: cd-rom setLoc command should not reset reading flag 2023-07-24 20:30:30 +01:00
Luke Usher 7233efbc39 ps1: implement GTE interrupt quirk 2023-07-24 20:09:11 +01:00
invertego dbd779376a n64: implement rsp dual issue restrictions for cfc2/ctc2 2023-07-24 12:10:49 +01:00
Adrian Siekierka ecba464a58 Revert "ws: fix off-by-one in line IRQ handling"
This reverts commit fb711deb0d.
2023-07-23 20:15:30 +01:00
Adrian Siekierka fb711deb0d ws: fix off-by-one in line IRQ handling 2023-07-23 19:41:26 +01:00
invertego d901f4e336 mia: fix a few n64 serials
- Beetle Adventure Racing (J): Replace "NBB" with "NB8" (fix substitution).
- J.League Live 64: Replace "NLJ" with "NJL" (fix transposition).
- Quake 64: Replace "NKQ" with "NQK" (fix transposition).
- Star Fox 64: Remove "NFP" variant. This was printed on the box/manual
  in some regions but only "NFX" appears in the ROM.
2023-07-23 17:51:59 +01:00
invertego 631b0f6452 n64: implement rsp vu pipeline timings
Implement vector unit (VU) timing including dual issue with the SU.
Tested against real hardware with a random instruction stream generator.

Known limitations:
- Invalid/missing instruction timings not implemented.
- CFC2/CTC2 dual issue restrictions not implemented.
- Some corner-case dual issue restrictions (e.g. LTV + VNOP) not implemented.
  On real hardware, VU instructions with a DE operand are sometimes prevented
  from dual issuing with vector loads under seemingly erroneous conditions.
2023-07-23 17:51:24 +01:00
jcm 3de3f99ad1 Update MoltenVK dependency to version 1.2.4 2023-07-23 10:20:25 +01:00
Adrian Siekierka d201c4827a gb: fix MBC3 RAM bank register width 2023-07-21 22:42:37 +01:00
Adrian Siekierka 2ccd7ba670 gb: fix MBC3 RTC overflow/register handling 2023-07-21 22:42:37 +01:00
1295 changed files with 272526 additions and 60819 deletions

No files matched your search

+2 -1
View File
@@ -13,7 +13,8 @@ Before submitting an issue, please use the search to make sure an issue doesn't
A clear and concise description of what the bug is.
**Rom Information**
Specify the impacted rom filename and any associated hashes (sha256 preferred)
Specify the impacted rom filename and any associated hashes (sha256 preferred);.
The easiest way to do this is to open the game in ares, and go to "Tools -> Manifest" and copy/paste the contents.
```
file: Some Game (REGION).ext
+21 -6
View File
@@ -8,6 +8,7 @@ on:
jobs:
build:
strategy:
fail-fast: false
matrix:
program:
- ares
@@ -18,14 +19,14 @@ jobs:
windres: windres
shell: 'msys2 {0}'
msystem: clang64
install: mingw-w64-clang-x86_64-clang
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-pkgconf mingw-w64-clang-x86_64-SDL2
- name: windows-arm64
os: windows-latest
compiler: clang++ --target=aarch64-w64-windows-gnu --sysroot=/clangarm64 -resource-dir=/clangarm64/lib/clang/$(basename "$(clang++ -print-resource-dir)")
windres: windres --target=aarch64-w64-windows-gnu
shell: 'msys2 {0}'
msystem: clang64
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-aarch64-clang
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-pkgconf mingw-w64-clang-aarch64-SDL2
- name: windows-msvc-x64
os: windows-latest
compiler: clang++
@@ -77,7 +78,8 @@ jobs:
sudo apt-get install libsdl2-dev libgtk-3-dev libao-dev libopenal-dev
- name: "macOS: Import Certificate"
if: runner.os == 'macOS' && github.event_name != 'pull_request'
uses: devbotsxyz/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
uses: wpilibsuite/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
with:
certificate-data: ${{ secrets.MACOS_CERTIFICATE_DATA }}
certificate-passphrase: ${{ secrets.MACOS_CERTIFICATE_PASSPHRASE }}
@@ -85,7 +87,7 @@ jobs:
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
- name: Checkout source code
uses: actions/checkout@v2
- name: "macOS: recover cache"
- name: "macOS: recover MoltenVK cache"
if: runner.os == 'macOS'
uses: actions/cache@v3
env:
@@ -93,11 +95,23 @@ jobs:
with:
path: thirdparty/MoltenVK/MoltenVK
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
- name: "macOS: recover SDL cache"
if: runner.os == 'macOS'
uses: actions/cache@v3
env:
cache-name: sdl
with:
path: thirdparty/SDL/SDL
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/SDL/HEAD') }}
- name: Install macOS Dependencies
if: runner.os == 'macOS'
run: |
brew install make ninja
brew install make ninja cmake
pip install --no-input setuptools
echo "MAKE=gmake" >> $GITHUB_ENV
pushd thirdparty/SDL
./build-SDL.sh
popd
pushd thirdparty/MoltenVK
./build-moltenvk.sh
popd
@@ -111,7 +125,7 @@ jobs:
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler="${{ matrix.platform.compiler }}" windres="${{ matrix.platform.windres }}"
- name: "macOS: Make universal app"
if: runner.os == 'macOS'
run: scripts/macos-make-universal.sh
run: scripts/macos-make-universal.sh build=optimized local=false
env:
MAKEFLAGS: -j3
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
@@ -129,6 +143,7 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com'
- name: "macOS: notarize"
if: runner.os == 'macOS' && github.event_name != 'pull_request'
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
run: |
ditto -c -k --keepParent desktop-ui/out/ares.app /tmp/ares.zip
xcrun notarytool submit /tmp/ares.zip --apple-id "$MACOS_NOTARIZATION_USERNAME" --password "$MACOS_NOTARIZATION_PASSWORD" --team-id "$MACOS_NOTARIZATION_TEAMID" --wait
+4 -1
View File
@@ -1,9 +1,12 @@
*.moc
*.user
.vs/
.vscode/
.idea/
cmake-*/
obj/
out/
obj-amd64/
obj-arm64/
out-amd64/
out-arm64/
out-arm64/
+34
View File
@@ -436,3 +436,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
----------------------------------------------------------------------
----------------------------------------------------------------------
ymfm
BSD 3-Clause License
Copyright (c) 2021, Aaron Giles
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.
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.
----------------------------------------------------------------------
+8 -7
View File
@@ -3,7 +3,7 @@
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/higan-emu/ares/blob/master/LICENSE)
**ares** is a multi-system emulator that began development on October 14th, 2004.
It is a descendent of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
It is a descendant of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
Official Releases
-----------------
@@ -34,11 +34,12 @@ ares supports additional audio drivers besides the ALSA drivers included above.
By default, GTK3 is used, but support for GTK2 is available. You will need to install the additional package `libgtk2.0-dev` as well as specifying the command line option `hiro=gtk2` at compile time.
###### SDL2 for input
If you would like to use SDL for input, you will need to install the following the `libsdl2-dev` and `libsdl2-0.0` packages and perform a clean build of ares. You should then be able to select SDL for input in the Settings > Drivers menu.
If you would like to use SDL for input (e.g. for using a controller), you will need to install the `libsdl2-dev` and `libsdl2-2.0-0` packages and perform a clean build of ares.
You should then be able to select SDL for input in the Settings > Drivers menu.
##### Building with clang
clang++ is now the preferred compiler for ares. If it is detected, the build will default to building with clang. It is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
clang++ is now the preferred compiler for ares. If clang is detected on Windows/macOS/BSD, it will be selected by default. On Linux and other platforms, g++ remains the default if present. To build with clang, it is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
--------------
@@ -47,7 +48,7 @@ clang++ is now the preferred compiler for ares. If it is detected, the build wil
To build on Windows, using MSYS2 is recommended which can be download [here](https://www.msys2.org/). Follow the instructions
on this page to install and setup an appropriate MINGW64 environment. Running the command:
```
pacman -S --needed base-devel mingw-w64-x86_64-toolchain
pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2
```
from the MSYS2 MSYS terminal should setup everything you need to compile ares. Note that in order to compile, you will want to be in a MINGW64 terminal window after install and setup is complete.
@@ -55,7 +56,7 @@ from the MSYS2 MSYS terminal should setup everything you need to compile ares. N
clang is available through Visual Studio (or Build Tools for Visual Studio) through its installer and can be used to build ares. You will still need to supply GNU make in this instance. MSYS2 also offers a clang environment. You will want to make sure you select the clangw64 option during installation of MSYS2 which should provide and additional CLANG64 pre-configured environment. Install the clang toolchain package from the MSYS2 terminal:
```
pacman -S mingw-w64-clang-x86_64-toolchain
pacman -S mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-SDL2
```
Once complete, open a CLANG64 terminal window and proceed with building ares.
@@ -90,7 +91,7 @@ make clean
#### Building specific cores
If you would like to build a subset of cores, you can specify the `cores="core1 core2"` option. Currently available cores:
```
a26 fc sfc n64 sg ms md ps1 pce ng msx cv gb gba ws ngp spec
a26 fc sfc n64 sg ms md ps1 pce ng msx cv myvision gb gba ws ngp spec
```
Build Output
@@ -148,4 +149,4 @@ High-level Components
Contributing
------------
Please join our discord to chat with other ares developers: https://discord.com/invite/gz2quhk2kv
Please join our discord to chat with other ares developers: https://discord.com/invite/gz2quhk2kv
-4
View File
@@ -13,10 +13,6 @@ ares.objects := ares ares-fixed-allocator
$(object.path)/ares.o: $(ares.path)/ares/ares.cpp
$(object.path)/ares-fixed-allocator.o: $(ares.path)/ares/memory/fixed-allocator.cpp
ifeq ($(platform),macos)
ares.dylibs :=
endif
ifeq ($(vulkan),true)
flags += -DVULKAN
endif
+1
View File
@@ -24,6 +24,7 @@ auto CPU::main() -> void {
}
auto CPU::step(u32 clocks) -> void {
if(io.rdyLine == 1) io.scanlineCycles += clocks;
Thread::step(clocks);
Thread::synchronize();
}
+1
View File
@@ -42,6 +42,7 @@ struct CPU : MOS6502, Thread {
//protected:
struct IO {
n1 rdyLine = 1;
n32 scanlineCycles = 0;
} io;
};
+1 -1
View File
@@ -5,7 +5,7 @@ auto CPU::Debugger::load(Node::Object parent) -> void {
auto CPU::Debugger::instruction() -> void {
if(tracer.instruction->enabled() && tracer.instruction->address(cpu.PC)) {
tracer.instruction->notify(cpu.disassembleInstruction(), cpu.disassembleContext());
tracer.instruction->notify(cpu.disassembleInstruction(), {cpu.disassembleContext(), " [c:", cpu.io.scanlineCycles, " l:", tia.io.vcounter, " p:", tia.io.hcounter, "]"});
}
}
+2
View File
@@ -1,5 +1,7 @@
inline auto CPU::readBus(n16 address) -> n8 {
address &= 0x1fff;
if(auto result = platform->cheat(address)) return *result;
n8 data = cartridge.read(address);
if(address.bit(12) == 0 && address.bit(7) == 0) {
+3 -2
View File
@@ -1,6 +1,8 @@
auto CPU::read(n16 address) -> n8 {
step(1);
if(io.rdyLine == 0) {
while (io.rdyLine == 0) {
while(io.rdyLine == 0) {
step(1);
}
@@ -8,7 +10,6 @@ auto CPU::read(n16 address) -> n8 {
return MDR;
}
step(1);
MDR = readBus(address);
return MDR;
}
+13 -4
View File
@@ -7,12 +7,18 @@ auto RIOT::writeRam(n8 address, n8 data) -> void {
}
auto RIOT::readIo(n8 address) -> n8 {
switch(address) {
switch(address.bit(0, 2)) {
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;
case 0x04: timer.interruptFlag = address.bit(3); return timer.counter;
case 0x05: case 0x07: {
n8 output = 0;
output.bit(6) = 0; //TODO: PA7 IRQ Flag
output.bit(7) = timer.interruptFlag;
return output;
}
}
debug(unimplemented, "[RIOT] IO read ", hex(address));
@@ -25,11 +31,14 @@ auto RIOT::writeIo(n8 address, n8 data) -> void {
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: {
case 0x14: case 0x15: case 0x16: case 0x17:
case 0x1c: case 0x1d: case 0x1e: case 0x1f: {
const n16 intervals[] = {1, 8, 64, 1024};
timer.counter = data;
timer.interval = intervals[address - 0x14];
timer.interval = intervals[address.bit(0, 1)];
timer.reload = timer.interval;
timer.interruptFlag = 0;
timer.interruptEnable = address.bit(3);
return; }
}
+5 -11
View File
@@ -19,18 +19,12 @@ auto RIOT::unload() -> void {
}
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;
if(--timer.counter == 0xff) {
timer.interval = 1;
timer.interruptFlag = 1;
}
step(timer.interval);
}
auto RIOT::step(u32 clocks) -> void {
+2
View File
@@ -30,6 +30,8 @@ struct RIOT : Thread {
u8 counter;
n16 interval;
n16 reload;
n1 interruptEnable;
n1 interruptFlag;
} timer;
struct Port {
+2
View File
@@ -6,6 +6,8 @@ auto RIOT::serialize(serializer& s) -> void {
s(timer.counter);
s(timer.interval);
s(timer.reload);
s(timer.interruptEnable);
s(timer.interruptFlag);
for(auto n : range(2)) {
s(port[n].data);
+1 -1
View File
@@ -1,4 +1,4 @@
static const string SerializerVersion = "v131";
static const string SerializerVersion = "v132";
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
+10 -15
View File
@@ -33,9 +33,9 @@ auto TIA::write(n8 address, n8 data) -> void {
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 0x0d: writeQueue.add(address, data, 2); return; // PF0
case 0x0e: writeQueue.add(address, data, 2); return; // PF1
case 0x0f: writeQueue.add(address, data, 2); return; // PF2
case 0x10: resp(0); return; // RESP0
case 0x11: resp(1); return; // RESP1
case 0x12: resm(0); return; // RESM0
@@ -47,8 +47,8 @@ auto TIA::write(n8 address, n8 data) -> void {
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 0x1b: writeQueue.add(address, data, 1); return; // GRP0
case 0x1c: writeQueue.add(address, data, 1); 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
@@ -72,8 +72,9 @@ auto TIA::write(n8 address, n8 data) -> void {
auto TIA::vsync(n1 state) -> void {
if(io.vsync && !state) {
if(!io.vsync && state) {
frame();
tia.io.vcounter = 0;
}
io.vsync = state;
@@ -124,23 +125,17 @@ auto TIA::grp(n1 index, n8 data) -> void {
}
auto TIA::resp(n1 index) -> void {
player[index].position = (io.hcounter < 68 ? 3 : io.hcounter - 68 + 8) % 160;
player[index].position = (io.hcounter - 68 + 5) % 160;
}
auto TIA::resm(n1 index) -> void {
missile[index].position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7) % 160;
missile[index].position = (io.hcounter - 68 + 4) % 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);
ball.position = (io.hcounter - 68 + 4) % 160;
}
+60 -25
View File
@@ -13,12 +13,12 @@ TIA tia;
auto TIA::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("TIA");
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
screen = node->append<Node::Video::Screen>("Screen", 180, displayHeight());
screen->colors(1 << 7, {&TIA::color, this});
screen->setSize(160, displayHeight());
screen->setScale(1.0, 1.0);
screen->setAspect(12.0, 7.0);
screen->setViewport(0, 0, 160, displayHeight());
screen->setSize(180, displayHeight());
screen->setScale(2.0, 1.0);
Region::PAL() ? screen->setAspect(38.0, 45.0) : screen->setAspect(4.0, 5.0);
screen->setViewport(0, 0, 180, displayHeight());
stream = node->append<Node::Audio::Stream>("Audio");
stream->setChannels(1);
@@ -35,19 +35,22 @@ auto TIA::unload() -> void {
}
auto TIA::main() -> void {
scanline();
io.vcounter++;
io.hmoveTriggered = 255;
scanline();
io.vcounter++;
io.hmoveTriggered = 255;
}
auto TIA::scanline() -> void {
cpu.rdyLine(1);
cpu.io.scanlineCycles = 0;
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
auto x = io.hcounter - 68;
if(x >= 0 && io.vcounter < vlines()) pixel(x);
writeQueue.step();
auto x = io.hcounter - 68;
auto y = io.vcounter - voffset();
if(x >= 0 && y > 0 && y < vlines()) pixel(x);
runAudio();
step();
if(io.hcounter == 0) cpu.io.rdyLine = 1;
}
// Prevent an infinite emulator hang when games miss vblank
@@ -58,7 +61,9 @@ auto TIA::scanline() -> void {
}
auto TIA::pixel(n8 x) -> void {
auto output = screen->pixels().data() + (io.vcounter * 160);
auto y = io.vcounter - voffset();
auto output = screen->pixels().data() + (y * 180) + 10;
if(y >= displayHeight()) return;
// 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)) {
@@ -98,10 +103,13 @@ auto TIA::pixel(n8 x) -> void {
}
auto TIA::runPlayfield(n8 x) -> n1 {
auto pos = x >> 2;
if((x % 4) == 0) {
auto pos = x >> 2;
playfield.pixel = (!playfield.mirror || pos < 20) ? playfield.graphics.bit(pos % 20)
: playfield.graphics.bit(19 - (pos % 20));
}
if(!playfield.mirror || pos < 20) return playfield.graphics.bit(pos % 20);
return playfield.graphics.bit(19 - (pos % 20));
return playfield.pixel;
}
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
@@ -136,10 +144,45 @@ auto TIA::runPlayer(n8 x, n1 index) -> n1 {
auto TIA::runMissile(n8 x, n1 index) -> n1 {
auto& missile = this->missile[index];
if (!missile.enable || missile.reset) return 0;
auto& player = this->player[index];
auto position = missile.position;
if(missile.reset) {
auto offset = 3;
if(player.size == 5) offset = 6;
if(player.size == 7) offset = 10;
missile.position = player.position + offset;
return 0;
}
if(!missile.enable) return 0;
const int missileSizes[4] = {1, 2, 4, 8};
return x >= missile.position && x < missile.position + missileSizes[missile.size];
// Handle player stretch capability
auto width = missileSizes[missile.size];
auto repeatWidth = width;
if(player.size == 5) repeatWidth *= 2;
if(player.size == 7) repeatWidth *= 4;
// 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) {
return 1;
}
position = (position + (spacing + repeatWidth)) % 160;
}
return 0;
}
auto TIA::runBall(n8 x) -> n1 {
@@ -150,14 +193,6 @@ auto TIA::runBall(n8 x) -> n1 {
}
auto TIA::frame() -> void {
auto height = io.vcounter;
if(height <= 0) {
debug(unusual, "invalid screen height");
height = vlines();
}
screen->setViewport(0, 0, 160, height);
screen->frame();
scheduler.exit(Event::Frame);
}
+3 -1
View File
@@ -4,7 +4,8 @@ struct TIA : Thread {
Node::Audio::Stream stream;
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
auto displayHeight() const -> u32 { return Region::NTSC() ? 192 : 228; }
auto displayHeight() const -> u32 { return Region::PAL() ? 243 : 228; }
auto voffset() const -> s32 { return Region::PAL() ? 40 : 20; }
//tia.cpp
auto load(Node::Object) -> void;
@@ -79,6 +80,7 @@ struct TIA : Thread {
struct {
n20 graphics;
n1 pixel;
n1 mirror;
n1 scoreMode;
n1 priority;
+1 -1
View File
@@ -23,7 +23,7 @@ auto TIA::WriteQueue::step() -> void {
auto TIA::WriteQueue::QueueItem::commit() -> void {
switch(address) {
case 0x01: tia.io.vblank = data.bit(1); if(tia.io.vblank == 0) { tia.io.vcounter = 0; } return; // VBLANK
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
+10
View File
@@ -1,5 +1,6 @@
#include <ares/ares.hpp>
#include <ares/debug/debug.cpp>
#include <nall/gdb/server.cpp>
#include <ares/node/node.cpp>
#include <ares/resource/resource.cpp>
@@ -8,4 +9,13 @@ namespace ares {
Platform* platform = nullptr;
bool _runAhead = false;
const string Name = "ares";
const string Version = "136";
const string Copyright = "ares team, Near";
const string License = "ISC";
const string LicenseURI = "https://opensource.org/licenses/ISC";
const string Website = "ares-emu.net";
const string WebsiteURI = "https://ares-emu.net/";
const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
}
+8 -10
View File
@@ -40,16 +40,14 @@ using namespace nall;
using namespace nall::primitives;
namespace ares {
static const string Name = "ares";
static const string Version = "133";
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-emu.net";
static const string WebsiteURI = "https://ares-emu.net/";
//incremented only when serialization format changes
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
extern const string Name;
extern const string Version;
extern const string Copyright;
extern const string License;
extern const string LicenseURI;
extern const string Website;
extern const string WebsiteURI;
extern const u32 SerializerSignature;
namespace VFS {
using Pak = shared_pointer<vfs::directory>;
+22 -8
View File
@@ -1,18 +1,32 @@
#include <ares/ares.hpp>
#if !defined(PLATFORM_MACOS)
#define STATIC_ALLOCATION
#endif
namespace ares::Memory {
#if defined(PLATFORM_MACOS)
//stub for unsupported platforms
FixedAllocator::FixedAllocator() {
}
#else
alignas(4096) u8 fixedBuffer[128_MiB];
constexpr u32 fixedBufferSize = 128_MiB;
#if defined(STATIC_ALLOCATION)
u8 fixedBuffer[fixedBufferSize + 64_KiB];
#endif
FixedAllocator::FixedAllocator() {
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
u8* buffer = nullptr;
#if defined(STATIC_ALLOCATION)
//align to 64 KiB (maximum page size of any supported OS)
auto offset = -(uintptr)fixedBuffer % 64_KiB;
//set protection to executable
if(memory::protect(fixedBuffer + offset, fixedBufferSize, true)) {
//use static allocation
buffer = fixedBuffer + offset;
}
#endif
_allocator.resize(fixedBufferSize, bump_allocator::executable, buffer);
}
#endif
auto FixedAllocator::get() -> bump_allocator& {
static FixedAllocator allocator;
@@ -71,9 +71,7 @@ struct Instruction : Tracer {
if(!enabled()) return;
if(_omitted) {
PlatformLog({
"[Omitted: ", _omitted, "]\n"}
);
PlatformLog(shared(), {"[Omitted: ", _omitted, "]"});
_omitted = 0;
}
@@ -84,7 +82,7 @@ struct Instruction : Tracer {
context, " ",
extra
};
PlatformLog({output.strip(), "\n"});
PlatformLog(shared(), {output.strip()});
}
auto serialize(string& output, string depth) -> void override {
@@ -6,12 +6,7 @@ struct Notification : Tracer {
auto notify(const string& message = {}) -> void {
if(!enabled()) return;
if(message) {
PlatformLog({_component, " ", _name, ": ", message, "\n"});
} else {
PlatformLog({_component, " ", _name, "\n"});
}
PlatformLog(shared(), message);
}
protected:
+21 -5
View File
@@ -6,24 +6,40 @@ struct Tracer : Debugger {
}
auto component() const -> string { return _component; }
auto enabled() const -> bool { return _enabled; }
auto enabled() const -> bool { return file() || terminal(); }
auto prefix() const -> bool { return _prefix; }
auto terminal() const -> bool { return _terminal; }
auto file() const -> bool { return _file; }
auto autoLineBreak() const -> bool { return _autoLineBreak; }
auto setToggle(function<void ()> toggle) -> void { _toggle = toggle; }
auto setComponent(string component) -> void { _component = component; }
auto setEnabled(bool enabled) -> void { _enabled = enabled; }
auto setPrefix(bool prefix) -> void { _prefix = prefix; }
auto setTerminal(bool terminal) -> void { _terminal = terminal; if(_toggle) _toggle(); }
auto setFile(bool file) -> void { _file = file; if(_toggle) _toggle(); }
auto setAutoLineBreak(bool autoLineBreak) -> void { _autoLineBreak = autoLineBreak; }
auto serialize(string& output, string depth) -> void override {
Debugger::serialize(output, depth);
output.append(depth, " component: ", _component, "\n");
output.append(depth, " enabled: ", _enabled, "\n");
output.append(depth, " prefix: ", _prefix, "\n");
output.append(depth, " terminal: ", _terminal, "\n");
output.append(depth, " file: ", _file, "\n");
}
auto unserialize(Markup::Node node) -> void override {
Debugger::unserialize(node);
_component = node["component"].string();
_enabled = node["enabled"].boolean();
_prefix = node["prefix"].boolean();
_terminal = node["terminal"].boolean();
_file = node["file"].boolean();
}
protected:
function<void ()> _toggle;
string _component;
bool _enabled = false;
bool _prefix = false;
bool _terminal = false;
bool _file = false;
bool _autoLineBreak = true;
};
+11 -3
View File
@@ -2,9 +2,17 @@ struct Rumble : Input {
DeclareClass(Rumble, "input.rumble")
using Input::Input;
auto enable() const -> bool { return _enable; }
auto setEnable(bool enable) -> void { _enable = enable; }
auto weakValue() const -> u16 { return _weak; }
auto strongValue() const -> u16 { return _strong; }
auto setValues(u16 strong, u16 weak) -> void { _weak = weak; _strong = strong; }
auto setWeak(u16 weak) -> void { _weak = weak; }
auto setStrong(u16 strong) -> void { _strong = strong; }
// For systems with binary motors
auto enable() const -> bool { return _weak > 0 || _strong > 0; }
auto setEnable(bool enable) -> void { _weak = enable ? 65535 : 0; _strong = enable ? 65535 : 0; }
protected:
bool _enable = 0;
u16 _weak = 0;
u16 _strong = 0;
};
+1 -1
View File
@@ -94,7 +94,7 @@ namespace ares::Core {
// <ares/platform.hpp> forward declarations
static auto PlatformAttach(Node::Object) -> void;
static auto PlatformDetach(Node::Object) -> void;
static auto PlatformLog(string_view) -> void;
static auto PlatformLog(Node::Debugger::Tracer::Tracer tracer, string_view) -> void;
#include <ares/node/attribute.hpp>
#include <ares/node/class.hpp>
+20 -6
View File
@@ -76,6 +76,11 @@ auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
_viewportHeight = height;
}
auto Screen::setOverscan(bool overscan) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_overscan = overscan;
}
auto Screen::setSize(u32 width, u32 height) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_width = width;
@@ -122,6 +127,11 @@ auto Screen::setColorBleed(bool colorBleed) -> void {
_colorBleed = colorBleed;
}
auto Screen::setColorBleedWidth(u32 width) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_colorBleedWidth = width;
}
auto Screen::setInterframeBlending(bool interframeBlending) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_interframeBlending = interframeBlending;
@@ -228,14 +238,18 @@ auto Screen::refresh() -> void {
}
}
if(_colorBleed) {
if (_colorBleed) {
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
for(u32 y : range(height)) {
for (u32 y : range(height)) {
auto target = output + y * width;
for(u32 x : range(width)) {
auto a = target[x];
auto b = target[x + (x != width - 1)];
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
for (u32 x : range(0, width, _colorBleedWidth)) {
for (u32 offset = 0; offset < _colorBleedWidth && (x + offset) < width; ++offset) {
u32 next = x + _colorBleedWidth;
if (next + offset >= width) next = x;
auto a = target[x + offset];
auto b = target[next + offset];
target[x + offset] = (a + b - ((a ^ b) & mask)) >> 1;
}
}
}
}
+5
View File
@@ -16,6 +16,7 @@ struct Screen : Video {
auto scaleY() const -> f64 { return _scaleY; }
auto aspectX() const -> f64 { return _aspectX; }
auto aspectY() const -> f64 { return _aspectY; }
auto overscan() const -> bool { return _overscan; }
auto colors() const -> u32 { return _colors; }
auto pixels(bool frame = 0) -> array_span<u32>;
@@ -37,6 +38,7 @@ struct Screen : Video {
auto setSize(u32 width, u32 height) -> void;
auto setScale(f64 scaleX, f64 scaleY) -> void;
auto setAspect(f64 aspectX, f64 aspectY) -> void;
auto setOverscan(bool overscan) -> void;
auto setSaturation(f64 saturation) -> void;
auto setGamma(f64 gamma) -> void;
@@ -44,6 +46,7 @@ struct Screen : Video {
auto setFillColor(u32 fillColor) -> void;
auto setColorBleed(bool colorBleed) -> void;
auto setColorBleedWidth(u32 width) -> void;
auto setInterframeBlending(bool interframeBlending) -> void;
auto setRotation(u32 rotation) -> void;
@@ -78,7 +81,9 @@ protected:
f64 _luminance = 1.0;
u32 _fillColor = 0;
bool _colorBleed = false;
bool _colorBleedWidth = 1;
bool _interframeBlending = false;
bool _overscan = true;
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
function<n64 (n32)> _color;
+3 -2
View File
@@ -15,11 +15,12 @@ struct Platform {
virtual auto detach(Node::Object) -> void {}
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
virtual auto event(Event) -> void {}
virtual auto log(string_view message) -> void {}
virtual auto log(Node::Debugger::Tracer::Tracer, 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 {}
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
};
extern Platform* platform;
@@ -30,5 +31,5 @@ namespace ares::Core {
// <ares/node/node.hpp> forward declarations
auto PlatformAttach(Node::Object node) -> void { if(platform && node->name()) platform->attach(node); }
auto PlatformDetach(Node::Object node) -> void { if(platform && node->name()) platform->detach(node); }
auto PlatformLog(string_view text) -> void { if(platform) platform->log(text); }
auto PlatformLog(Node::Debugger::Tracer::Tracer node, string_view text) -> void { if(platform) platform->log(node, text); }
}
+1 -1
View File
@@ -41,7 +41,7 @@ private:
vector<Thread*> _threads;
bool _synchronize = false;
friend class Thread;
friend struct Thread;
};
extern Scheduler scheduler;
+1 -1
View File
@@ -46,5 +46,5 @@ protected:
u64 _scalar = 0;
u64 _clock = 0;
friend class Scheduler;
friend struct Scheduler;
};
+10 -1
View File
@@ -12,10 +12,13 @@ ares.objects += $(if $(findstring ym2612,$(ares.components)),ares-audio-ym2612)
ares.objects += $(if $(findstring m24c,$(ares.components)),ares-eeprom-m24c)
ares.objects += $(if $(findstring m93lcx6,$(ares.components)),ares-eeprom-m93lcx6)
ares.objects += $(if $(findstring i8255,$(ares.components)),ares-io-i8255)
ares.objects += $(if $(findstring arm7tdmi,$(ares.components)),ares-processor-arm7tdmi)
ares.objects += $(if $(findstring gsu,$(ares.components)),ares-processor-gsu)
ares.objects += $(if $(findstring hg51b,$(ares.components)),ares-processor-hg51b)
ares.objects += $(if $(findstring huc6280,$(ares.components)),ares-processor-huc6280)
ares.objects += $(if $(findstring i8080,$(ares.components)),ares-processor-i8080)
ares.objects += $(if $(findstring m68hc05,$(ares.components)),ares-processor-m68hc05)
ares.objects += $(if $(findstring m68000,$(ares.components)),ares-processor-m68000)
ares.objects += $(if $(findstring mos6502,$(ares.components)),ares-processor-mos6502)
@@ -30,6 +33,8 @@ ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
ares.objects += $(if $(findstring sst39sf0x0,$(ares.components)),ares-flash-sst39sf0x0)
ares.objects += $(if $(findstring tms9918,$(ares.components)),ares-video-tms9918)
ares.objects += $(if $(findstring v9938,$(ares.components)),ares-video-v9938)
@@ -39,16 +44,18 @@ $(object.path)/ares-audio-sn76489.o: $(ares.path)/component/audio/sn76489/s
$(object.path)/ares-audio-t6w28.o: $(ares.path)/component/audio/t6w28/t6w28.cpp
$(object.path)/ares-audio-ym2149.o: $(ares.path)/component/audio/ym2149/ym2149.cpp
$(object.path)/ares-audio-ym2413.o: $(ares.path)/component/audio/ym2413/ym2413.cpp
$(object.path)/ares-audio-ym2610.o: $(ares.path)/component/audio/ym2610/ym2610.cpp
$(object.path)/ares-audio-ym2612.o: $(ares.path)/component/audio/ym2612/ym2612.cpp
$(object.path)/ares-eeprom-m24c.o: $(ares.path)/component/eeprom/m24c/m24c.cpp
$(object.path)/ares-eeprom-m93lcx6.o: $(ares.path)/component/eeprom/m93lcx6/m93lcx6.cpp
$(object.path)/ares-io-i8255.o: $(ares.path)/component/io/i8255/i8255.cpp
$(object.path)/ares-processor-arm7tdmi.o: $(ares.path)/component/processor/arm7tdmi/arm7tdmi.cpp
$(object.path)/ares-processor-gsu.o: $(ares.path)/component/processor/gsu/gsu.cpp
$(object.path)/ares-processor-hg51b.o: $(ares.path)/component/processor/hg51b/hg51b.cpp
$(object.path)/ares-processor-huc6280.o: $(ares.path)/component/processor/huc6280/huc6280.cpp
$(object.path)/ares-processor-i8080.o: $(ares.path)/component/processor/i8080/i8080.cpp
$(object.path)/ares-processor-m68hc05.o: $(ares.path)/component/processor/m68hc05/m68hc05.cpp
$(object.path)/ares-processor-m68000.o: $(ares.path)/component/processor/m68000/m68000.cpp
$(object.path)/ares-processor-mos6502.o: $(ares.path)/component/processor/mos6502/mos6502.cpp
@@ -63,5 +70,7 @@ $(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
$(object.path)/ares-flash-sst39sf0x0.o: $(ares.path)/component/flash/sst39sf0x0/sst39sf0x0.cpp
$(object.path)/ares-video-tms9918.o: $(ares.path)/component/video/tms9918/tms9918.cpp
$(object.path)/ares-video-v9938.o: $(ares.path)/component/video/v9938/v9938.cpp
-106
View File
@@ -1,106 +0,0 @@
auto YM2610::read(n2 address) -> n8 {
switch(address) {
case 0: return fm.readStatus();
case 1: return ssg.read();
case 2: return pcmStatus();
}
debug(unimplemented, "[YM2610] read ", hex(address));
return 0;
}
auto YM2610::pcmStatus() -> n8 {
n8 output;
for(auto c : range(6)) output.bit(c) = pcmA.channels[c].ended;
output.bit(7) = pcmB.ended;
return output;
}
auto YM2610::write(n2 address, n8 data) -> void {
switch(address) {
case 0: {
registerAddress = 0x000 | data;
if (registerAddress <= 0xf) ssg.select(registerAddress);
else fm.writeAddress(registerAddress);
return;
}
case 1: return writeLower(data);
case 2: registerAddress = 0x100 | data; fm.writeAddress(registerAddress); return;
case 3: return writeUpper(data);
}
}
auto YM2610::writeLower(n8 data) -> void {
switch(registerAddress) {
case range16(0x000, 0x00f):
ssg.write(data);
return;
case 0x010:
pcmB.repeat = data.bit(4);
if(data.bit(0)) pcmB.power();
if(data.bit(7)) pcmB.beginPlay();
else pcmB.playing = 0;
return;
case 0x011: pcmB.left = data.bit(7);
pcmB.right = data.bit(6); return;
case 0x012: pcmB.startAddress.bit( 8, 15) = data; return;
case 0x013: pcmB.startAddress.bit(16, 23) = data; return;
case 0x014: pcmB.endAddress.bit ( 8, 15) = data; return;
case 0x015: pcmB.endAddress.bit (16, 23) = data; return;
case range3(0x016, 0x018):
debug(unimplemented, "[YM2610::ADPCMB] Write ", hex(registerAddress), " = ", data);
return;
case 0x019: pcmB.delta.bit ( 0, 7) = data; return;
case 0x01a: pcmB.delta.bit ( 8, 15) = data; return;
case 0x01b: pcmB.volume = data; return;
case 0x01c:
for(auto c : range(6)) {
pcmA.channels[c].endedMask = data.bit(c);
if(data.bit(c)) pcmA.channels[c].ended = 0;
}
pcmB.endedMask = data.bit(7);
if(data.bit(7)) pcmB.ended = 0;
return;
default:
if(registerAddress >= 0x01d && registerAddress <= 0x1ff) {
fm.writeData(data);
return;
}
break;
}
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
}
auto YM2610::writeUpper(n8 data) -> void {
switch(registerAddress) {
case 0x100:
for(auto n : range(6)) {
if(!data.bit(n)) continue;
if(data.bit(7)) {
pcmA.channels[n].keyOff();
continue;
}
pcmA.channels[n].keyOn();
}
return;
case 0x101: pcmA.volume = data.bit(0, 5); return;
case range6(0x108, 0x10d): pcmA.channels[registerAddress - 0x108].volume = data.bit(0,4);
pcmA.channels[registerAddress - 0x108].left = data.bit(7);
pcmA.channels[registerAddress - 0x108].right = data.bit(6); return;
case range6(0x110, 0x115): pcmA.channels[registerAddress - 0x110].startAddress.bit( 8, 15) = data; return;
case range6(0x118, 0x11d): pcmA.channels[registerAddress - 0x118].startAddress.bit(16, 23) = data; return;
case range6(0x120, 0x125): pcmA.channels[registerAddress - 0x120].endAddress.bit ( 8, 15) = data; return;
case range6(0x128, 0x12d): pcmA.channels[registerAddress - 0x128].endAddress.bit (16, 23) = data; return;
default:
if(registerAddress >= 0x130 && registerAddress <= 0x1ff) {
fm.writeAddress(registerAddress);
fm.writeData(data);
return;
}
break;
}
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
}
-153
View File
@@ -1,153 +0,0 @@
auto YM2610::PCMA::power() -> void {
for(auto c : range(6)) {
channels[c].startAddress.bit(0, 7) = 0x00;
channels[c].endAddress.bit(0, 7) = 0x00;
channels[c].decodeAccumulator = 0;
channels[c].decodeStep = 0;
}
i16 stepSize[] = {
16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
};
// Generate a decode table
for (int step = 0; step < 49; step++) {
for (int nibble = 0; nibble < 16; nibble++) {
int value = (2 * (nibble & 0x07) + 1) * stepSize[step] / 8;
decodeTable[step * 16 + nibble] = ((nibble & 0x08) != 0) ? -value : value;
}
}
}
auto YM2610::PCMA::clock() -> array<i16[2]> {
f32 left = 0;
f32 right = 0;
for(auto c : range(6)) {
if(!channels[c].playing) continue;
if(((channels[c].currentAddress ^ (channels[c].endAddress + (1 << 8))) & 0xfffff) == 0) {
channels[c].playing = 0;
if(!channels[c].endedMask) channels[c].ended = 1;
continue;
}
auto sample = channels[c].decode(self.readPCMA(channels[c].currentAddress));
int totalVolume = (volume ^ 0x3f) + (channels[c].volume ^ 0x1f);
int volumeMultiplier = 15 - (totalVolume & 7);
int volumeShift = 5 + (totalVolume >> 3);
sample = ((sample * volumeMultiplier) >> volumeShift) & ~3;
if(channels[c].left) left += sample / 2047.0f;
if(channels[c].right) right += sample / 2047.0f;
}
return {sclamp<16>(left * 32768.0), sclamp<16>(right * 32768.0)};
}
auto YM2610::PCMA::Channel::keyOn() -> void {
if(ended) return;
playing = 1;
currentAddress = startAddress;
decodeAccumulator = 0;
decodeStep = 0;
currentNibble = 0;
}
auto YM2610::PCMA::Channel::keyOff() -> void {
playing = 0;
}
auto YM2610::PCMA::Channel::decode(n8 value) -> i12 {
int stepAdjust[] = { -1, -1, -1, -1, 2, 5, 7, 9, -1, -1, -1, -1, 2, 5, 7, 9 };
value = currentNibble ? value.bit(0, 3) : value.bit(4, 7);
if(currentNibble) currentAddress++;
currentNibble = !currentNibble;
decodeAccumulator += self.decodeTable[decodeStep + value];
decodeStep += stepAdjust[value & 7] * 16;
decodeStep = std::clamp((int)decodeStep, 0, 48 * 16);
return decodeAccumulator;
}
auto YM2610::PCMB::power() -> void {
currentNibble = 0;
currentAddress = 0;
decodeAccumulator = 0;
previousAccumulator = 0;
decodePosition = 0;
decodeStep = 127;
volume = 255;
}
auto YM2610::PCMB::clock() -> array<i16[2]> {
f32 leftSample = 0;
f32 rightSample = 0;
decode();
i32 sample = (previousAccumulator * (i32)((decodePosition ^ 0xffff) + 1) + decodeAccumulator * (i32)(decodePosition)) >> 16;
sample = (sample * (i32)volume) >> 9;
if(left) leftSample += (sample / 65535.0);
if(right) rightSample += (sample / 65535.0);
return {sclamp<16>(leftSample * 32768.0), sclamp<16>(rightSample * 32768.0)};
}
auto YM2610::PCMB::decode() -> void {
if(!playing) {
return;
}
u8 stepSize[8] = { 57, 57, 57, 57, 77, 102, 128, 153 };
u32 position = decodePosition + delta;
decodePosition = (u16)position;
if(position < 0x10000) return;
n8 value = self.readPCMB(currentAddress);
value = currentNibble ? value.bit(0, 3) : value.bit(4, 7);
currentNibble = !currentNibble;
if(currentNibble == 0) {
currentAddress++;
if(currentAddress == endAddress) {
if(repeat) beginPlay();
else {
decodeAccumulator = 0;
previousAccumulator = 0;
playing = 0;
if(!endedMask) ended = 1;
}
}
}
previousAccumulator = decodeAccumulator;
i32 _delta = (value.bit(0, 2) * 2 + 1) * decodeStep / 8;
if(value.bit(3)) _delta = -_delta;
decodeAccumulator = sclamp<16>(decodeAccumulator + _delta);
decodeStep = std::clamp(((int)decodeStep * stepSize[value.bit(0, 2)]) / 64, 127, 24576);
}
auto YM2610::PCMB::beginPlay() -> void {
if(ended) return;
currentAddress = startAddress;
currentNibble = 0;
decodeAccumulator = 0;
previousAccumulator = 0;
decodePosition = 0;
decodeStep = 127;
playing = 1;
}
@@ -1,44 +0,0 @@
auto YM2610::serialize(serializer& s) -> void {
s(registerAddress);
s(fm);
s(ssg);
s(pcmA);
s(pcmB);
}
auto YM2610::PCMA::serialize(serializer& s) -> void {
for(auto n : range(6)) {
s(channels[n].playing);
s(channels[n].ended);
s(channels[n].endedMask);
s(channels[n].left);
s(channels[n].right);
s(channels[n].volume);
s(channels[n].startAddress);
s(channels[n].endAddress);
s(channels[n].currentAddress);
s(channels[n].decodeAccumulator);
s(channels[n].decodeStep);
s(channels[n].currentNibble);
}
}
auto YM2610::PCMB::serialize(serializer& s) -> void {
s(playing);
s(ended);
s(endedMask);
s(repeat);
s(left);
s(right);
s(startAddress);
s(endAddress);
s(delta);
s(volume);
s(currentAddress);
s(currentNibble);
s(decodeAccumulator);
s(decodePosition);
s(previousAccumulator);
s(decodeStep);
}
-17
View File
@@ -1,17 +0,0 @@
#include <ares/ares.hpp>
#include "ym2610.hpp"
namespace ares {
#include "io.cpp"
#include "pcm.cpp"
#include "serialization.cpp"
auto YM2610::power() -> void {
fm.power();
ssg.power();
pcmA.power();
pcmB.power();
}
}
-97
View File
@@ -1,97 +0,0 @@
#pragma once
#include <component/audio/ym2612/ym2612.hpp>
#include <component/audio/ym2149/ym2149.hpp>
namespace ares {
//Yamaha YM2610 (OPNB)
struct YM2610 {
//ym2610.cpp
auto power() -> void;
//io.cpp
auto read(n2 address) -> n8;
auto write(n2 address, n8 data) -> void;
auto writeLower(n8 data) -> void;
auto writeUpper(n8 data) -> void;
auto pcmStatus() -> n8;
//serialization.cpp
auto serialize(serializer&) -> void;
virtual auto readPCMA(u32 address) -> u8 { return 0; }
virtual auto readPCMB(u32 address) -> u8 { return 0; }
protected:
n9 registerAddress;
YM2612 fm;
YM2149 ssg;
struct PCMA {
YM2610& self;
// pcm.cpp
auto power() -> void;
auto clock() -> array<i16[2]>;
//serialization.cpp
auto serialize(serializer&) -> void;
struct Channel {
PCMA& self;
auto keyOn() -> void;
auto keyOff() -> void;
auto decode(n8) -> i12;
n1 playing;
n1 ended;
n1 endedMask;
n1 left;
n1 right;
n5 volume;
n32 startAddress;
n32 endAddress;
n32 currentAddress;
i12 decodeAccumulator;
i32 decodeStep;
bool currentNibble;
} channels[6]{{*this}, {*this}, {*this}, {*this}, {*this}, {*this}};
n6 volume;
int decodeTable[16 * 49];
} pcmA{*this};
struct PCMB {
YM2610& self;
// pcm.cpp
auto power() -> void;
auto clock() -> array<i16[2]>;
auto decode() -> void;
auto beginPlay() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
n1 playing;
n1 ended;
n1 endedMask;
n1 repeat;
n1 left;
n2 right;
n32 startAddress;
n32 endAddress;
n16 delta;
n8 volume;
n32 currentAddress;
bool currentNibble;
i32 decodeAccumulator;
u32 decodePosition;
i32 previousAccumulator;
i32 decodeStep;
} pcmB{*this};
};
}
+4 -4
View File
@@ -1,6 +1,6 @@
auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
if(keyOn == state) return; //no change
keyOn = state;
auto YM2612::Channel::Operator::updateKeyState() -> void {
if(keyOn == keyLine) return; //no change
keyOn = keyLine;
if(keyOn) {
//restart phase and envelope generators
@@ -27,7 +27,6 @@ auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
}
auto YM2612::Channel::Operator::runEnvelope() -> void {
updateKeyState(keyLine);
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
u32 value = ym2612.envelope.clock >> envelope.divider;
@@ -56,6 +55,7 @@ auto YM2612::Channel::Operator::runEnvelope() -> void {
}
auto YM2612::Channel::Operator::runPhase() -> void {
updateKeyState();
phase.value += phase.delta; //advance wave position
if(!(ssg.enable && envelope.value >= 0x200)) return; //SSG loop check
+1 -1
View File
@@ -91,7 +91,7 @@ protected:
Operator(Channel& channel) : channel(channel), ym2612(channel.ym2612) {}
//channel.cpp
auto updateKeyState(bool) -> void;
auto updateKeyState() -> void;
auto runEnvelope() -> void;
auto runPhase() -> void;
@@ -0,0 +1,76 @@
#include <ares/ares.hpp>
#include "sst39sf0x0.hpp"
namespace ares {
auto SST39SF0x0::reset() -> void {
mode = Mode::Data;
unlockStep = UnlockStep::Unlock0;
}
auto SST39SF0x0::read(n32 address) -> n8 {
if(mode == Mode::ID) {
if(address == 0x00) return 0xBF; /* Manufacturer ID */
if(address == 0x01) { /* Device ID */
if(flash.size() <= 128 * 1024) return 0xB5;
if(flash.size() <= 256 * 1024) return 0xB6;
return 0xB7;
}
return 0xFF;
}
return flash.read(address);
}
auto SST39SF0x0::write(n32 address, n8 data) -> void {
if(mode == Mode::Program) {
flash.write(address, data);
mode = Mode::Data;
} else if (mode == Mode::EraseBlock) {
address &= ~0xFFF;
for(u32 i = 0; i < 4096; i++) {
flash.write(address + i, 0xFF);
}
mode = Mode::Data;
} else if(mode == Mode::ID && data == 0xF0) {
unlockStep = UnlockStep::Unlock0;
mode = Mode::Data;
} else {
if(unlockStep == UnlockStep::Unlock0) {
if((address & 0x7FFF) == 0x5555 && (data & 0xFF) == 0xAA) unlockStep = UnlockStep::Unlock1;
} else if(unlockStep == UnlockStep::Unlock1) {
unlockStep = UnlockStep::Unlock0;
if((address & 0x7FFF) == 0x2AAA && (data & 0xFF) == 0x55) unlockStep = UnlockStep::Unlock2;
} else {
unlockStep = UnlockStep::Unlock0;
if((address & 0x7FFF) == 0x5555) {
switch(data) {
case 0xA0:
mode = Mode::Program;
break;
case 0x80:
mode = Mode::Erase;
break;
case 0x10:
if(mode != Mode::Erase) return;
for(u32 i = 0; i < flash.size(); i++) {
flash.write(i, 0xFF);
}
break;
case 0x30:
if(mode != Mode::Erase) return;
mode = Mode::EraseBlock;
break;
case 0x90:
mode = Mode::ID;
break;
case 0xF0:
default:
mode = Mode::Data;
break;
}
}
}
}
}
};
@@ -0,0 +1,29 @@
#pragma once
//Generic flash
namespace ares {
struct SST39SF0x0 {
auto reset() -> void;
auto read(n32 address) -> n8;
auto write(n32 address, n8 data) -> void;
Memory::Writable<n8> flash;
enum class Mode : u32 {
Data,
ID, /* 0x90 */
Program, /* 0xA0 */
Erase, /* 0x80 */
EraseBlock /* 0x80 0x30 */
} mode;
enum class UnlockStep : u32 {
Unlock0, /* 0xF0 */
Unlock1,
Unlock2
} unlockStep;
};
}
+72
View File
@@ -0,0 +1,72 @@
#include <ares/ares.hpp>
#include "i8255.hpp"
namespace ares {
auto I8255::power() -> void {
io.portCLowerInput = 1;
io.portBInput = 1;
io.groupBMode = 0;
io.portCHigherInput = 1;
io.portAInput = 1;
io.groupAMode = 0;
io.ioMode = 0;
state.portA = 0xff;
state.portB = 0xff;
state.portC = 0xff;
}
auto I8255::read(n2 port) -> n8 {
switch(port) {
case 0: return io.portAInput ? readPortA() : state.portA;
case 1: return io.portBInput ? readPortB() : state.portB;
case 2: {
n8 result = readPortC();
if(!io.portCLowerInput) result.bit(0,3) = state.portC.bit(0,3);
if(!io.portCHigherInput) result.bit(4,7) = state.portC.bit(4,7);
return result;
}
case 3: return 0xff;
}
unreachable;
}
auto I8255::write(n2 port, n8 data) -> void {
switch(port) {
case 0: state.portA = data; return writePortA(data);
case 1: state.portB = data; return writePortB(data);
case 2: state.portC = data; return writePortC(data);
case 3: return writeControl(data);
}
unreachable;
}
auto I8255::writeControl(n8 data) -> void {
io.portCLowerInput = data.bit(0);
io.portBInput = data.bit(1);
io.groupBMode = data.bit(2);
io.portCHigherInput = data.bit(3);
io.portAInput = data.bit(4);
io.groupAMode = data.bit(5,6);
io.ioMode = data.bit(7);
if(!io.ioMode) debug(unimplemented, "[I8255] BSR Mode");
if(io.groupAMode != 0) debug(unimplemented, "[I8255] Group A Mode != 0");
if(io.groupBMode != 0) debug(unimplemented, "[I8255] Group B Mode != 0");
}
auto I8255::serialize(serializer& s) -> void {
s(io.portCLowerInput);
s(io.portBInput);
s(io.groupBMode);
s(io.portCHigherInput);
s(io.portAInput);
s(io.groupAMode);
s(io.ioMode);
s(state.portA);
s(state.portB);
s(state.portC);
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
namespace ares {
struct I8255 {
//i8255.cpp
auto power() -> void;
auto read(n2 port) -> n8;
auto write(n2 port, n8 data) -> void;
auto writeControl(n8 data) -> void;
auto serialize(serializer&) -> void;
virtual auto readPortA() -> n8 { return 0xff; };
virtual auto readPortB() -> n8 { return 0xff; };
virtual auto readPortC() -> n8 { return 0xff; };
virtual auto writePortA(n8 data) -> void {};
virtual auto writePortB(n8 data) -> void {};
virtual auto writePortC(n8 data) -> void {};
struct IO {
n1 portCLowerInput;
n1 portBInput;
n1 groupBMode;
n1 portCHigherInput;
n1 portAInput;
n2 groupAMode;
n1 ioMode;
} io;
struct State {
n8 portA;
n8 portB;
n8 portC;
} state;
};
}
@@ -62,7 +62,7 @@ struct ARM7TDMI {
auto thumbInitialize() -> void;
//instructions-arm.cpp
auto armALU(n4 mode, n4 target, n4 source, n32 data) -> void;
auto armALU(n4 mode, n4 target, n32 source, n32 data) -> void;
auto armMoveToStatus(n4 field, n1 source, n32 data) -> void;
auto armInstructionBranch(i24, n1) -> void;
@@ -1,6 +1,4 @@
auto ARM7TDMI::armALU(n4 mode, n4 d, n4 n, n32 rm) -> void {
n32 rn = r(n);
auto ARM7TDMI::armALU(n4 mode, n4 d, n32 rn, n32 rm) -> void {
switch(mode) {
case 0: r(d) = BIT(rn & rm); break; //AND
case 1: r(d) = BIT(rn ^ rm); break; //EOR
@@ -64,14 +62,16 @@ auto ARM7TDMI::armInstructionBranchExchangeRegister
auto ARM7TDMI::armInstructionDataImmediate
(n8 immediate, n4 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
n32 rn = r(n);
n32 data = immediate;
carry = cpsr().c;
if(shift) data = ROR(data, shift << 1);
armALU(mode, d, n, data);
armALU(mode, d, rn, data);
}
auto ARM7TDMI::armInstructionDataImmediateShift
(n4 m, n2 type, n5 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
n32 rn = r(n);
n32 rm = r(m);
carry = cpsr().c;
@@ -82,12 +82,13 @@ auto ARM7TDMI::armInstructionDataImmediateShift
case 3: rm = shift ? ROR(rm, shift) : RRX(rm); break;
}
armALU(mode, d, n, rm);
armALU(mode, d, rn, rm);
}
auto ARM7TDMI::armInstructionDataRegisterShift
(n4 m, n2 type, n4 s, n4 d, n4 n, n1 save, n4 mode) -> void {
n8 rs = r(s) + (s == 15 ? 4 : 0);
n32 rn = r(n) + (n == 15 ? 4 : 0);
n32 rm = r(m) + (m == 15 ? 4 : 0);
carry = cpsr().c;
@@ -98,7 +99,7 @@ auto ARM7TDMI::armInstructionDataRegisterShift
case 3: if(rs) rm = ROR(rm, rs & 31 ? u32(rs & 31) : 32); break;
}
armALU(mode, d, n, rm);
armALU(mode, d, rn, rm);
}
auto ARM7TDMI::armInstructionLoadImmediate
@@ -167,7 +168,7 @@ auto ARM7TDMI::armInstructionMoveHalfRegister
auto ARM7TDMI::armInstructionMoveImmediateOffset
(n12 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> void {
n32 rn = r(n);
n32 rd = r(d);
n32 rd = r(d) + (d == 15 ? 4 : 0);
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
if(mode == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
@@ -201,7 +202,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
for(u32 m : range(16)) {
if(!list.bit(m)) continue;
if(mode == 1) r(m) = read(Word | sequential, rn);
if(mode == 0) write(Word | sequential, rn, r(m));
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
rn += 4;
sequential = Sequential;
}
@@ -126,17 +126,23 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
(n8 list, n3 n, n1 mode) -> void {
n32 rn = r(n);
u32 sequential = Nonsequential;
for(u32 m : range(8)) {
if(!list.bit(m)) continue;
switch(mode) {
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
case 0: write(Word | sequential, rn, r(m)); break; //STMIA
case 1: r(m) = read(Word | sequential, rn); break; //LDMIA
}
rn += 4;
sequential = Sequential;
}
if(mode == 0 || !list.bit(n)) r(n) = rn;
if(mode == 1) idle();
if(mode == 1) {
idle();
} else {
pipeline.nonsequential = true;
}
}
auto ARM7TDMI::thumbInstructionMoveRegisterOffset
@@ -0,0 +1,85 @@
auto I8080::ADD(n8 x, n8 y, bool c) -> n8 {
n9 z = x + y + c;
CF = z.bit(8);
PF = parity(z);
HF = n8(x ^ y ^ z).bit(4);
ZF = n8(z) == 0;
SF = z.bit(7);
return z;
}
auto I8080::AND(n8 x, n8 y) -> n8 {
n8 z = x & y;
CF = 0;
HF = n8(x | y).bit(3);
PF = parity(z);
ZF = z == 0;
SF = z.bit(7);
return z;
}
auto I8080::CP(n8 x, n8 y) -> void {
SUB(x, y, 0);
}
auto I8080::DEC(n8 x) -> n8 {
n8 z = x - 1;
PF = parity(z);
HF = x.bit(0,3) != 0x00;
ZF = z == 0;
SF = z.bit(7);
return z;
}
auto I8080::INC(n8 x) -> n8 {
n8 z = x + 1;
PF = parity(z);
HF = x.bit(0,3) == 0x0f;
ZF = z == 0;
SF = z.bit(7);
return z;
}
auto I8080::OR(n8 x, n8 y) -> n8 {
n8 z = x | y;
CF = 0;
PF = parity(z);
HF = 0;
ZF = z == 0;
SF = z.bit(7);
return z;
}
auto I8080::SUB(n8 x, n8 y, bool c) -> n8 {
n9 z = x + ~y + !c; // internally implemented as negated ADD
CF = z.bit(8);
PF = parity(z);
HF = n8(x ^ ~y ^ z).bit(4);
ZF = n8(z) == 0;
SF = z.bit(7);
return z;
}
auto I8080::XOR(n8 x, n8 y) -> n8 {
n8 z = x ^ y;
CF = 0;
HF = 0;
PF = parity(z);
ZF = z == 0;
SF = z.bit(7);
return z;
}
@@ -0,0 +1,410 @@
no_optimize
auto I8080::disassembleInstruction(maybe<n16> _pc) -> string {
auto pc = _pc ? *_pc : PC;
string s, output;
n8 prefix = 0x00;
auto code = bus->read(pc++);
if(code == 0xdd || code == 0xfd) {
prefix = code;
code = bus->read(pc++);
if(code == 0xdd || code == 0xfd) {
if(prefix == 0xdd) {
s.append("ix:");
goto finish;
}
if(prefix == 0xfd) {
s.append("iy:");
goto finish;
}
}
}
output = disassemble(pc, prefix, code);
finish:
return pad(output, -18L);
}
no_optimize
auto I8080::disassembleContext() -> string {
string s;
s.append("AF:", hex(af.word, 4L), " ");
s.append("BC:", hex(bc.word, 4L), " ");
s.append("DE:", hex(de.word, 4L), " ");
s.append("HL:", hex(hl.word, 4L), " ");
s.append("IX:", hex(ix.word, 4L), " ");
s.append("IY:", hex(iy.word, 4L), " ");
s.append("SP:", hex(SP, 4L), " ");
return s;
}
#define op(id, name, ...) case id: return {name, " ", vector<string>{__VA_ARGS__}.merge(",")};
#define N string{"$", hex(byte(), 2L)}
#define IN string{"(", N, ")"}
#define NN string{"$", hex(word(), 4L)}
#define INN string{"(", NN, ")"}
#define REL string{"$", hex(branch(), 4L)}
#define A "a"
#define F "f"
#define B "b"
#define C "c"
#define D "d"
#define E "e"
#define H prefix == 0xdd ? "ixh" : prefix == 0xfd ? "iyh" : "h"
#define L prefix == 0xdd ? "ixl" : prefix == 0xfd ? "iyl" : "l"
#define _H "h"
#define _L "l"
#define _HL "hl"
#define AF "af"
#define BC "bc"
#define DE "de"
#define HL prefix == 0xdd ? "ix" : prefix == 0xfd ? "iy" : "hl"
#define AF_ "af'"
#define BC_ "bc'"
#define DE_ "de'"
#define HL_ "hl'"
#define SP "sp"
#define PC "pc"
#define I "i"
#define R "r"
#define IC "(c)"
#define IBC "(bc)"
#define IDE "(de)"
#define IHL "(hl)"
#define ISP "(sp)"
no_optimize
auto I8080::disassemble(n16 pc, n8 prefix, n8 code) -> string {
auto byte = [&] {
return bus->read(pc++);
};
auto word = [&] {
n16 data = byte() << 0;
return data | byte() << 8;
};
auto branch = [&] {
auto d = byte();
return pc + (i8)d;
};
auto displace = [&] {
if(!prefix) return string{};
auto d = (i8)byte();
return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)};
};
switch(code) {
op(0x00, "nop ")
op(0x01, "ld ", BC, NN)
op(0x02, "ld ", IBC, A)
op(0x03, "inc ", BC)
op(0x04, "inc ", B)
op(0x05, "dec ", B)
op(0x06, "ld ", B, N)
op(0x07, "rlca")
op(0x08, "ex ", AF, AF_)
op(0x09, "add ", HL, BC)
op(0x0a, "ld ", A, IBC)
op(0x0b, "dec ", BC)
op(0x0c, "inc ", C)
op(0x0d, "dec ", C)
op(0x0e, "ld ", C, N)
op(0x0f, "rrca")
op(0x10, "djnz", REL)
op(0x11, "ld ", DE, NN)
op(0x12, "ld ", IDE, A)
op(0x13, "inc ", DE)
op(0x14, "inc ", D)
op(0x15, "dec ", D)
op(0x16, "ld ", D, N)
op(0x17, "rla ")
op(0x18, "jr ", REL)
op(0x19, "add ", HL, DE)
op(0x1a, "ld ", A, IDE)
op(0x1b, "dec ", DE)
op(0x1c, "inc ", E)
op(0x1d, "dec ", E)
op(0x1e, "ld ", E, N)
op(0x1f, "rra ")
op(0x20, "jr ", "nz", REL)
op(0x21, "ld ", HL, NN)
op(0x22, "ld ", INN, HL)
op(0x23, "inc ", HL)
op(0x24, "inc ", H)
op(0x25, "dec ", H)
op(0x26, "ld ", H, N)
op(0x27, "daa ")
op(0x28, "jr ", "z", REL)
op(0x29, "add ", HL, HL)
op(0x2a, "ld ", HL, INN)
op(0x2b, "dec ", HL)
op(0x2c, "inc ", L)
op(0x2d, "dec ", L)
op(0x2e, "ld ", L, N)
op(0x2f, "cpl ")
op(0x30, "jr ", "nc", REL)
op(0x31, "ld ", SP, NN)
op(0x32, "ld ", INN, A)
op(0x33, "inc ", SP)
op(0x34, "inc ", IHL)
op(0x35, "dec ", IHL)
op(0x36, "ld ", IHL, N)
op(0x37, "scf ")
op(0x38, "jr ", "c", REL)
op(0x39, "add ", HL, SP)
op(0x3a, "ld ", A, INN)
op(0x3b, "dec ", SP)
op(0x3c, "inc ", A)
op(0x3d, "dec ", A)
op(0x3e, "ld ", A, N)
op(0x3f, "ccf ")
op(0x40, "ld ", B, B)
op(0x41, "ld ", B, C)
op(0x42, "ld ", B, D)
op(0x43, "ld ", B, E)
op(0x44, "ld ", B, H)
op(0x45, "ld ", B, L)
op(0x46, "ld ", B, IHL)
op(0x47, "ld ", B, A)
op(0x48, "ld ", C, B)
op(0x49, "ld ", C, C)
op(0x4a, "ld ", C, D)
op(0x4b, "ld ", C, E)
op(0x4c, "ld ", C, H)
op(0x4d, "ld ", C, L)
op(0x4e, "ld ", C, IHL)
op(0x4f, "ld ", C, A)
op(0x50, "ld ", D, B)
op(0x51, "ld ", D, C)
op(0x52, "ld ", D, D)
op(0x53, "ld ", D, E)
op(0x54, "ld ", D, H)
op(0x55, "ld ", D, L)
op(0x56, "ld ", D, IHL)
op(0x57, "ld ", D, A)
op(0x58, "ld ", E, B)
op(0x59, "ld ", E, C)
op(0x5a, "ld ", E, D)
op(0x5b, "ld ", E, E)
op(0x5c, "ld ", E, H)
op(0x5d, "ld ", E, L)
op(0x5e, "ld ", E, IHL)
op(0x5f, "ld ", E, A)
op(0x60, "ld ", H, B)
op(0x61, "ld ", H, C)
op(0x62, "ld ", H, D)
op(0x63, "ld ", H, E)
op(0x64, "ld ", H, H)
op(0x65, "ld ", H, L)
op(0x66, "ld ", _H, IHL)
op(0x67, "ld ", H, A)
op(0x68, "ld ", L, B)
op(0x69, "ld ", L, C)
op(0x6a, "ld ", L, D)
op(0x6b, "ld ", L, E)
op(0x6c, "ld ", L, H)
op(0x6d, "ld ", L, L)
op(0x6e, "ld ", _L, IHL)
op(0x6f, "ld ", L, A)
op(0x70, "ld ", IHL, B)
op(0x71, "ld ", IHL, C)
op(0x72, "ld ", IHL, D)
op(0x73, "ld ", IHL, E)
op(0x74, "ld ", IHL, _H)
op(0x75, "ld ", IHL, _L)
op(0x76, "halt")
op(0x77, "ld ", IHL, A)
op(0x78, "ld ", A, B)
op(0x79, "ld ", A, C)
op(0x7a, "ld ", A, D)
op(0x7b, "ld ", A, E)
op(0x7c, "ld ", A, H)
op(0x7d, "ld ", A, L)
op(0x7e, "ld ", A, IHL)
op(0x7f, "ld ", A, A)
op(0x80, "add ", A, B)
op(0x81, "add ", A, C)
op(0x82, "add ", A, D)
op(0x83, "add ", A, E)
op(0x84, "add ", A, H)
op(0x85, "add ", A, L)
op(0x86, "add ", A, IHL)
op(0x87, "add ", A, A)
op(0x88, "adc ", A, B)
op(0x89, "adc ", A, C)
op(0x8a, "adc ", A, D)
op(0x8b, "adc ", A, E)
op(0x8c, "adc ", A, H)
op(0x8d, "adc ", A, L)
op(0x8e, "adc ", A, IHL)
op(0x8f, "adc ", A, A)
op(0x90, "sub ", A, B)
op(0x91, "sub ", A, C)
op(0x92, "sub ", A, D)
op(0x93, "sub ", A, E)
op(0x94, "sub ", A, H)
op(0x95, "sub ", A, L)
op(0x96, "sub ", A, IHL)
op(0x97, "sub ", A, A)
op(0x98, "sbc ", A, B)
op(0x99, "sbc ", A, C)
op(0x9a, "sbc ", A, D)
op(0x9b, "sbc ", A, E)
op(0x9c, "sbc ", A, H)
op(0x9d, "sbc ", A, L)
op(0x9e, "sbc ", A, IHL)
op(0x9f, "sbc ", A, A)
op(0xa0, "and ", A, B)
op(0xa1, "and ", A, C)
op(0xa2, "and ", A, D)
op(0xa3, "and ", A, E)
op(0xa4, "and ", A, H)
op(0xa5, "and ", A, L)
op(0xa6, "and ", A, IHL)
op(0xa7, "and ", A, A)
op(0xa8, "xor ", A, B)
op(0xa9, "xor ", A, C)
op(0xaa, "xor ", A, D)
op(0xab, "xor ", A, E)
op(0xac, "xor ", A, H)
op(0xad, "xor ", A, L)
op(0xae, "xor ", A, IHL)
op(0xaf, "xor ", A, A)
op(0xb0, "or ", A, B)
op(0xb1, "or ", A, C)
op(0xb2, "or ", A, D)
op(0xb3, "or ", A, E)
op(0xb4, "or ", A, H)
op(0xb5, "or ", A, L)
op(0xb6, "or ", A, IHL)
op(0xb7, "or ", A, A)
op(0xb8, "cp ", A, B)
op(0xb9, "cp ", A, C)
op(0xba, "cp ", A, D)
op(0xbb, "cp ", A, E)
op(0xbc, "cp ", A, H)
op(0xbd, "cp ", A, L)
op(0xbe, "cp ", A, IHL)
op(0xbf, "cp ", A, A)
op(0xc0, "ret ", "nz")
op(0xc1, "pop ", BC)
op(0xc2, "jp ", "nz", NN)
op(0xc3, "jp ", NN)
op(0xc4, "call", "nz", NN)
op(0xc5, "push", BC)
op(0xc6, "add ", A, N)
op(0xc7, "rst ", "$0000")
op(0xc8, "ret ", "z")
op(0xc9, "ret ")
op(0xca, "jp ", "z", NN)
op(0xcb, "cb: ")
op(0xcc, "call", "z", NN)
op(0xcd, "call", NN)
op(0xce, "adc ", A, N)
op(0xcf, "rst ", "$0008")
op(0xd0, "ret ", "nc")
op(0xd1, "pop ", DE)
op(0xd2, "jp ", "nc", NN)
op(0xd3, "out ", IN, A)
op(0xd4, "call", "nc", NN)
op(0xd5, "push", DE)
op(0xd6, "sub ", A, N)
op(0xd7, "rst ", "$0010")
op(0xd8, "ret ", "c")
op(0xd9, "exx ")
op(0xda, "jp ", "c", NN)
op(0xdb, "in ", A, IN)
op(0xdc, "call", "c", NN)
op(0xdd, "ix: ")
op(0xde, "sbc ", A, N)
op(0xdf, "rst ", "$0018")
op(0xe0, "ret ", "po")
op(0xe1, "pop ", HL)
op(0xe2, "jp ", "po", NN)
op(0xe3, "ex ", ISP, HL)
op(0xe4, "call", "po", NN)
op(0xe5, "push", HL)
op(0xe6, "and ", A, N)
op(0xe7, "rst ", "$0020")
op(0xe8, "ret ", "pe")
op(0xe9, "jp ", HL) //officially jp (hl); but as read is not indirect, use jp hl
op(0xea, "jp ", "pe", NN)
op(0xeb, "ex ", DE, _HL)
op(0xec, "call", "pe", NN)
op(0xed, "ed: ")
op(0xee, "xor ", A, N)
op(0xef, "rst ", "$0028")
op(0xf0, "ret ", "p")
op(0xf1, "pop ", AF)
op(0xf2, "jp ", "p", NN)
op(0xf3, "di ")
op(0xf4, "call", "p", NN)
op(0xf5, "push", AF)
op(0xf6, "or ", A, N)
op(0xf7, "rst ", "$0030")
op(0xf8, "ret ", "m")
op(0xf9, "ld ", SP, HL)
op(0xfa, "jp ", "m", NN)
op(0xfb, "ei ")
op(0xfc, "call", "m", NN)
op(0xfd, "iy: ")
op(0xfe, "cp ", A, N)
op(0xff, "rst ", "$0038")
}
unreachable;
}
#undef op
#undef N
#undef IN
#undef NN
#undef INN
#undef REL
#undef A
#undef F
#undef B
#undef C
#undef D
#undef E
#undef H
#undef L
#undef _H
#undef _L
#undef _HL
#undef AF
#undef BC
#undef DE
#undef HL
#undef AF_
#undef BC_
#undef DE_
#undef HL_
#undef SP
#undef PC
#undef I
#undef R
#undef IC
#undef IBC
#undef IDE
#undef IHL
#undef ISP
+81
View File
@@ -0,0 +1,81 @@
#include <ares/ares.hpp>
#include "i8080.hpp"
namespace ares {
#include "disassembler.cpp"
#include "registers.hpp"
#include "memory.cpp"
#include "instruction.cpp"
#include "algorithms.cpp"
#include "instructions.cpp"
#include "serialization.cpp"
auto I8080::power() -> void {
prefix = Prefix::hl;
af.word = 0xffd7; af_.word = 0x0000;
bc.word = 0x0000; bc_.word = 0x0000;
de.word = 0x0000; de_.word = 0x0000;
hl.word = 0x0000; hl_.word = 0x0000;
ix.word = 0x0000;
iy.word = 0x0000;
ir.word = 0x0000;
wz.word = 0x0000;
SP = 0xffff;
PC = 0x0000;
EI = 0;
P = 0;
Q = 0;
HALT = 0;
INTE = 0;
}
auto I8080::reset() -> void {
prefix = Prefix::hl;
ir.word = 0x0000;
WZ = PC = 0x0000;
EI = 0;
HALT = 0;
INTE = 0;
}
auto I8080::irq(n8 extbus) -> bool {
//do not execute maskable interrupts if disabled or immediately after EI instruction
if(!INTE || EI) return false;
WZ = extbus;
wait(6);
instruction(WZ);
INTE = 0;
HALT = 0;
if(P) PF = 0;
P = 0;
Q = 0;
return true;
}
auto I8080::nmi() -> bool {
push(PC);
WZ = 0x66;
wait(5);
PC = WZ;
INTE = 0;
HALT = 0;
if(P) PF = 0;
P = 0;
Q = 0;
return true;
}
auto I8080::parity(n8 value) const -> bool {
value ^= value >> 4;
value ^= value >> 2;
value ^= value >> 1;
return !(value & 1);
}
}
+160
View File
@@ -0,0 +1,160 @@
#pragma once
//Intel 8080
namespace ares {
struct I8080 {
struct Bus {
virtual auto read(n16 address) -> n8 = 0;
virtual auto write(n16 address, n8 data) -> void = 0;
virtual auto in(n16 address) -> n8 = 0;
virtual auto out(n16 address, n8 data) -> void = 0;
};
virtual auto step(u32 clocks) -> void = 0;
virtual auto synchronizing() const -> bool = 0;
//i8080.cpp
auto power() -> void;
auto reset() -> void;
auto irq(n8 extbus = 0xff) -> bool;
auto nmi() -> bool;
auto parity(n8) const -> bool;
//memory.cpp
auto wait(u32 clocks = 1) -> void;
auto opcode() -> n8;
auto operand() -> n8;
auto operands() -> n16;
auto push(n16) -> void;
auto pop() -> n16;
auto read(n16 address) -> n8;
auto write(n16 address, n8 data) -> void;
auto in(n16 address) -> n8;
auto out(n16 address, n8 data) -> void;
//instruction.cpp
auto instruction() -> void;
auto instruction(n8 code) -> void;
//algorithms.cpp
auto ADD(n8, n8, bool = false) -> n8;
auto AND(n8, n8) -> n8;
auto CP (n8, n8) -> void;
auto DEC(n8) -> n8;
auto INC(n8) -> n8;
auto OR (n8, n8) -> n8;
auto SUB(n8, n8, bool = false) -> n8;
auto XOR(n8, n8) -> n8;
//instructions.cpp
auto instructionADC_a_irr(n16&) -> void;
auto instructionADC_a_n() -> void;
auto instructionADC_a_r(n8&) -> void;
auto instructionADD_a_irr(n16&) -> void;
auto instructionADD_a_n() -> void;
auto instructionADD_a_r(n8&) -> void;
auto instructionADD_hl_rr(n16&) -> void;
auto instructionAND_a_irr(n16&) -> void;
auto instructionAND_a_n() -> void;
auto instructionAND_a_r(n8&) -> void;
auto instructionCALL_c_nn(bool c) -> void;
auto instructionCCF() -> void;
auto instructionCP_a_irr(n16& x) -> void;
auto instructionCP_a_n() -> void;
auto instructionCP_a_r(n8& x) -> void;
auto instructionCPL() -> void;
auto instructionDAA() -> void;
auto instructionDEC_irr(n16&) -> void;
auto instructionDEC_r(n8&) -> void;
auto instructionDEC_rr(n16&) -> void;
auto instructionDI() -> void;
auto instructionEI() -> void;
auto instructionEX_irr_rr(n16&, n16&) -> void;
auto instructionEX_rr_rr(n16&, n16&) -> void;
auto instructionHALT() -> void;
auto instructionIN_a_in() -> void;
auto instructionINC_irr(n16&) -> void;
auto instructionINC_r(n8&) -> void;
auto instructionINC_rr(n16&) -> void;
auto instructionJP_c_nn(bool) -> void;
auto instructionJP_rr(n16&) -> void;
auto instructionLD_a_inn() -> void;
auto instructionLD_a_irr(n16& x) -> void;
auto instructionLD_inn_a() -> void;
auto instructionLD_inn_rr(n16&) -> void;
auto instructionLD_irr_a(n16&) -> void;
auto instructionLD_irr_n(n16&) -> void;
auto instructionLD_irr_r(n16&, n8&) -> void;
auto instructionLD_r_n(n8&) -> void;
auto instructionLD_r_irr(n8&, n16&) -> void;
auto instructionLD_r_r(n8&, n8&) -> void;
auto instructionLD_rr_inn(n16&) -> void;
auto instructionLD_rr_nn(n16&) -> void;
auto instructionLD_sp_rr(n16&) -> void;
auto instructionNOP() -> void;
auto instructionOR_a_irr(n16&) -> void;
auto instructionOR_a_n() -> void;
auto instructionOR_a_r(n8&) -> void;
auto instructionOUT_in_a() -> void;
auto instructionPOP_rr(n16&) -> void;
auto instructionPUSH_rr(n16&) -> void;
auto instructionRET() -> void;
auto instructionRET_c(bool c) -> void;
auto instructionRLA() -> void;
auto instructionRLCA() -> void;
auto instructionRRA() -> void;
auto instructionRRCA() -> void;
auto instructionRST_o(n3) -> void;
auto instructionSBC_a_irr(n16&) -> void;
auto instructionSBC_a_n() -> void;
auto instructionSBC_a_r(n8&) -> void;
auto instructionSCF() -> void;
auto instructionSUB_a_irr(n16&) -> void;
auto instructionSUB_a_n() -> void;
auto instructionSUB_a_r(n8&) -> void;
auto instructionXOR_a_irr(n16&) -> void;
auto instructionXOR_a_n() -> void;
auto instructionXOR_a_r(n8&) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
//disassembler.cpp
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
noinline auto disassembleContext() -> string;
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
enum class Prefix : u32 { hl, ix, iy } prefix = Prefix::hl;
union Pair {
Pair() : word(0) {}
n16 word;
struct Byte { n8 order_msb2(hi, lo); } byte;
};
Pair af, af_;
Pair bc, bc_;
Pair de, de_;
Pair hl, hl_;
Pair ix;
Pair iy;
Pair ir;
Pair wz;
n16 SP;
n16 PC;
b1 EI; //"ei" executed last
b1 P; //"ld a,i" or "ld a,r" executed last
b1 Q; //opcode that updated flag registers executed last
b1 HALT; //"halt" instruction executed
b1 INTE; //interrupt enable
Bus* bus = nullptr;
};
}
@@ -0,0 +1,273 @@
auto I8080::instruction() -> void {
EI = 0;
P = 0;
if(HALT) {
return wait(1);
}
n8 code;
while(true) {
code = opcode();
if(code == 0xdd) { prefix = Prefix::ix; continue; }
if(code == 0xfd) { prefix = Prefix::iy; continue; }
break;
}
instruction(code);
prefix = Prefix::hl;
}
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
auto I8080::instruction(n8 code) -> void {
switch(code) {
op(0x00, NOP)
op(0x01, LD_rr_nn, BC)
op(0x02, LD_irr_a, BC)
op(0x03, INC_rr, BC)
op(0x04, INC_r, B)
op(0x05, DEC_r, B)
op(0x06, LD_r_n, B)
op(0x07, RLCA)
op(0x09, ADD_hl_rr, BC)
op(0x0a, LD_a_irr, BC)
op(0x0b, DEC_rr, BC)
op(0x0c, INC_r, C)
op(0x0d, DEC_r, C)
op(0x0e, LD_r_n, C)
op(0x0f, RRCA)
op(0x11, LD_rr_nn, DE)
op(0x12, LD_irr_a, DE)
op(0x13, INC_rr, DE)
op(0x14, INC_r, D)
op(0x15, DEC_r, D)
op(0x16, LD_r_n, D)
op(0x17, RLA)
op(0x19, ADD_hl_rr, DE)
op(0x1a, LD_a_irr, DE)
op(0x1b, DEC_rr, DE)
op(0x1c, INC_r, E)
op(0x1d, DEC_r, E)
op(0x1e, LD_r_n, E)
op(0x1f, RRA)
op(0x21, LD_rr_nn, HL)
op(0x22, LD_inn_rr, HL)
op(0x23, INC_rr, HL)
op(0x24, INC_r, H)
op(0x25, DEC_r, H)
op(0x26, LD_r_n, H)
op(0x27, DAA)
op(0x29, ADD_hl_rr, HL)
op(0x2a, LD_rr_inn, HL)
op(0x2b, DEC_rr, HL)
op(0x2c, INC_r, L)
op(0x2d, DEC_r, L)
op(0x2e, LD_r_n, L)
op(0x2f, CPL)
op(0x31, LD_rr_nn, SP)
op(0x32, LD_inn_a)
op(0x33, INC_rr, SP)
op(0x34, INC_irr, HL)
op(0x35, DEC_irr, HL)
op(0x36, LD_irr_n, HL)
op(0x37, SCF)
op(0x39, ADD_hl_rr, SP)
op(0x3a, LD_a_inn)
op(0x3b, DEC_rr, SP)
op(0x3c, INC_r, A)
op(0x3d, DEC_r, A)
op(0x3e, LD_r_n, A)
op(0x3f, CCF)
op(0x40, LD_r_r, B, B)
op(0x41, LD_r_r, B, C)
op(0x42, LD_r_r, B, D)
op(0x43, LD_r_r, B, E)
op(0x44, LD_r_r, B, H)
op(0x45, LD_r_r, B, L)
op(0x46, LD_r_irr, B, HL)
op(0x47, LD_r_r, B, A)
op(0x48, LD_r_r, C, B)
op(0x49, LD_r_r, C, C)
op(0x4a, LD_r_r, C, D)
op(0x4b, LD_r_r, C, E)
op(0x4c, LD_r_r, C, H)
op(0x4d, LD_r_r, C, L)
op(0x4e, LD_r_irr, C, HL)
op(0x4f, LD_r_r, C, A)
op(0x50, LD_r_r, D, B)
op(0x51, LD_r_r, D, C)
op(0x52, LD_r_r, D, D)
op(0x53, LD_r_r, D, E)
op(0x54, LD_r_r, D, H)
op(0x55, LD_r_r, D, L)
op(0x56, LD_r_irr, D, HL)
op(0x57, LD_r_r, D, A)
op(0x58, LD_r_r, E, B)
op(0x59, LD_r_r, E, C)
op(0x5a, LD_r_r, E, D)
op(0x5b, LD_r_r, E, E)
op(0x5c, LD_r_r, E, H)
op(0x5d, LD_r_r, E, L)
op(0x5e, LD_r_irr, E, HL)
op(0x5f, LD_r_r, E, A)
op(0x60, LD_r_r, H, B)
op(0x61, LD_r_r, H, C)
op(0x62, LD_r_r, H, D)
op(0x63, LD_r_r, H, E)
op(0x64, LD_r_r, H, H)
op(0x65, LD_r_r, H, L)
op(0x66, LD_r_irr, H, HL)
op(0x67, LD_r_r, H, A)
op(0x68, LD_r_r, L, B)
op(0x69, LD_r_r, L, C)
op(0x6a, LD_r_r, L, D)
op(0x6b, LD_r_r, L, E)
op(0x6c, LD_r_r, L, H)
op(0x6d, LD_r_r, L, L)
op(0x6e, LD_r_irr, L, HL)
op(0x6f, LD_r_r, L, A)
op(0x70, LD_irr_r, HL, B)
op(0x71, LD_irr_r, HL, C)
op(0x72, LD_irr_r, HL, D)
op(0x73, LD_irr_r, HL, E)
op(0x74, LD_irr_r, HL, H)
op(0x75, LD_irr_r, HL, L)
op(0x76, HALT)
op(0x77, LD_irr_r, HL, A)
op(0x78, LD_r_r, A, B)
op(0x79, LD_r_r, A, C)
op(0x7a, LD_r_r, A, D)
op(0x7b, LD_r_r, A, E)
op(0x7c, LD_r_r, A, H)
op(0x7d, LD_r_r, A, L)
op(0x7e, LD_r_irr, A, HL)
op(0x7f, LD_r_r, A, A)
op(0x80, ADD_a_r, B)
op(0x81, ADD_a_r, C)
op(0x82, ADD_a_r, D)
op(0x83, ADD_a_r, E)
op(0x84, ADD_a_r, H)
op(0x85, ADD_a_r, L)
op(0x86, ADD_a_irr, HL)
op(0x87, ADD_a_r, A)
op(0x88, ADC_a_r, B)
op(0x89, ADC_a_r, C)
op(0x8a, ADC_a_r, D)
op(0x8b, ADC_a_r, E)
op(0x8c, ADC_a_r, H)
op(0x8d, ADC_a_r, L)
op(0x8e, ADC_a_irr, HL)
op(0x8f, ADC_a_r, A)
op(0x90, SUB_a_r, B)
op(0x91, SUB_a_r, C)
op(0x92, SUB_a_r, D)
op(0x93, SUB_a_r, E)
op(0x94, SUB_a_r, H)
op(0x95, SUB_a_r, L)
op(0x96, SUB_a_irr, HL)
op(0x97, SUB_a_r, A)
op(0x98, SBC_a_r, B)
op(0x99, SBC_a_r, C)
op(0x9a, SBC_a_r, D)
op(0x9b, SBC_a_r, E)
op(0x9c, SBC_a_r, H)
op(0x9d, SBC_a_r, L)
op(0x9e, SBC_a_irr, HL)
op(0x9f, SBC_a_r, A)
op(0xa0, AND_a_r, B)
op(0xa1, AND_a_r, C)
op(0xa2, AND_a_r, D)
op(0xa3, AND_a_r, E)
op(0xa4, AND_a_r, H)
op(0xa5, AND_a_r, L)
op(0xa6, AND_a_irr, HL)
op(0xa7, AND_a_r, A)
op(0xa8, XOR_a_r, B)
op(0xa9, XOR_a_r, C)
op(0xaa, XOR_a_r, D)
op(0xab, XOR_a_r, E)
op(0xac, XOR_a_r, H)
op(0xad, XOR_a_r, L)
op(0xae, XOR_a_irr, HL)
op(0xaf, XOR_a_r, A)
op(0xb0, OR_a_r, B)
op(0xb1, OR_a_r, C)
op(0xb2, OR_a_r, D)
op(0xb3, OR_a_r, E)
op(0xb4, OR_a_r, H)
op(0xb5, OR_a_r, L)
op(0xb6, OR_a_irr, HL)
op(0xb7, OR_a_r, A)
op(0xb8, CP_a_r, B)
op(0xb9, CP_a_r, C)
op(0xba, CP_a_r, D)
op(0xbb, CP_a_r, E)
op(0xbc, CP_a_r, H)
op(0xbd, CP_a_r, L)
op(0xbe, CP_a_irr, HL)
op(0xbf, CP_a_r, A)
op(0xc0, RET_c, ZF == 0)
op(0xc1, POP_rr, BC)
op(0xc2, JP_c_nn, ZF == 0)
op(0xc3, JP_c_nn, 1)
op(0xc4, CALL_c_nn, ZF == 0)
op(0xc5, PUSH_rr, BC)
op(0xc6, ADD_a_n)
op(0xc7, RST_o, 0)
op(0xc8, RET_c, ZF == 1)
op(0xc9, RET)
op(0xca, JP_c_nn, ZF == 1)
op(0xcc, CALL_c_nn, ZF == 1)
op(0xcd, CALL_c_nn, true)
op(0xce, ADC_a_n)
op(0xcf, RST_o, 1)
op(0xd0, RET_c, CF == 0)
op(0xd1, POP_rr, DE)
op(0xd2, JP_c_nn, CF == 0)
op(0xd3, OUT_in_a)
op(0xd4, CALL_c_nn, CF == 0)
op(0xd5, PUSH_rr, DE)
op(0xd6, SUB_a_n)
op(0xd7, RST_o, 2)
op(0xd8, RET_c, CF == 1)
op(0xda, JP_c_nn, CF == 1)
op(0xdb, IN_a_in)
op(0xdc, CALL_c_nn, CF == 1)
op(0xde, SBC_a_n)
op(0xdf, RST_o, 3)
op(0xe0, RET_c, PF == 0)
op(0xe1, POP_rr, HL)
op(0xe2, JP_c_nn, PF == 0)
op(0xe3, EX_irr_rr, SP, HL)
op(0xe4, CALL_c_nn, PF == 0)
op(0xe5, PUSH_rr, HL)
op(0xe6, AND_a_n)
op(0xe7, RST_o, 4)
op(0xe8, RET_c, PF == 1)
op(0xe9, JP_rr, HL)
op(0xea, JP_c_nn, PF == 1)
op(0xeb, EX_rr_rr, DE, HL)
op(0xec, CALL_c_nn, PF == 1)
op(0xee, XOR_a_n)
op(0xef, RST_o, 5)
op(0xf0, RET_c, SF == 0)
op(0xf1, POP_rr, AF)
op(0xf2, JP_c_nn, SF == 0)
op(0xf3, DI)
op(0xf4, CALL_c_nn, SF == 0)
op(0xf5, PUSH_rr, AF)
op(0xf6, OR_a_n)
op(0xf7, RST_o, 6)
op(0xf8, RET_c, SF == 1)
op(0xf9, LD_sp_rr, HL)
op(0xfa, JP_c_nn, SF == 1)
op(0xfb, EI)
op(0xfc, CALL_c_nn, SF == 1)
op(0xfe, CP_a_n)
op(0xff, RST_o, 7)
}
}
#undef op
@@ -0,0 +1,364 @@
//legend:
// a = register A
// c = condition
// e = relative operand
// in = (operand)
// inn = (operand-word)
// irr = (register-word)
// o = opcode bits
// n = operand
// nn = operand-word
// r = register
auto I8080::instructionADC_a_irr(n16& x) -> void { Q = 1;
A = ADD(A, read(x), CF);
}
auto I8080::instructionADC_a_n() -> void { Q = 1;
A = ADD(A, operand(), CF);
}
auto I8080::instructionADC_a_r(n8& x) -> void { Q = 1;
A = ADD(A, x, CF);
}
auto I8080::instructionADD_a_irr(n16& x) -> void { Q = 1;
A = ADD(A, read(x));
}
auto I8080::instructionADD_a_n() -> void { Q = 1;
A = ADD(A, operand());
}
auto I8080::instructionADD_a_r(n8& x) -> void { Q = 1;
A = ADD(A, x);
}
auto I8080::instructionADD_hl_rr(n16& x) -> void { Q = 1;
WZ = HL + 1;
bool pf = PF, zf = ZF, sf = SF, hf = HF;
wait(3);
auto lo = ADD(HL >> 0, x >> 0);
wait(4);
auto hi = ADD(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
PF = pf, ZF = zf, SF = sf, HF = hf; //restore unaffected flags
}
auto I8080::instructionAND_a_irr(n16& x) -> void { Q = 1;
A = AND(A, read(x));
}
auto I8080::instructionAND_a_n() -> void { Q = 1;
A = AND(A, operand());
}
auto I8080::instructionAND_a_r(n8& x) -> void { Q = 1;
A = AND(A, x);
}
auto I8080::instructionCALL_c_nn(bool c) -> void { Q = 0;
WZ = operands();
if(!c) return;
wait(1);
push(PC);
PC = WZ;
}
auto I8080::instructionCCF() -> void {
CF = !CF;
Q = 1;
}
auto I8080::instructionCP_a_irr(n16& x) -> void { Q = 1;
CP(A, read(x));
}
auto I8080::instructionCP_a_n() -> void { Q = 1;
CP(A, operand());
}
auto I8080::instructionCP_a_r(n8& x) -> void { Q = 1;
CP(A, x);
}
auto I8080::instructionCPL() -> void { Q = 1;
A = ~A;
}
auto I8080::instructionDAA() -> void { Q = 1;
auto a = A;
if(CF || (A.bit(0,7) > 0x99)) { A += 0x60; CF = 1; }
if(HF || (A.bit(0,3) > 0x09)) { A += 0x06; }
PF = parity(A);
HF = n8(A ^ a).bit(4);
ZF = A == 0;
SF = A.bit(7);
}
auto I8080::instructionDEC_irr(n16& x) -> void { Q = 1;
auto addr = x;
auto data = read(addr);
wait(1);
write(addr, DEC(data));
}
auto I8080::instructionDEC_r(n8& x) -> void { Q = 1;
x = DEC(x);
}
auto I8080::instructionDEC_rr(n16& x) -> void { Q = 0;
wait(2);
x--;
}
auto I8080::instructionDI() -> void { Q = 0;
INTE = 0;
}
auto I8080::instructionEI() -> void { Q = 0;
INTE = 1;
EI = 1; //ignore maskable interrupts until after the next instruction
}
auto I8080::instructionEX_irr_rr(n16& x, n16& y) -> void { Q = 0;
WZL = read(x + 0);
WZH = read(x + 1);
wait(1);
write(x + 0, y >> 0);
write(x + 1, y >> 8);
wait(2);
y = WZ;
}
auto I8080::instructionEX_rr_rr(n16& x, n16& y) -> void { Q = 0;
swap(x, y);
}
auto I8080::instructionHALT() -> void { Q = 0;
HALT = 1;
}
auto I8080::instructionIN_a_in() -> void { Q = 0;
WZL = operand();
WZH = A;
A = in(WZ++);
}
auto I8080::instructionINC_irr(n16& x) -> void { Q = 1;
auto addr = x;
auto data = read(addr);
wait(1);
write(addr, INC(data));
}
auto I8080::instructionINC_r(n8& x) -> void { Q = 1;
x = INC(x);
}
auto I8080::instructionINC_rr(n16& x) -> void { Q = 0;
wait(2);
x++;
}
auto I8080::instructionJP_c_nn(bool c) -> void { Q = 0;
WZ = operands();
if(c) PC = WZ;
}
auto I8080::instructionJP_rr(n16& x) -> void { Q = 0;
PC = x;
}
auto I8080::instructionLD_a_inn() -> void { Q = 0;
WZ = operands();
A = read(WZ++);
}
auto I8080::instructionLD_a_irr(n16& x) -> void { Q = 0;
WZ = x;
A = read(x);
WZ++;
}
auto I8080::instructionLD_inn_a() -> void { Q = 0;
WZ = operands();
write(WZ++, A);
WZH = A;
}
auto I8080::instructionLD_inn_rr(n16& x) -> void { Q = 0;
WZ = operands();
write(WZ + 0, x >> 0);
write(WZ + 1, x >> 8);
WZ++;
}
auto I8080::instructionLD_irr_a(n16& x) -> void { Q = 0;
WZ = x;
write(x, A);
WZL++;
WZH = A;
}
auto I8080::instructionLD_irr_n(n16& x) -> void { Q = 0;
auto addr = x;
auto data = operand();
if(&x == &ix.word || &x == &iy.word) wait(2);
write(addr, data);
}
auto I8080::instructionLD_irr_r(n16& x, n8& y) -> void { Q = 0;
write(x, y);
}
auto I8080::instructionLD_r_n(n8& x) -> void { Q = 0;
x = operand();
}
auto I8080::instructionLD_r_irr(n8& x, n16& y) -> void { Q = 0;
x = read(y);
}
auto I8080::instructionLD_r_r(n8& x, n8& y) -> void { Q = 0;
x = y;
}
auto I8080::instructionLD_rr_inn(n16& x) -> void { Q = 0;
WZ = operands();
x.byte(0) = read(WZ);
WZ++;
x.byte(1) = read(WZ);
}
auto I8080::instructionLD_rr_nn(n16& x) -> void { Q = 0;
x = operands();
}
auto I8080::instructionLD_sp_rr(n16& x) -> void { Q = 0;
wait(2);
SP = x;
}
auto I8080::instructionNOP() -> void { Q = 0;
}
auto I8080::instructionOR_a_irr(n16& x) -> void { Q = 1;
A = OR(A, read(x));
}
auto I8080::instructionOR_a_n() -> void { Q = 1;
A = OR(A, operand());
}
auto I8080::instructionOR_a_r(n8& x) -> void { Q = 1;
A = OR(A, x);
}
auto I8080::instructionOUT_in_a() -> void { Q = 0;
WZL = operand();
WZH = A;
out(WZ, A);
WZL++;
}
//note: even though "pop af" affects flags, it does not set Q
auto I8080::instructionPOP_rr(n16& x) -> void { Q = 0;
x = pop();
if(&x == &af.word) F = (F & 0xd7) | 2;
}
auto I8080::instructionPUSH_rr(n16& x) -> void { Q = 0;
wait(1);
if(&x == &af.word) F = (F & 0xd7) | 2;
push(x);
}
auto I8080::instructionRET() -> void { Q = 0;
WZ = pop();
PC = WZ;
}
auto I8080::instructionRET_c(bool c) -> void { Q = 0;
wait(1);
if(!c) return;
WZ = pop();
PC = WZ;
}
auto I8080::instructionRLA() -> void { Q = 1;
bool c = A.bit(7);
A = A << 1 | CF;
CF = c;
}
auto I8080::instructionRLCA() -> void { Q = 1;
bool c = A.bit(7);
A = A << 1 | c;
CF = c;
}
auto I8080::instructionRRA() -> void { Q = 1;
bool c = A.bit(0);
A = CF << 7 | A >> 1;
CF = c;
}
auto I8080::instructionRRCA() -> void { Q = 1;
bool c = A.bit(0);
A = c << 7 | A >> 1;
CF = c;
}
auto I8080::instructionRST_o(n3 vector) -> void { Q = 0;
wait(1);
push(PC);
WZ = vector << 3;
PC = WZ;
}
auto I8080::instructionSBC_a_irr(n16& x) -> void { Q = 1;
A = SUB(A, read(x), CF);
}
auto I8080::instructionSBC_a_n() -> void { Q = 1;
A = SUB(A, operand(), CF);
}
auto I8080::instructionSBC_a_r(n8& x) -> void { Q = 1;
A = SUB(A, x, CF);
}
auto I8080::instructionSCF() -> void {
CF = 1;
Q = 1;
}
auto I8080::instructionSUB_a_irr(n16& x) -> void { Q = 1;
A = SUB(A, read(x));
}
auto I8080::instructionSUB_a_n() -> void { Q = 1;
A = SUB(A, operand());
}
auto I8080::instructionSUB_a_r(n8& x) -> void { Q = 1;
A = SUB(A, x);
}
auto I8080::instructionXOR_a_irr(n16& x) -> void { Q = 1;
A = XOR(A, read(x));
}
auto I8080::instructionXOR_a_n() -> void { Q = 1;
A = XOR(A, operand());
}
auto I8080::instructionXOR_a_r(n8& x) -> void { Q = 1;
A = XOR(A, x);
}
+48
View File
@@ -0,0 +1,48 @@
auto I8080::wait(u32 clocks) -> void {
step(clocks);
}
auto I8080::opcode() -> n8 {
step(4);
return bus->read(PC++);
}
auto I8080::operand() -> n8 {
step(3);
return bus->read(PC++);
}
auto I8080::operands() -> n16 {
n16 data = operand() << 0;
return data | operand() << 8;
}
auto I8080::push(n16 x) -> void {
write(--SP, x >> 8);
write(--SP, x >> 0);
}
auto I8080::pop() -> n16 {
n16 data = read(SP++) << 0;
return data | read(SP++) << 8;
}
auto I8080::read(n16 address) -> n8 {
step(3);
return bus->read(address);
}
auto I8080::write(n16 address, n8 data) -> void {
step(3);
return bus->write(address, data);
}
auto I8080::in(n16 address) -> n8 {
step(3);
return bus->in(address);
}
auto I8080::out(n16 address, n8 data) -> void {
step(3);
return bus->out(address, data);
}
@@ -0,0 +1,22 @@
#define AF af.word
#define BC bc.word
#define DE de.word
#define HL hl.word
#define WZ wz.word
#define WZH wz.byte.hi
#define WZL wz.byte.lo
#define A af.byte.hi
#define F af.byte.lo
#define B bc.byte.hi
#define C bc.byte.lo
#define D de.byte.hi
#define E de.byte.lo
#define H hl.byte.hi
#define L hl.byte.lo
#define CF af.byte.lo.bit(0)
#define PF af.byte.lo.bit(2)
#define HF af.byte.lo.bit(4)
#define ZF af.byte.lo.bit(6)
#define SF af.byte.lo.bit(7)
@@ -0,0 +1,22 @@
auto I8080::serialize(serializer& s) -> void {
s((u32&)prefix);
s(af.word);
s(bc.word);
s(de.word);
s(hl.word);
s(af_.word);
s(bc_.word);
s(de_.word);
s(hl_.word);
s(ix.word);
s(iy.word);
s(ir.word);
s(wz.word);
s(SP);
s(PC);
s(EI);
s(P);
s(Q);
s(HALT);
s(INTE);
}
@@ -197,9 +197,9 @@ template<u32 Size> auto M68000::instructionASR(n4 count, DataRegister with) -> v
template<u32 Size> auto M68000::instructionASR(DataRegister from, DataRegister with) -> void {
auto count = read<Long>(from) & 63;
prefetch();
idle((Size != Long ? 2 : 4) + count * 2);
auto result = ASR<Size>(read<Size>(with), count);
prefetch();
write<Size>(with, result);
}
+1 -1
View File
@@ -66,7 +66,7 @@ auto M68000::exception(u32 exception, u32 vector, u32 priority) -> void {
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)
idle(14+4); // assuming autovector cycles (approximated for Megadrive)
r.i = priority;
}
@@ -123,6 +123,12 @@ auto MOS6502::algorithmDEC(n8 i) -> n8 {
return i;
}
auto MOS6502::algorithmDCP(n8 i) -> n8 {
auto value = algorithmDEC(i);
A = algorithmCMP(value);
return value;
}
auto MOS6502::algorithmEOR(n8 i) -> n8 {
n8 o = A ^ i;
Z = o == 0;
@@ -137,6 +143,12 @@ auto MOS6502::algorithmINC(n8 i) -> n8 {
return i;
}
auto MOS6502::algorithmISC(n8 i) -> n8 {
auto value = algorithmINC(i);
A = algorithmSBC(value);
return value;
}
auto MOS6502::algorithmLD(n8 i) -> n8 {
Z = i == 0;
N = i.bit(7);
@@ -151,6 +163,12 @@ auto MOS6502::algorithmLSR(n8 i) -> n8 {
return i;
}
auto MOS6502::algorithmSRE(n8 i) -> n8 {
auto value = algorithmLSR(i);
A = algorithmEOR(value);
return value;
}
auto MOS6502::algorithmORA(n8 i) -> n8 {
n8 o = A | i;
Z = o == 0;
@@ -167,6 +185,12 @@ auto MOS6502::algorithmROL(n8 i) -> n8 {
return i;
}
auto MOS6502::algorithmRLA(n8 i) -> n8 {
auto value = algorithmROL(i);
A = algorithmAND(value);
return value;
}
auto MOS6502::algorithmROR(n8 i) -> n8 {
bool c = C;
C = i.bit(0);
@@ -176,6 +200,12 @@ auto MOS6502::algorithmROR(n8 i) -> n8 {
return i;
}
auto MOS6502::algorithmRRA(n8 i) -> n8 {
auto value = algorithmROR(i);
A = algorithmADC(value);
return value;
}
auto MOS6502::algorithmSBC(n8 i) -> n8 {
i = ~i;
i16 o = A + i + C;
@@ -200,13 +230,7 @@ auto MOS6502::algorithmSBC(n8 i) -> n8 {
}
auto MOS6502::algorithmSLO(n8 i) -> n8 {
C = i.bit(7);
i <<= 1;
Z = i == 0;
N = i.bit(7);
A |= i;
Z = A == 0;
N = A.bit(7);
return i;
auto value = algorithmASL(i);
A = algorithmORA(value);
return value;
}
@@ -51,6 +51,7 @@ auto MOS6502::instruction() -> void {
op(0x24, ZeroPageRead, fp(BIT), A)
op(0x25, ZeroPageRead, fp(AND), A)
op(0x26, ZeroPageModify, fp(ROL))
op(0x27, ZeroPageModify, fp(RLA))
op(0x28, PullP)
op(0x29, Immediate, fp(AND), A)
op(0x2a, Implied, fp(ROL), A)
@@ -58,22 +59,28 @@ auto MOS6502::instruction() -> void {
op(0x2c, AbsoluteRead, fp(BIT), A)
op(0x2d, AbsoluteRead, fp(AND), A)
op(0x2e, AbsoluteModify, fp(ROL))
op(0x2f, AbsoluteModify, fp(RLA))
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(0x37, ZeroPageModify, fp(RLA), X)
op(0x38, Set, C)
op(0x39, AbsoluteRead, fp(AND), A, Y)
op(0x3a, NoOperation)
op(0x3b, AbsoluteModify, fp(RLA), Y)
op(0x3c, NoOperationAbsolute, X)
op(0x3d, AbsoluteRead, fp(AND), A, X)
op(0x3e, AbsoluteModify, fp(ROL), X)
op(0x3f, AbsoluteModify, fp(RLA), X)
op(0x40, ReturnInterrupt)
op(0x41, IndirectXRead, fp(EOR), A)
op(0x43, IndirectXRead, fp(SRE), X)
op(0x44, NoOperationZeroPage)
op(0x45, ZeroPageRead, fp(EOR), A)
op(0x46, ZeroPageModify, fp(LSR))
op(0x47, ZeroPageModify, fp(SRE))
op(0x48, Push, A)
op(0x49, Immediate, fp(EOR), A)
op(0x4a, Implied, fp(LSR), A)
@@ -81,22 +88,27 @@ auto MOS6502::instruction() -> void {
op(0x4c, JumpAbsolute)
op(0x4d, AbsoluteRead, fp(EOR), A)
op(0x4e, AbsoluteModify, fp(LSR))
op(0x4f, AbsoluteModify, fp(SRE))
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(0x57, ZeroPageModify, fp(SRE), X)
op(0x58, Clear, I)
op(0x59, AbsoluteRead, fp(EOR), A, Y)
op(0x5a, NoOperation)
op(0x5b, AbsoluteModify, fp(SRE), Y)
op(0x5c, NoOperationAbsolute, X)
op(0x5d, AbsoluteRead, fp(EOR), A, X)
op(0x5e, AbsoluteModify, fp(LSR), X)
op(0x5f, AbsoluteModify, fp(SRE), 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(0x67, ZeroPageModify, fp(RRA))
op(0x68, Pull, A)
op(0x69, Immediate, fp(ADC), A)
op(0x6a, Implied, fp(ROR), A)
@@ -104,17 +116,21 @@ auto MOS6502::instruction() -> void {
op(0x6c, JumpIndirect)
op(0x6d, AbsoluteRead, fp(ADC), A)
op(0x6e, AbsoluteModify, fp(ROR))
op(0x6f, AbsoluteModify, fp(RRA))
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(0x77, ZeroPageModify, fp(RRA), X)
op(0x78, Set, I)
op(0x79, AbsoluteRead, fp(ADC), A, Y)
op(0x7a, NoOperation)
op(0x7b, AbsoluteModify, fp(RRA), Y)
op(0x7c, NoOperationAbsolute, X)
op(0x7d, AbsoluteRead, fp(ADC), A, X)
op(0x7e, AbsoluteModify, fp(ROR), X)
op(0x7f, AbsoluteModify, fp(RRA), X)
op(0x80, NoOperationImmediate)
op(0x81, IndirectXWrite, A)
op(0x82, NoOperationImmediate)
@@ -167,6 +183,7 @@ auto MOS6502::instruction() -> void {
op(0xc4, ZeroPageRead, fp(CPY), Y)
op(0xc5, ZeroPageRead, fp(CMP), A)
op(0xc6, ZeroPageModify, fp(DEC))
op(0xc7, ZeroPageModify, fp(DCP))
op(0xc8, Implied, fp(INC), Y)
op(0xc9, Immediate, fp(CMP), A)
op(0xca, Implied, fp(DEC), X)
@@ -174,23 +191,28 @@ auto MOS6502::instruction() -> void {
op(0xcc, AbsoluteRead, fp(CPY), Y)
op(0xcd, AbsoluteRead, fp(CMP), A)
op(0xce, AbsoluteModify, fp(DEC))
op(0xcf, AbsoluteModify, fp(DCP))
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(0xd7, ZeroPageModify, fp(DCP), X)
op(0xd8, Clear, D)
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
op(0xda, NoOperation)
op(0xdb, AbsoluteModify, fp(DCP), Y)
op(0xdc, NoOperationAbsolute, X)
op(0xdd, AbsoluteRead, fp(CMP), A, X)
op(0xde, AbsoluteModify, fp(DEC), X)
op(0xdf, AbsoluteModify, fp(DCP), 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))
op(0xe7, ZeroPageModify, fp(ISC))
op(0xe8, Implied, fp(INC), X)
op(0xe9, Immediate, fp(SBC), A)
op(0xea, NoOperation)
@@ -198,17 +220,21 @@ auto MOS6502::instruction() -> void {
op(0xec, AbsoluteRead, fp(CPX), X)
op(0xed, AbsoluteRead, fp(SBC), A)
op(0xee, AbsoluteModify, fp(INC))
op(0xef, AbsoluteModify, fp(ISC))
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(0xf7, ZeroPageModify, fp(ISC), X)
op(0xf8, Set, D)
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
op(0xfa, NoOperation)
op(0xfb, AbsoluteModify, fp(ISC), Y)
op(0xfc, NoOperationAbsolute, X)
op(0xfd, AbsoluteRead, fp(SBC), A, X)
op(0xfe, AbsoluteModify, fp(INC), X)
op(0xff, AbsoluteModify, fp(ISC), X)
}
//unimplemented instruction
@@ -42,13 +42,18 @@ struct MOS6502 {
auto algorithmCPX(n8) -> n8;
auto algorithmCPY(n8) -> n8;
auto algorithmDEC(n8) -> n8;
auto algorithmDCP(n8) -> n8;
auto algorithmEOR(n8) -> n8;
auto algorithmINC(n8) -> n8;
auto algorithmISC(n8) -> n8;
auto algorithmLD (n8) -> n8;
auto algorithmLSR(n8) -> n8;
auto algorithmSRE(n8) -> n8;
auto algorithmORA(n8) -> n8;
auto algorithmROL(n8) -> n8;
auto algorithmRLA(n8) -> n8;
auto algorithmROR(n8) -> n8;
auto algorithmRRA(n8) -> n8;
auto algorithmSBC(n8) -> n8;
auto algorithmSLO(n8) -> n8;
@@ -5,7 +5,7 @@ auto SH2::hint(P&&... p) const -> string {
return {std::forward<P>(p)...};
}
auto SH2::disassembleInstruction() -> string {
auto SH2::disassembleInstruction(u16 opcode) -> string {
vector<string> s;
auto registerName = [&](u32 r) -> string {
@@ -48,8 +48,6 @@ auto SH2::disassembleInstruction() -> string {
return {"0x", hex(PC + (i12)d * 2, 8L)};
};
u16 opcode = readWord(PC - 4);
#define n (opcode >> 8 & 0x00f)
#define m (opcode >> 4 & 0x00f)
#define i (opcode >> 0 & 0x0ff)
+1 -1
View File
@@ -1,6 +1,6 @@
auto SH2::exceptionHandler() -> void {
if(!exceptions) return;
if(inDelaySlot()) { cyclesUntilSync = 0; return; }
if(inDelaySlot()) { cyclesUntilRecompilerExit = 0; return; }
if(exceptions & ResetCold) {
exceptions &= ~ResetCold;
+5 -1
View File
@@ -23,6 +23,7 @@ auto SH2::instruction() -> void {
}
ID = 0;
u16 opcode = readWord(PC - 4);
instructionPrologue(opcode);
execute(opcode);
instructionEpilogue();
}
@@ -35,7 +36,10 @@ auto SH2::instruction() -> void {
do {
auto block = recompiler.block(PC - 4);
block->execute(*this);
} while (CCR < cyclesUntilSync);
} while (CCR < cyclesUntilRecompilerExit);
// Reset the count as it may have been set to 0 for an early exit
cyclesUntilRecompilerExit = recompilerStepCycles;
step(CCR);
CCR = 0;
+6 -3
View File
@@ -43,6 +43,7 @@ auto SH2::Recompiler::pool(u32 address) -> Pool* {
if(!pool) {
pool = (Pool*)allocator.acquire(sizeof(Pool));
memory::jitprotect(false);
*pool = {};
pool->generation = generation;
memory::jitprotect(true);
} else if(address >> 29 == Area::Cached && pool->generation != generation) {
@@ -115,9 +116,7 @@ auto SH2::Recompiler::hash(u32 address, u8 size) -> u64 {
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);
allocator.release();
reset();
}
@@ -130,6 +129,10 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
inDelaySlot = 1; //force runtime check on first instruction
while(true) {
u16 instruction = instructions[index++];
if(callInstructionPrologue) {
mov32(reg(1), imm(instruction));
call(&SH2::instructionPrologue);
}
auto branch = emitInstruction(instruction);
inDelaySlot = branch == Branch::Slot;
add64(CCR, CCR, imm(1));
+5 -3
View File
@@ -48,7 +48,7 @@ auto SH2::power(bool reset) -> void {
ET = 0;
ID = 0;
exceptions = !reset ? ResetCold : ResetWarm;
cyclesUntilSync = 0;
cyclesUntilRecompilerExit = 0;
cache = {*this};
intc = {*this};
@@ -64,8 +64,10 @@ auto SH2::power(bool reset) -> void {
cache.power();
if constexpr(Accuracy::Recompiler) {
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
if(!reset) {
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
recompiler.allocator.resize(64_MiB, bump_allocator::executable, buffer);
}
recompiler.reset();
}
}
+5 -3
View File
@@ -7,6 +7,7 @@
namespace ares {
struct SH2 {
virtual auto instructionPrologue(u16 instruction) -> void = 0;
virtual auto step(u32 clocks) -> void = 0;
virtual auto busReadByte(u32 address) -> u32 = 0;
virtual auto busReadWord(u32 address) -> u32 = 0;
@@ -189,7 +190,7 @@ struct SH2 {
//disassembler.cpp
template<typename... P> auto hint(P&&...) const -> string;
auto disassembleInstruction() -> string;
auto disassembleInstruction(u16 opcode) -> string;
auto disassembleContext() -> string;
static constexpr u32 undefined = 0;
@@ -245,8 +246,8 @@ struct SH2 {
};
u32 exceptions = 0; //delayed exception flags
s32 cyclesUntilSync = 0;
s32 minCyclesBetweenSyncs = 0;
s32 cyclesUntilRecompilerExit = 0;
s32 recompilerStepCycles = 0;
struct Recompiler : recompiler::generic {
SH2& self;
@@ -299,6 +300,7 @@ struct SH2 {
static auto mask(u8 address, u8 size) -> u64;
bool callInstructionPrologue = false;
bool inDelaySlot;
u32 generation;
bump_allocator allocator;
+3 -3
View File
@@ -137,7 +137,7 @@ template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
}
case Area::Uncached: {
cyclesUntilSync = 0;
cyclesUntilRecompilerExit = 0;
return busWriteByte(address & 0x1fff'ffff, data);
}
@@ -186,7 +186,7 @@ template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
}
case Area::Uncached: {
cyclesUntilSync = 0;
cyclesUntilRecompilerExit = 0;
return busWriteWord(address & 0x1fff'fffe, data);
}
@@ -232,7 +232,7 @@ template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
}
case Area::Uncached: {
cyclesUntilSync = 0;
cyclesUntilRecompilerExit = 0;
return busWriteLong(address & 0x1fff'fffc, data);
}
@@ -1,5 +1,5 @@
auto SH2::INTC::run() -> void {
if(self->inDelaySlot()) { self->cyclesUntilSync = 0; return; }
if(self->inDelaySlot()) { self->cyclesUntilRecompilerExit = 0; return; }
if(self->frt.pendingOutputIRQ) {
if(self->SR.I < iprb.frtip) {
@@ -13,11 +13,9 @@ auto SH2::INTC::run() -> void {
if(self->dmac.pendingIRQ & 1) {
self->interrupt(ipra.dmacip, self->dmac.vcrdma[0] & 0x7f);
self->dmac.pendingIRQ &= ~1;
print("dreq0\n");
} else if(self->dmac.pendingIRQ & 2) {
self->interrupt(ipra.dmacip, self->dmac.vcrdma[1] & 0x7f);
self->dmac.pendingIRQ &= ~2;
print("dreq1\n");
}
}
}
@@ -10,7 +10,7 @@ auto V30MZ::interrupt(u8 vector, InterruptSource source) -> bool {
//if an IRQ fires during a rep string instruction;
//flush prefix queue and seek back to first prefix.
//this allows the transfer to resume after the IRQ.
if(!prefixes.empty()) {
if(repeat()) {
PC -= prefixes.size();
prefixes.flush();
}
@@ -131,18 +131,20 @@ auto V30MZ::instructionEnter() -> void {
auto offset = fetch<Word>();
auto length = fetch<Byte>() & 0x1f;
push(BP);
BP = SP;
SP -= offset;
u16 bp = SP;
if(length) {
wait(length > 1 ? 7 : 6);
for(auto index = 1; index < length; index++) {
wait(4);
wait(2);
auto data = read<Word>(segment(SS), BP - index * 2);
push(data);
}
push(BP);
push(bp);
}
BP = bp;
SP -= offset;
}
auto V30MZ::instructionLeave() -> void {
+11 -11
View File
@@ -9,7 +9,7 @@ template<> auto V30MZ::read<Word>(u16 segment, u16 address) -> u32 {
u32 data = 0;
step(speed(segment * 16 + address));
data |= read(segment * 16 + address++) << 0;
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
data |= read(segment * 16 + address++) << 8;
return data;
}
@@ -18,11 +18,11 @@ template<> auto V30MZ::read<Long>(u16 segment, u16 address) -> u32 {
u32 data = 0;
step(speed(segment * 16 + address));
data |= read(segment * 16 + address++) << 0;
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
data |= read(segment * 16 + address++) << 8;
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
data |= read(segment * 16 + address++) << 16;
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
data |= read(segment * 16 + address++) << 24;
return data;
}
@@ -35,12 +35,12 @@ template<> auto V30MZ::write<Byte>(u16 segment, u16 address, u16 data) -> void {
template<> auto V30MZ::write<Word>(u16 segment, u16 address, u16 data) -> void {
step(speed(segment * 16 + address));
write(segment * 16 + address++, data >> 0);
if(width(address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
write(segment * 16 + address++, data >> 8);
}
template<> auto V30MZ::in<Byte>(u16 address) -> u16 {
step(1);
step(ioSpeed(address));
u16 data = 0;
data |= in(address++) << 0;
return data;
@@ -48,22 +48,22 @@ template<> auto V30MZ::in<Byte>(u16 address) -> u16 {
template<> auto V30MZ::in<Word>(u16 address) -> u16 {
u16 data = 0;
step(1);
step(ioSpeed(address));
data |= in(address++) << 0;
if(!(address & 1)) step(1);
if(ioWidth(address) == Byte || !(address & 1)) step(ioSpeed(address));
data |= in(address++) << 8;
return data;
}
template<> auto V30MZ::out<Byte>(u16 address, u16 data) -> void {
step(1);
step(ioSpeed(address));
out(address++, data >> 0);
}
template<> auto V30MZ::out<Word>(u16 address, u16 data) -> void {
step(1);
step(ioSpeed(address));
out(address++, data >> 0);
if(!(address & 1)) step(1);
if(ioWidth(address) == Byte || !(address & 1)) step(ioSpeed(address));
out(address++, data >> 8);
}
+2
View File
@@ -32,6 +32,8 @@ struct V30MZ {
virtual auto write(n20 address, n8 data) -> void = 0;
virtual auto in(n16 port) -> n8 = 0;
virtual auto out(n16 port, n8 data) -> void = 0;
virtual auto ioWidth(n16 port) -> u32 = 0;
virtual auto ioSpeed(n16 port) -> n32 = 0;
//v30mz.cpp
auto power() -> void;
@@ -205,9 +205,10 @@ E S.h = 0x01;
auto WDC65816::instructionPullB() -> void {
idle();
idle();
L B = pull();
L B = pullN();
ZF = B == 0;
NF = B & 0x80;
E S.h = 0x01;
}
auto WDC65816::instructionPullP() -> void {
@@ -290,6 +290,7 @@ auto Z80::instructionINDR() -> void { Q = 1;
if(!B) return;
wait(5);
PC -= 2;
WZ = PC + 1;
XF = PC.bit(11);
YF = PC.bit(13);
@@ -327,6 +328,7 @@ auto Z80::instructionINIR() -> void { Q = 1;
if(!B) return;
wait(5);
PC -= 2;
WZ = PC + 1;
XF = PC.bit(11);
YF = PC.bit(13);
@@ -516,6 +518,7 @@ auto Z80::instructionOTDR() -> void { Q = 1;
if(!B) return;
wait(5);
PC -= 2;
WZ = PC + 1;
XF = PC.bit(11);
YF = PC.bit(13);
@@ -537,6 +540,7 @@ auto Z80::instructionOTIR() -> void { Q = 1;
if(!B) return;
wait(5);
PC -= 2;
WZ = PC + 1;
XF = PC.bit(11);
YF = PC.bit(13);
+1 -4
View File
@@ -14,10 +14,7 @@ auto TMS9918::Background::run(n8 hoffset, n8 voffset) -> void {
}
auto TMS9918::Background::text1(n8 hoffset, n8 voffset) -> void {
// Offset by 6px and blank the last 10px to centre the display
i32 x = hoffset - 6;
if (x < 0) return;
if (hoffset > 246) return;
i32 x = hoffset;
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
nameAddress.bit(10,13) = io.nameTableAddress;
+5 -3
View File
@@ -1,5 +1,7 @@
auto TMS9918::DAC::setup(n8 y) -> void {
output = self.screen->pixels().data() + y * 256;
auto TMS9918::DAC::setup(n9 y) -> void {
y = (y + 27) % 243;
output = self.screen->pixels().data() + y * 284;
for (u32 x : range(284)) output[x] = io.colorBackground;
}
auto TMS9918::DAC::run(n8 x, n8 y) -> void {
@@ -8,7 +10,7 @@ auto TMS9918::DAC::run(n8 x, n8 y) -> void {
if(self.background.output.color) color = self.background.output.color;
if(self.sprite.output.color) color = self.sprite.output.color;
}
output[x] = color;
output[(x + 13) % 284] = color;
}
auto TMS9918::DAC::power() -> void {
+1 -1
View File
@@ -18,11 +18,11 @@ auto TMS9918::unload() -> void {
}
auto TMS9918::main() -> void {
dac.setup(io.vcounter);
if(io.vcounter < 192) {
n8 y = io.vcounter;
background.setup(y);
sprite.setup(y);
dac.setup(y);
for(n8 x : range(256)) {
background.run(x, y);
sprite.run(x, y);
+1 -1
View File
@@ -101,7 +101,7 @@ protected:
TMS9918& self;
//dac.cpp
auto setup(n8 voffset) -> void;
auto setup(n9 voffset) -> void;
auto run(n8 hoffset, n8 voffset) -> void;
auto power() -> void;
+1 -4
View File
@@ -20,10 +20,7 @@ auto V9938::Background::run(n8 x, n8 y) -> void {
}
auto V9938::Background::text1(n8 hoffset, n8 voffset) -> void {
// Offset by 6px and blank the last 10px to centre the display
i32 x = hoffset - 6;
if (x < 0) return;
if (hoffset > 246) return;
i32 x = hoffset;
n14 nameAddress = (voffset.bit(3,7) * 40) + (x / 6);
nameAddress.bit(10,16) = io.nameTableAddress.bit(10,16);
+7 -4
View File
@@ -1,7 +1,10 @@
auto V9938::DAC::setup(n8 voffset) -> void {
n9 vcenter = voffset + (!self.overscan() ? 10 : 0); //centers output within buffer
output = self.screen->pixels().data() + vcenter * 1024;
if(self.interlace() && self.field()) output += 512;
auto V9938::DAC::setup(n9 voffset) -> void {
//TODO: Verify border in overscan mode
voffset = (voffset + (self.latch.overscan ? 13 : 27)) % 243;
output = self.screen->pixels().data() + voffset * 1136;
if(self.interlace() && self.field()) output += 568;
for(auto n: range(568)) output[n] = self.pram[self.io.colorBackground];
output += 26;
}
auto V9938::DAC::run(n8 hoffset, n8 voffset) -> void {
+1 -1
View File
@@ -19,12 +19,12 @@ auto V9938::unload() -> void {
}
auto V9938::main() -> void {
dac.setup(io.vcounter);
if(io.vcounter < vlines()) {
n9 y = io.vcounter;
latch.vscroll = io.vscroll;
background.setup(y);
sprite.setup(y + latch.vscroll);
dac.setup(y);
while(io.hcounter < 256) {
n9 x = io.hcounter;
+1 -1
View File
@@ -154,7 +154,7 @@ protected:
V9938& self;
//dac.cpp
auto setup(n8 voffset) -> void;
auto setup(n9 voffset) -> void;
auto run(n8 hoffset, n8 voffset) -> void;
auto power() -> void;
+33
View File
@@ -0,0 +1,33 @@
namespace Board {
#include "coleco.cpp"
#include "xin1.cpp"
#include "megacart.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 0; }
virtual auto write(n16 address, n8 data) -> void {}
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;
};
}
+25
View File
@@ -0,0 +1,25 @@
struct coleco : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
auto load() -> void override {
Interface::load(rom, "program.rom");
}
auto save() -> void override {}
auto unload() -> void override {}
auto read(n16 address) -> n8 override {
return rom.read(address);
}
auto write(n16 address, n8 data) -> void override {
return;
}
auto power() -> void override {}
auto serialize(serializer& s) -> void override {}
};
+35
View File
@@ -0,0 +1,35 @@
struct megacart : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
auto load() -> void override {
Interface::load(rom, "program.rom");
bankCount = rom.size() >> 14;
}
auto save() -> void override {}
auto unload() -> void override {}
auto read(n16 address) -> n8 override {
if(address >= 0x7fc0) return bank = address & (bankCount - 1);
if(address >= 0x4000) return rom.read((bank << 14) + (address - 0x4000));
return rom.read((bankCount << 14) + (address - 0x4000));
}
auto write(n16 address, n8 data) -> void override {
return;
}
auto power() -> void override {
bank = 0;
}
auto serialize(serializer& s) -> void override {
s(bank);
s(bankCount);
}
n16 bank;
n16 bankCount;
};
+34
View File
@@ -0,0 +1,34 @@
struct xin1 : Interface {
using Interface::Interface;
Memory::Readable<n8> rom;
auto load() -> void override {
Interface::load(rom, "program.rom");
bankCount = rom.size() / 0x8000;
}
auto save() -> void override {}
auto unload() -> void override {}
auto read(n16 address) -> n8 override {
if(address >= 0x7fc0) return bank = (address - 0x7fc0) % bankCount;
return rom.read(address + (bank * 0x8000));
}
auto write(n16 address, n8 data) -> void override {
return;
}
auto power() -> void override {
bank = bankCount - 1;
}
auto serialize(serializer& s) -> void override {
s(bank);
s(bankCount);
}
n16 bank;
n16 bankCount;
};
+23 -10
View File
@@ -3,6 +3,7 @@
namespace ares::ColecoVision {
Cartridge& cartridge = cartridgeSlot.cartridge;
#include "board/board.cpp"
#include "slot.cpp"
#include "serialization.cpp"
@@ -16,31 +17,43 @@ auto Cartridge::connect() -> void {
information = {};
information.title = pak->attribute("title");
information.region = pak->attribute("region");
information.board = pak->attribute("board");
if(auto fp = pak->read("program.rom")) {
rom.allocate(fp->size());
rom.load(fp);
}
if(information.board == "xin1") board = new Board::xin1{*this};
if(information.board == "coleco") board = new Board::coleco{*this};
if(information.board == "megacart") board = new Board::megacart{*this};
if(!board) board = new Board::Interface{*this};
board->pak = pak;
board->load();
power();
}
auto Cartridge::disconnect() -> void {
if(!node) return;
rom.reset();
pak.reset();
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(address >= rom.size()) return 0xff;
return rom.read(address);
if(board) return board->read(address);
return 0;
}
auto Cartridge::write(n16 address, n8 data) -> void {
if(board) return board->write(address, data);
}
}
+8 -3
View File
@@ -1,3 +1,6 @@
struct Cartridge;
#include "board/board.hpp"
struct Cartridge {
Node::Peripheral node;
VFS::Pak pak;
@@ -14,17 +17,19 @@ struct Cartridge {
auto power() -> void;
auto read(n16 address) -> n8;
auto write(n16 address, n8 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
private:
unique_pointer<Board::Interface> board;
//private:
struct Information {
string title;
string region;
string board;
} information;
Memory::Readable<n8> rom;
};
#include "slot.hpp"
+1
View File
@@ -1,5 +1,6 @@
auto CPU::read(n16 address) -> n8 {
n8 data = 0xff;
if(auto result = platform->cheat(address)) return *result;
if(address >= 0x0000 && address <= 0x1fff && io.replaceBIOS) return expansion.read(address);
if(address >= 0x2000 && address <= 0x7fff && io.replaceRAM ) return expansion.read(address);
if(address >= 0x0000 && address <= 0x1fff) return system.bios[address & 0x1fff];
+16 -3
View File
@@ -11,12 +11,12 @@ auto VDP::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("VDP");
screen = node->append<Node::Video::Screen>("Screen", 256, 192);
screen = node->append<Node::Video::Screen>("Screen", 284, 243);
screen->colors(1 << 4, {&VDP::color, this});
screen->setSize(256, 192);
screen->setSize(284, 243);
screen->setScale(1.0, 1.0);
screen->setAspect(8.0, 7.0);
screen->setViewport(0, 0, 256, 192);
screen->setViewport(0, 0, 284, 243);
TMS9918::load(screen);
}
@@ -39,6 +39,19 @@ auto VDP::irq(bool line) -> void {
}
auto VDP::frame() -> void {
if(screen->overscan()) {
screen->setSize(284, 243);
screen->setViewport(0, 0, 284, 243);
} else {
int x = 16;
int y = 16;
int width = 284 - 32;
int height = 243 - 32;
screen->setSize(width, height);
screen->setViewport(x, y, width, height);
}
screen->frame();
scheduler.exit(Event::Frame);
}
+1
View File
@@ -2,6 +2,7 @@ ares.components += mos6502
ares.components += ym2149
ares.components += ym2413
ares.components += m24c
ares.components += sst39sf0x0
ares.objects += ares-fc-system
ares.objects += ares-fc-controller
+123
View File
@@ -0,0 +1,123 @@
struct Action53 : Interface {
static auto create(string id) -> Interface* {
if(id == "ACTION53") return new Action53();
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;
n8 programBankMask = (1 << programBankWidth) - 1;
if (programBankMode < 2) {
n8 outerBank = programOuterBank & ~programBankMask;
n8 innerBank = programBank & programBankMask;
return programROM.read((outerBank | innerBank) << 15 | (n15)address);
}
n9 outerBank = (programOuterBank & ~programBankMask) << 1;
n9 innerBank = programBank & ((2 << programBankWidth) - 1);
n9 offset = (address >> 14) & 1;
if(offset == (programBankMode & 1))
return programROM.read((programOuterBank << 15) | (n15)address);
return programROM.read((outerBank | innerBank) << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
// Register select
if((address & 0xF000) == 0x5000) {
currentRegister = data & 0x81;
return;
}
// Register write
if(address < 0x8000) return;
switch(currentRegister) {
case 0x00: { /* CHR bank */
characterBank = data.bit(0, 1);
if(!mirror) nametableBank = data.bit(4);
} break;
case 0x01: { /* PRG bank */
programBank = data.bit(0, 3);
if(!mirror) nametableBank = data.bit(4);
} break;
case 0x80: { /* Mode */
nametableBank = data.bit(0);
mirror = data.bit(1);
programBankMode = data.bit(2, 3);
programBankWidth = data.bit(4, 5);
} break;
case 0x81: { /* PRG outer bank */
programOuterBank = data;
} break;
}
}
inline auto addressCIRAM(n32 address) -> n11 {
if(mirror) {
return ((address >> nametableBank) & 0x0400) | (n10)address;
}
return (nametableBank << 10) | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
return ppu.readCIRAM(addressCIRAM(address));
}
if(characterROM) return characterROM.read(characterBank << 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 ppu.writeCIRAM(addressCIRAM(address), data);
}
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
currentRegister = 0;
characterBank = 0;
programBank = 0;
programOuterBank = 0xFF;
nametableBank = 0;
mirror = 0;
programBankMode = 0;
programBankWidth = 0;
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(currentRegister);
s(characterBank);
s(programBank);
s(programOuterBank);
s(nametableBank);
s(mirror);
s(programBankMode);
s(programBankWidth);
}
n8 currentRegister;
n2 characterBank;
n4 programBank;
n8 programOuterBank;
n1 nametableBank;
n1 mirror;
n2 programBankMode;
n2 programBankWidth;
};
+8
View File
@@ -1,5 +1,6 @@
namespace Board {
#include "action53.cpp"
#include "ave-nina-001.cpp"
#include "ave-nina-06.cpp"
#include "bandai-74161.cpp"
@@ -34,10 +35,12 @@ namespace Board {
#include "hvc-sxrom.cpp"
#include "hvc-txrom.cpp"
#include "hvc-uxrom.cpp"
#include "inl-nsf.cpp"
#include "irem-g101.cpp"
#include "irem-h3001.cpp"
#include "irem-lrog017.cpp"
#include "irem-tam-s1.cpp"
#include "magicfloor.cpp"
#include "mlt-action52.cpp"
#include "namco-118.cpp"
#include "namco-163.cpp"
@@ -51,9 +54,11 @@ namespace Board {
#include "taito-tc0690.cpp"
#include "taito-x1-005.cpp"
#include "taito-x1-017.cpp"
#include "unrom-512.cpp"
auto Interface::create(string board) -> Interface* {
Interface* p = nullptr;
if(!p) p = Action53::create(board);
if(!p) p = AveNina001::create(board);
if(!p) p = AveNina06::create(board);
if(!p) p = Bandai74161::create(board);
@@ -76,6 +81,7 @@ 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 = INL_NSF::create(board);
if(!p) p = IremG101::create(board);
if(!p) p = IremH3001::create(board);
if(!p) p = IremLROG017::create(board);
@@ -92,6 +98,7 @@ 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 = MagicFloor::create(board);
if(!p) p = MltAction52::create(board);
if(!p) p = Namco118::create(board);
if(!p) p = Namco163::create(board);
@@ -105,6 +112,7 @@ auto Interface::create(string board) -> Interface* {
if(!p) p = TaitoTC0690::create(board);
if(!p) p = TaitoX1005::create(board);
if(!p) p = TaitoX1017::create(board);
if(!p) p = UNROM512::create(board);
if(!p) p = new Interface;
return p;
}
+10 -4
View File
@@ -1,26 +1,30 @@
struct GTROM : Interface {
static auto create(string id) -> Interface* {
if(id == "GTROM" ) return new GTROM();
if(id == "GTROM") return new GTROM();
return nullptr;
}
Memory::Readable<n8> programROM;
SST39SF0x0 flash;
Memory::Writable<n8> characterRAM;
Memory::Writable<n8> videoRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(flash.flash, "program.flash");
Interface::load(characterRAM, "character.ram");
videoRAM.allocate(16_KiB);
}
auto save() -> void override {
if(auto fp = pak->write("program.flash")) {
flash.flash.save(fp);
}
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);
return flash.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
@@ -30,6 +34,8 @@ struct GTROM : Interface {
videoBank = data.bit(5);
return;
}
if(address < 0x8000) return;
flash.write(programBank << 15 | (n15)address, data);
}
auto readCHR(n32 address, n8 data) -> n8 override {
+1 -1
View File
@@ -200,7 +200,7 @@ struct HVC_ExROM : Interface { //MMC5
address &= 0x7fff;
} else if(programMode == 1) {
if((address & 0xc000) == 0x8000) bank = programBank[1] & ~1;
if((address & 0xe000) == 0xc000) bank = programBank[3] & ~1;
if((address & 0xc000) == 0xc000) bank = programBank[3] & ~1;
address &= 0x3fff;
} else if(programMode == 2) {
if((address & 0xe000) == 0x8000) bank = programBank[1] & ~1 | 0;
+62
View File
@@ -0,0 +1,62 @@
struct INL_NSF : Interface {
static auto create(string id) -> Interface* {
if(id == "INL-NSF") return new INL_NSF;
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((banks[(address >> 12) & 7] << 12) | (n12)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if((address & 0xF000) != 0x5000) return;
banks[address & 7] = data;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(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);
}
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
banks[7] = 0xFF;
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(mirror);
s(banks);
}
n1 mirror; //0 = horizontal, 1 = vertical
n8 banks[8];
};
+32
View File
@@ -0,0 +1,32 @@
struct MagicFloor : Interface {
static auto create(string id) -> Interface* {
if(id == "MAGICFLOOR") return new MagicFloor();
return nullptr;
}
Memory::Readable<n8> programROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
pin = (n2)(pak->attribute("pinout/va10").natural() - 10);
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read((n15)address);
}
auto readCHR(n32 address, n8 data) -> n8 override {
return ppu.readCIRAM(((address >> pin) & 0x400) | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
return ppu.writeCIRAM(((address >> pin) & 0x400) | (n10)address, data);
}
auto serialize(serializer& s) -> void override {
s(pin);
}
n2 pin;
};
Loaded 100 of 1295 files, more files were not shown because too many files have changed in this diff. Show more