Compare commits

...
268 Commits
Author SHA1 Message Date
Luke Usher 86db200a23 ares: update version number to v130 2022-09-30 16:09:39 +01:00
Luke Usher 2781365ac3 LICENSE: add MoltenVK license text 2022-09-30 15:57:45 +01:00
Giovanni Bajo a238a7e754 ci: cache MoltenVK builds to speed up CI 2022-09-30 15:12:35 +01:00
Giovanni Bajo 299d211ab1 ci: add signing on Windows 2022-09-30 15:12:35 +01:00
Giovanni Bajo fc2278486e ci: reactivate moltenvk build 2022-09-29 23:12:43 +01:00
Giovanni Bajo f7d5a87667 ci: add macOS notarization 2022-09-29 22:52:27 +01:00
Giovanni Bajo 3e2934529e desktop-ui: fix ares.plist by specifying the package type 2022-09-29 22:52:27 +01:00
Luke Usher ff9afca894 mia: GTROM has 16k chr-ram 2022-09-29 19:42:25 +01:00
Giovanni Bajo 3f002928ed n64: in vulkan backend, fix DP_START.
DP_START shouldn't change after a RDP DMA transfer. This was correct
in the mame-RDP backend but it was wrong in the Vulkan backend.
2022-09-29 14:07:11 +01:00
Giovanni Bajo b124e8a8f0 n64: fix option name in settings change 2022-09-29 14:07:11 +01:00
TascoDLX 0f965050b1 md: support Triple Play cart mappers 2022-09-29 08:24:53 +01:00
Luke Usher b9cd02ddee ng: fix loading of roms with different v-rom naming, fixes aof.
Note that this will cause ADPCM-B roms to be appended to ADPCM-A roms,
 but it's the best we can do without a game database with per-game mappings.
2022-09-28 22:56:03 +01:00
Luke Usher b7198a2334 ng: fix ADPCM-B serialization 2022-09-28 22:48:37 +01:00
Luke Usher 8967e05524 ng: allow ADPCM-B samples to be silenced early 2022-09-28 22:45:33 +01:00
Luke Usher 1b1e7f8f2c ng: implement ADPCM-B audio 2022-09-28 22:45:33 +01:00
TascoDLX 00eb27681e m32x-cd: workaround for Corpse Killer 32X CD gamepad bug 2022-09-28 09:17:48 +01:00
TascoDLX 9ad4b27681 md: add eeprom support for Wonder Boy V (J) 2022-09-28 09:17:48 +01:00
TascoDLX 060eaba2ea sh2: (sh7604) fix serial comm 2022-09-28 09:17:48 +01:00
TascoDLX d75a495fc1 m32x: clear adapter control reg unused bits 2022-09-28 09:17:48 +01:00
Luke Usher f593b12416 ci: switch exclusively to clang for windows builds 2022-09-27 22:24:10 +01:00
Luke Usher 2f3385776d a26: increase scale factor for consistency with other cores 2022-09-27 14:12:05 +01:00
Luke Usher fe8afe05de mia: auto-detect and load common msu-1 filename schemes 2022-09-27 11:57:13 +01:00
Luke Usher bd9f52cf8c Merge commit '35453600333f47f6173bf43228e8bc01c2df5a6e' 2022-09-26 18:23:51 +01:00
Luke Usher 3545360033 Squashed 'thirdparty/libchdr/' changes from b3c1651d0f..e02b3d68eb
e02b3d68eb Merge pull request #84 from scribam/dr_flac
146f076ca7 dr_flac: Version 0.12.39
9882eeaaa2 Bump dr_flac to latest, bump libchdr version to 0.2
44c91c96d4 Bump LZMA SDK to 22.01, need to implement asm optimizations
8cd08c0752 Upgrade to zlib 1.2.12
2781322c4a Merge pull request #78 from snickerbockers/virtual_core_file
40ec221c8a use virtual file i/o API which defaults to a stdio wrapper

git-subtree-dir: thirdparty/libchdr
git-subtree-split: e02b3d68eb759793ab8142376594c840c1194b3e
2022-09-26 18:23:51 +01:00
Luke Usher 5c61c3acbd Merge commit '3d3513e365437e5b16d6c015f55d79d0d2eb2def' 2022-09-26 18:23:47 +01:00
Luke Usher 3d3513e365 Squashed 'thirdparty/sljit/' changes from 7c37b7ebcf..4de967acd0
4de967acd0 Support memory moving of register pairs on x86 and arm.

git-subtree-dir: thirdparty/sljit
git-subtree-split: 4de967acd03499e7def404eab861e83a0689579b
2022-09-26 18:23:47 +01:00
Luke Usher 6a0f2eaf1a ng: tweak overscan 2022-09-26 15:23:12 +01:00
Luke Usher 9422ea5905 md: implement 32x reset register and remove adapterEnable hack 2022-09-26 15:01:30 +01:00
invertego 582fc8f846 sh2: store correct pc in exception frame
The value of PC indicates the fourth byte (second instruction) after the
current instruction. The value stored when an exception raised does not
maintain this four byte offset. Consequently, the RTE instruction must
offset the value it reads back by four bytes.

ares was storing the offset value (and not applying one in RTE) and this
went unnoticed until an encounter with code that manually sets up an
exception frame (in an interrupt handler common to CD 32X games).

This change also fixes the PC stored in the event of an illegal slot
exception, which should be the delayed branch target.
2022-09-26 10:34:27 +01:00
Giovanni Bajo 73893352f7 desktop-ui: show status messages on console too 2022-09-26 00:43:45 +01:00
Giovanni Bajo 47c9ec1f80 desktop-ui: add entitlement to allow JIT as a signed application on mac
Also disable library validation to allow MoltenVK to load. It seems
this is required even if the library is signed. This requires more
investigation.
2022-09-26 00:43:45 +01:00
Giovanni Bajo b482344691 Build universal macOS app on CI 2022-09-26 00:43:45 +01:00
Giovanni Bajo cbdedc9f9c Move update-subtrees.sh into scripts subdirectory 2022-09-26 00:43:45 +01:00
Giovanni Bajo 12a1585ee2 Add script to build an universal binary on mac 2022-09-26 00:43:45 +01:00
Giovanni Bajo 4644bb4494 nall: allow cross-compilation on macOS 2022-09-26 00:43:45 +01:00
Giovanni Bajo a54b551e4d desktop-ui: activate Vulkan by default on all platforms 2022-09-26 00:43:45 +01:00
Giovanni Bajo 30724164be Build MoltenVK on mac builders 2022-09-26 00:43:45 +01:00
Giovanni Bajo 517e0cc454 desktop-ui: mention "GPU acceleration" rather than Vulkan 2022-09-26 00:43:45 +01:00
Giovanni Bajo 2c489cdadd desktop-ui: allow building and bundling libMoltenVK on mac
build
2022-09-26 00:43:45 +01:00
invertego 28a956111d build: ignore -Wdeprecated-non-prototype in zlib 2022-09-25 23:04:25 +01:00
Luke Usher 7115ed9ab5 ng: clip overscan
this will be improved/made configurable later, but for now,
it removes screen-edge artifacts in many games.
2022-09-25 22:30:10 +01:00
invertego 566184f669 md: connect 32x to cart slot in cd+32x mode
This is required for register reads/writes and h/vblank signals to be
handled by the 32x.
2022-09-25 21:42:58 +01:00
TascoDLX 8b5fbf511f md: combine banked & linear carts into standard mapper 2022-09-25 09:57:38 +01:00
TascoDLX c6a6972a75 ym2612: fix volume floor 2022-09-25 09:56:14 +01:00
Luke Usher 59713ea9b1 ng: show the full frame + fix top/bottom blanking 2022-09-24 22:39:08 +01:00
Luke Usher 4e1e3e02bb ng: position sprites relative to the top border rather than line 0. 2022-09-24 16:14:56 +01:00
Luke Usher a26e1ae006 md: apply 32:35 aspect ratio 2022-09-24 15:03:40 +01:00
Luke Usher 160ddb4457 Merge remote-tracking branch 'upstream/master' 2022-09-23 17:17:52 +01:00
invertego 8dda1836ac m32x: expose cart sram through 0x900000... range
The functional part of this change is just a few additional lines in
bus-external.cpp. The rest is refactoring to model the game cart as a
separate entity from the 32x, making it possible to delegate reads and
writes through the 0x900000... range to the cart implementation.

This fixes the corrupted menus and crashes in  Golf Magazine: 36 Great
Holes Starring Fred Couples.
2022-09-23 16:13:30 +01:00
Giovanni Bajo 1e9c07c803 n64: add RDP crash support to Vulkan backend
Like #769, but for Vulkan.
2022-09-23 12:28:00 +01:00
Giovanni Bajo 18884d6aa2 Squashed 'ares/n64/vulkan/parallel-rdp/' changes from 428884dea..fc3121239
fc3121239 Update to 0f7cc6c3a13494a575dbbb977ff4424743b688d7.
d37cf0ed0 Update to 93dfb98643d0dd8094c5739300dfed818502304e.
d433bc4d2 Update to 93d77f713ba0fbcf6bb9e654924e89f808b04398.
22f03bb13 Update to 0f0f9446470f4d0aa018b88ff6280bbc4137b6f6.
482a41deb Update to 07c3d161dbeb78121be5eebdab7c810b72af822a

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: fc3121239716e3cd99ec63175c9ce5c9d088c033
2022-09-23 12:28:00 +01:00
kev4cards 2c463d0322 n64: fix control stick dead-zone scaling
Scale axially instead of radially
2022-09-23 12:27:40 +01:00
TascoDLX 2c3963c24e z80: fix extended/prefixed instruction timings + behavior
* timing info based on Z80 netlist analysis by Floh

1. Corrected cycle counts for DDCB & FDCB prefixed opcode fetches (was 17, now 16). Instructions cycle counts were increased to compensate for the lost cycle, though this corrects wrong cycle counts for corresponding instructions without a DD or FD prefix (+1 cycle).
2. Removed redundant code for instructions fixed in 1.
3. Changed BIT instructions in DDCB & FDCB blocks to not modify registers (ref: "The Undocumented Z80 Documented").
2022-09-22 07:48:04 +01:00
TascoDLX 3d57bf7914 z80: fix various instruction timings
* based on Z80 netlist analysis by Floh

1. Corrected cycle counts for EX (SP),rr (was 16, now 19) & IM n (was 12, now 8).
2. Reordered cycles for ADD/ADC/SBC HL,rr, RLD/RRD, & LD (rr),n.
3. Removed redundant code for CALL nn & IN (C).
2022-09-22 07:48:04 +01:00
Luke Usher 9fa501cc3a ps1: add simple idle loop detection 2022-09-20 11:10:31 +01:00
Luke Usher c66e3471e0 z80: fix IM0 and simplify IRQ implementation 2022-09-19 10:53:36 +01:00
Giovanni Bajo 17924254a1 GNUmakefile: add error message for incompatible GNU make
While testing parallel-RDP on macOS via MoltenVK, I found out that
a special syntax used in the N64 Makefile to compile parallel-RDP
requires a version of GNU make which is newer than the one that comes
with macOS. To avoid confusion, just error out for now.
2022-09-19 10:24:55 +01:00
invertego 27fe06917e n64: fix numerical instability in Gamepad::read() 2022-09-18 16:33:54 +01:00
invertego 164ef7b678 ps1: clamp f32 to u8 color conversions 2022-09-18 16:33:54 +01:00
invertego 2ad30215df build: fix missing override warning 2022-09-18 16:33:54 +01:00
invertego d4ae964be4 v30mz: fix shift behavior
The presence of undefined behavior (in C++) revealed several
bugs in various shift and rotate instructions. These are
deceptively hard get right without resorting to iteration.
Rather than complicate the implementation further, a few more
are now implemented wtih loops like RCL/RCR.
2022-09-18 16:33:54 +01:00
invertego 228105e6de nall: fix hashset new[]/delete mismatch 2022-09-18 16:33:54 +01:00
Giovanni Bajo 6c5e8ed49e n64: add rdp.command.crashed to serialization 2022-09-18 07:33:21 +01:00
remutro 019287a648 Why did my line endings change 2022-09-17 18:28:42 +01:00
remutro 719dac2dc1 Add missing disc command 0xf - GetParam 2022-09-17 18:28:42 +01:00
Giovanni Bajo abf2ef5832 n64: fix buffer overflows in mame RDP
RDP supports pixel coordinates in range -2048..2047, so we need to
account for up to 4096 spans.
2022-09-17 15:34:56 +01:00
Giovanni Bajo 953a50d827 n64: fix support for interlaced resolutions
The new code was completely wrong for interlaced resolutions, fix it
by changing into a simple filter on the current field.
Moreover, for some reason, the screen was only updated at 30fps.
Do it at 60fps, as both Vulkan and the native Ares screen knows how
to handle that.
2022-09-17 15:34:56 +01:00
Giovanni Bajo 7c116300bc n64: add RDP crash support
There a handful of sequences of invalid commands that can
cause the RDP to crash (it will stop processing commands until hard
reboot). Mame RDP already knows about them and reacts by throwing an
exception, that currently kills Ares because it is not caught.

This commit lets Ares actually emulate the crashed RDP by basically
freezing its command processor. A log is also emitted on console.

This is useful to homebrew developers that can do mistakes while
coding.
2022-09-17 15:34:20 +01:00
invertego cbfd7ed270 m32x: unify interpreter/recompiler sync timing 2022-09-17 15:33:52 +01:00
invertego 78d926b31d m32x: synchronize sh2 processors w/ each other 2022-09-17 15:33:52 +01:00
invertego a4d03c263f sh2: break min_cycles loop on uncached writes 2022-09-17 15:33:52 +01:00
Luke Usher 5283135a49 ps1: fix input issues in many titles 2022-09-16 22:47:19 +01:00
invertego 2c9cf460f3 sh2: initial emulation of sh7604 internal bus
Fix DMA in Metal Head and recompiler implementation of TAS instruction.
2022-09-16 09:13:49 +01:00
LuigiBlood 2bcb236dd6 n64: add dd serialization 2022-09-14 14:14:46 +01:00
LuigiBlood 77ff400bb1 n64: adjust access times as it prevented certain games to work properly 2022-09-14 14:14:46 +01:00
LuigiBlood c87166f9c5 n64: accurate ERR_SECTOR and CUR_SECTOR register info 2022-09-14 14:14:46 +01:00
LuigiBlood 6827540f11 n64: adjust seek times to be more accurate 2022-09-14 14:14:46 +01:00
LuigiBlood 0615773904 n64: finetune disk access times to be closer to real hardware tests 2022-09-14 14:14:46 +01:00
LuigiBlood c73e19bdf2 n64: only start motor state timer after seek is complete 2022-09-14 14:14:46 +01:00
LuigiBlood c0cac55065 n64: use dd command busy state 2022-09-14 14:14:46 +01:00
LuigiBlood e45e82bd19 n64: implement dd motor state management and adjust timings 2022-09-14 14:14:46 +01:00
LuigiBlood 26a7ac35ca n64: change dd seek timings 2022-09-14 14:14:46 +01:00
LuigiBlood 9748b37532 n64: rely on hardcoded block sizes (for retail and development disks) 2022-09-14 14:14:46 +01:00
LuigiBlood 0feacdc6a5 n64: recognize 64dd cic on connect using iplrom and set dd id between retail and dev 2022-09-14 14:14:46 +01:00
LuigiBlood be2495b253 n64: properly recognize and convert development disks 2022-09-14 14:14:46 +01:00
Luke Usher 24762209aa n64: support NTSC-U/NTSC-J/DEV 64DD variants 2022-09-14 14:14:46 +01:00
LuigiBlood 74ec42e889 n64: check and analyze disk region and disk ID 2022-09-14 14:14:46 +01:00
LuigiBlood fa76d1fb16 n64: map DD bus only if DD is enabled 2022-09-14 14:14:46 +01:00
LuigiBlood c437bc2d25 n64: detect errors in disk with heuristics for system disk data and return errors if attempting to read it 2022-09-14 14:14:46 +01:00
LuigiBlood 86ec0d1226 n64: redo drive controller error management 2022-09-14 14:14:46 +01:00
LuigiBlood 12c771e5c9 n64: make sure disk is present before performing disk access commands 2022-09-14 14:14:46 +01:00
LuigiBlood 451d562186 n64: support combo n64 & dd emulation 2022-09-14 14:14:46 +01:00
LuigiBlood aa5efe67ef n64: perform basic system disk area check for conversion 2022-09-14 14:14:46 +01:00
LuigiBlood 1749d4bd9b n64: support already converted mame ndd files 2022-09-14 14:14:46 +01:00
LuigiBlood d21820dab7 n64: add mouse to Nintendo 64DD emulator core 2022-09-14 14:14:46 +01:00
LuigiBlood 51474d86e7 n64: make sure cartridge.rom exists before accessing it 2022-09-14 14:14:46 +01:00
LuigiBlood 7fc641c787 n64: working dd disk save 2022-09-14 14:14:46 +01:00
LuigiBlood 1059725734 n64: add timer to mecha dd commands 2022-09-14 14:14:45 +01:00
LuigiBlood 6396c8c9ac n64: fix disk writing and add comments 2022-09-14 14:14:45 +01:00
LuigiBlood 57647bf62b n64: working 64DD disk conversion, drive read/write, 64DD real time clock 2022-09-14 14:14:45 +01:00
LuigiBlood 5176bfead1 n64: Change PIF ROM loading based on Console Region 2022-09-14 14:14:45 +01:00
LuigiBlood 91281a8247 n64: implement more DD commands including previously undocumented features 2022-09-14 14:14:45 +01:00
LuigiBlood c46beaff32 n64: add rtc.cpp and implement rtc timestamp 2022-09-14 14:14:45 +01:00
LuigiBlood b3c49a4636 n64: working rtc save 2022-09-14 14:14:45 +01:00
LuigiBlood 7ae217bc79 n64: add rtc file pak + commands 2022-09-14 14:14:45 +01:00
LuigiBlood 5d37682c6b n64: Add basic DD interface 2022-09-14 14:14:45 +01:00
LuigiBlood d70cb368de n64: add DD interrupts 2022-09-14 14:14:45 +01:00
Luke Usher 4fcfa471a4 n64: refactor to allow 64dd ipl to work
hard-coded to Retail - Japan for now.
2022-09-14 14:14:45 +01:00
LuigiBlood 01da5ed20d n64: handle 64dd cic in pif power on 2022-09-14 14:14:45 +01:00
LuigiBlood 433f230c9c n64: recognize 64dd ipl rom and cic 2022-09-14 14:14:45 +01:00
Luke Usher 91e49ab940 Revert "gb: correct oam dma startup delay"
This reverts commit e464f45605 as while allowing the test to not crash, it caused regressions in games.
2022-09-14 09:10:29 +01:00
Luke Usher e464f45605 gb: correct oam dma startup delay
from mooneye/acceptance/oam_dma_start
```
 Expected timing (fresh DMA):
; M = 0: write to $FF46 happens
; M = 1: nothing (OAM still accessible)
; M = 2: new DMA starts, OAM reads will return $FF
```

The test still fails, likely due to bad restart behavior,
but it no longer hangs.
2022-09-13 17:41:55 +01:00
Luke Usher e062350552 desktop-ui: check for game load failure for Sega Master System 2022-09-13 17:41:55 +01:00
Giovanni Bajo 74d6e885f6 Implement overflow exception in TRUNC 2022-09-13 15:08:16 +01:00
invertego ec34c02e4f Add nall/float-env.hpp 2022-09-13 15:08:16 +01:00
Giovanni Bajo 0aa84312c5 n64: improve generation of FPU exceptions.
Hook at least standard IEEE-754 exceptions on basic operations. This
should already catch most mistakes done while developing home-brew
games.
2022-09-13 15:08:16 +01:00
invertego 17a654cfc4 32x: reduce recompiler min_cycles 22 -> 19
Required by After Burner Complete
2022-09-13 11:41:49 +01:00
invertego 027a3db94c sh2: fix pc-relative moves in branch delay slots 2022-09-13 11:41:49 +01:00
invertego 38a7c74f28 build: qualify std::move/forward calls 2022-09-13 09:44:41 +01:00
Luke Usher 3147c3c996 6502: fix more SLO variants 2022-09-12 23:04:03 +01:00
Luke Usher d6605625bc 6502: implement remaining undocumented immediate addressing opcodes 2022-09-12 23:02:51 +01:00
Luke Usher e534e56002 sh2: fix broken illegal instruction debug logging
Illegal instructions were being logged after jumping to the exception handler,
causing almost useless debug output
2022-09-11 23:05:41 +01:00
TascoDLX cf9c02e571 ym2612: fix pm vibrato effect 2022-09-10 23:41:21 +01:00
Giovanni Bajo 0483193439 n64: rewrite VI resolution scaling
This should fix all games that had broken resolutions when not using
Vulkan. It also fix garbled graphics appearing on screen during scene
changes, usually at boot.
2022-09-10 17:56:15 +01:00
TascoDLX 6112bcaced ares: bump serializer version (ym2612 fix) 2022-09-10 17:12:19 +01:00
TascoDLX 64ae531ead ym2612: fix the envelope update logic 2022-09-10 17:12:19 +01:00
Luke Usher e90c318c0e md: 32X interrupt control register returns 0 in the upper bits 2022-09-09 00:22:04 +01:00
Luke Usher a4551216e9 md: better fix for 32x overscan: this one fixes bottom border also 2022-09-08 21:08:53 +01:00
Luke Usher 2e38fa5fc6 md: prevent 32x from incorrectly writing over megadrive borders
fixes overscan issues in a few titles
2022-09-08 20:50:01 +01:00
Raphael Assenat e2b96c8bab ms: Add support for the Mega Mouse
This pull request adds support for the Sega Mega Mouse. As the name implies,
this mouse was released for the MegaDrive, but as it is the case for 3/6 Button
controllers, the Mega Mouse can be safely connected to a Master System console and
even used, provided that the game software is programmed accordingly.

To my knowledge there are no official games on the SMS with mouse support, but there
is one demo I released (SMS-A-Sketch) which supports the Mega Mouse, and I have
an unreleased game that also supports it. I think it would be great to have ares
support the Mega Mouse on SMS as the only alternative at this point is to use
real hardware.
2022-09-08 09:19:53 +01:00
Raphael Assenat 4c3c07eb38 md: Fix a typo 2022-09-08 09:19:25 +01:00
Luke Usher 3e7ca5baec ms: fix incorrect hcount calculation
The VDPTEST still doesn't pass for any hcounter tests.
Inserting debug prints to print the hcounter value for every possible x coord shows correct output.
I suspect there are still some core timing issues in the z80...
2022-09-07 23:00:17 +01:00
TascoDLX c74700f35d z80: proper reset logic + implement for md 2022-09-07 08:15:52 +01:00
TascoDLX 368578d736 z80: correct timings for RET, LD (IX+d),n & LD (IY+d),n 2022-09-07 08:15:52 +01:00
TascoDLX 65d9640b20 ym2612: various improvements & fixes
1. Envelope gen clean-up + emulation of odd behaviors (see inline comments)
2. SSG-EG fixes -- mode 7, control register behavior
3. LFO PM (vibrato) fix + calc tweak
4. Clock fixes for envelope, LFO, timer B
2022-09-07 08:15:52 +01:00
Luke Usher 836f3fc976 msx: add database entry for Batman (Japan) 2022-09-06 22:24:19 +01:00
Luke Usher 5636e2b0f6 ruby: use glXSwapIntervalEXT where available 2022-09-06 18:53:45 +01:00
Luke Usher 37bc375c64 msx: distinguish between ASC16 and the r-type specific variant 2022-09-06 14:25:12 +01:00
Luke Usher 50467b7e6d Update README.md 2022-09-06 10:04:40 +01:00
Raphael Assenat dc37e934e6 md: Add support for the Mega Mouse 2022-09-06 09:10:24 +01:00
Raphael Assenat 21e5daebfc N64: Make the "Nothing" controller type in the menu work as expected
At the moment, the game sees a controller in all ports, even after
the controller type is changed from Gamepad to Nothing. (the Gamepad
instance still lives and still gets called...)

I worked around this by making sure that port->allocate gets called,
even when the selection is "Nothing". The code in port.cpp simply
does not allocate anything in this case.

For most systems and in most practical cases this probably does
not matter. But now the raphnet N64 adapter manager ROM correctly
reports "Not conneted" in ports set to Nothing.
2022-09-06 09:08:25 +01:00
Raphael Assenat 8f579b1993 n64: Leave PIF RAM as-is when no data was received
When the ->comm method returns with the invalid bit set, it means no
data was received. This happens for instance when sending invalid
or proprietary commands to a standard controller which does not answer.

When there is no answer, the PIF emulation code sets bit 7 of the receive
length byte to flag this error.

Since no data was received, the original (filler) bytes should be left
as-is in PIF RAM. This is what the hardware appears to do.

This fixes code that checks if the receive buffer got overwritten instead of
inspecting bit 7 for errors.
2022-09-06 09:08:25 +01:00
Raphael Assenat ed336af513 Fix indentation 2022-09-06 09:01:34 +01:00
Raphael Assenat 03186eb4d3 desktop-ui: Document command-line options and add --help
Ideally I think one should not need to read the source code to learn
that ares accepts command-line options and what those options are.

I wish I had known earlier that I could pass --system MSX when loading
a ROM directly, it would have saved me a lot of time. I had tried --help,
but it just opened the application window...
2022-09-06 09:01:34 +01:00
Luke Usher 972495df89 gba: fix eeprom saves for real this time 2022-09-05 23:46:59 +01:00
Luke Usher 3366b1dc6c mia: if no size is specified in the manifest, use on-disk size 2022-09-05 23:46:30 +01:00
Luke Usher 2c86419dd8 Revert "gba: fix eeprom saves (no longer zero bytes)"
This reverts commit d5c094e9e8.
2022-09-05 22:40:41 +01:00
Luke Usher 0e30732950 Revert "gba: eeprom size must only come from manifest or auto-detect, not file size"
This reverts commit f0598aaeb3.
2022-09-05 22:40:41 +01:00
Luke Usher 0fb5569c6b Revert "gba: don't re-create the eeprom array: it's already large enough"
This reverts commit f289cdd08a.
2022-09-05 22:40:40 +01:00
Luke Usher f289cdd08a gba: don't re-create the eeprom array: it's already large enough 2022-09-05 22:19:40 +01:00
Luke Usher f0598aaeb3 gba: eeprom size must only come from manifest or auto-detect, not file size
fixes #717
2022-09-05 19:46:02 +01:00
Luke Usher 1a498d64c9 ci: remove broken subtree split workflow 2022-09-05 15:48:39 +01:00
Luke Usher c2f58d1c79 Update split-subtrees.yml 2022-09-05 15:43:27 +01:00
invertego 3d826f5b26 nall: always pass by reference to make_string()
Many specializations of stringify store a reference to the wrapped
value. Therefore, passing arguments to make_string() by value can be
disastrous if the compiler does not perform copy elision and stringify
stores a reference to a variable that goes out of scope when
make_string() returns.
2022-09-05 14:20:55 +01:00
invertego 2bb95a13cb build: detect when gcc is an alias for clang
Detect build environments where 'g++' is just an alias for 'clang++'.
Example: MSYS2's Clang64 environment. This avoids the need to manually
override compiler detection.
2022-09-05 09:04:39 +01:00
invertego 1de21e178c build: pass through target from top-level makefile 2022-09-05 09:04:11 +01:00
Raphael Assenat cde0e1603d ms: Implement megadrive 3 and 6 buttons pads
Master system ports are compatible with megadrive controllers and
those can be used directly to play many original master system
games.

However MD controllers can be fully supported (i.e all buttons
working) if the game software is programmed to do so.

While really only useful on real hardware, there are game patches to
enable the Start button on MD controllers. But it's also possible
to make patches that use additional buttons or code full support
for Megadrive controllers in a homebrew title.
2022-09-04 10:44:34 +01:00
invertego 6fe063815c n64, ps1: stop video thread immediately on unload
The N64 and PS1 cores set custom Screen:refresh() callbacks that run on
the video thread and have meaningful dependencies on core state. It's
not safe to unload components from beneath them, so the video thread
should be stopped before any unloading occurs.
2022-09-04 10:44:03 +01:00
invertego b527a33b85 nall: make posix impl of thread::join idempotent
ares uses nall::thread with the assumption that calling join() multiple
times on the same thread will have no harmful effects. This was true of
the Windows implementation but not of the POSIX one.
2022-09-04 08:55:44 +01:00
Raphael Assenat 29a9c53a62 ms: Fix writes to controller port 2
Corrects an error in the original pull request which sent incorrect
data to port 2. The Sports Pad now also works in port 2.
2022-09-04 08:55:00 +01:00
CasualPokePlayer 56fe0d8269 Correct types for screen.hpp
Fixes some memory leaks due to delete being used instead of delete[]
2022-09-03 23:47:11 +01:00
invertego dc28723d52 nall: fix assignment operators
Almost all of the assignment operators in nall leaked resources, failed
to handle self assignment, or both.
2022-09-03 12:32:01 +01:00
Luke Usher 36af497fdf nall: add support for ccache
Use ccache ccache=true is passed to make
See https://ccache.dev/ for further details.

On msys/mingw, the ccache package is `mingw-w64-x86_64-ccache`
2022-09-02 22:56:48 +01:00
Luke Usher d5c094e9e8 gba: fix eeprom saves (no longer zero bytes)
mia cannot save a file when the manifest size is zero.
instead, use the largest known eeprom size + 1 as a default.

The +1 signifies that we should auto-detect the real size of the eeprom
and the large size makes sure mia creates a file on disk large enough.

For games without a database entry, the eeprom size be larger on disk
than is actually used, but it's better than completely broken saves.

Note that even with this, some games will require database entries,
but most should work now.
2022-09-02 22:38:36 +01:00
Luke Usher 6adc70cbc8 input: prevent polling more than necessary
This prevents slowdown in games that poll games multiple times per-frame.
5ms was chosen as ares already has a 5ms limit higher up the chain.

For me, Advanced Wars 2 (GBA) jumped from 50-55fps to 65-68 with this change
2022-09-02 20:51:03 +01:00
invertego 2e385844d0 sfc: fix performance ppu override detection 2022-09-02 00:03:14 +01:00
invertego 7e48079a46 pce: performance vdp timing overrides
Following the precedent set by the SFC performance PPU, override the
timing of the PCE performance VDP for a handful of problematic games.

All of these games already work fine with the accuracy VDP (enabled with
the "pixel accuracy mode" video setting) which does not and never will
contain contain game specific logic.

Affected games:
- Kore ga Pro Yakyuu '89
- Kore ga Pro Yakyuu '90
- TV Sports Baseball
- TV Sports Football
- TV Sports Hockey
- Valkyrie no Densetsu
- Victory Run
2022-09-01 16:16:56 +01:00
Giovanni Bajo f9eb9304f0 README: simplify build instructions 2022-09-01 08:13:48 +01:00
Giovanni Bajo c3eb8bc49e n64: improve exceptions for invalid instructions in cop1/cop2
Also implement MFC2/MTC2 and friends which seems to exist at some
level (though they just access a latch).

Fixes new set of tests in n64-systemtest.
2022-09-01 07:39:55 +01:00
invertego 8759a7d3e8 n64: precise rsp block invalidation
- The primary lookup table is direct addressed
- The secondary lookup table is indexed by hash of code block contents
- IMEM invalidation is tracked at 64-byte granularity and used to
  evict primary lookup table entries
- The secondary lookup table is only pruned on a full code cache flush
- Only executed instructions are hashed, so the code cache no longer
  experiences unbounded growth
- Code blocks can now wrap around the end of IMEM
- generic::recompiler now supports aborting recompilation (required by
  by an earlier version of this change)
2022-08-30 09:28:37 +01:00
Raphael Assenat a785ce924b msx: auto-connect the Arkanoid Vaus Paddle 2022-08-30 09:17:40 +01:00
Raphael Assenat 938b1647b0 msx: Implement the Arkanoid Vaus Paddle
This adds support for the Taito Arkanoid Vaus Paddle.
2022-08-30 09:17:40 +01:00
Raphael Assenat 6abe6bfb19 msx: Implement joystick port 2
On MSX, both joysticks are read through a common port (PSG port 14).
Which joystick is being accessed is selected through bit 6 in PSG
port 15. In other words, the ports inputs are multiplexed.

This pull request adds a simple concept of controller multiplexer
which the PSG IO code can call whenever port 14 is read.

PSG port 15 also controls the level and direction of some joystick
pins, but those are not multiplexed. This pull request corrects how
writes to port 15 are handled and organizes output bits in a predefined
order so controller implementation do not need to know which port they
are connected to. This paves the way for more advanced peripherals
controlled through those outputs, such as the Taito Arkanoid Vaus Paddle.
2022-08-30 09:17:21 +01:00
Raphael Assenat d22fae0cae ay38910: Keep a copy of the last data written to IO ports 2022-08-30 09:17:21 +01:00
Luke Usher 4effa2b6d2 ms: auto-connect sports pad for games that require it 2022-08-29 23:28:10 +01:00
Luke Usher e6dfd5ee74 ms: auto-connect paddle controller for the games that require it 2022-08-29 23:00:35 +01:00
Raphael Assenat a5966490c9 ms: Add support for the Sports Pad
This adds support for the Sports Pad, a trackball controller for Master System. Input
is done using a Mouse or a trackball (best).

Tested with Sports Pad Soccer and a homebrew test ROM.
2022-08-29 22:08:11 +01:00
Raphael Assenat f5f75cdeef ms: Expose output signals to controller port
The write method is now actually called when TR/TH level and direction
IO port is written to. Also redefined the write method bit width (4) and
document which bit is used for what.

This will allow emulation of controllers which uses the TH line as a
control output. This includes the Export Paddle, the Sports Pad, and
Megadrive controllers.
2022-08-29 22:08:11 +01:00
Raphael Assenat a6209af97b ruby: xlib mouse double event workaround
When the mouse is grabbed, the current implementation hides the
pointer and centers it on the screen.

When the mouse is polled, its relative motion is determined by how far it
has moved from the center of the screen, then it is moved back to the
center by calling XWarpPointer().

XWarpPointer generates an event resulting in an additional poll, which then
sees no movement at all (since the pointer is now centered) and therefore
resets the relative motion values to zero.

If the above reset occurs before the controller code has had the chance to
access the relative motion values, the motion never reach the game. This
works around this issue using a flag to ignore the next event if the
mouse is still centered on the screen.
2022-08-29 22:07:06 +01:00
Luke Usher 93e24926ae mia: add partial iNES 2.0 support
just enough to detect additional mapper types and games with additional CHR RAM for now.
2022-08-27 14:04:04 +01:00
Luke Usher 6f3997dc38 ms: fix an issue where the GG bios failed to detect SMS mode. 2022-08-25 23:17:52 +01:00
Luke Usher b380834c0e ms: fix Game Gear bios support (bios is optional) 2022-08-25 23:00:32 +01:00
Luke Usher 9e551be17a ps1: improve gamepad behavior when receiving invalid commands 2022-08-25 21:47:31 +01:00
Luke Usher d77c6f0788 ms: various vdp improvements
1. Nametable address is not a latched register, it takes effect immediately
2. Sprites should still be processed & overflow is still set during display disable
3. Hcounter -94 offset is incorrect for ares, as we already have clock 0 = pixel 0.
2022-08-25 14:16:38 +01:00
Luke Usher a1df486779 ms: fully decode port $3e on GameGear + allow $3f writes (latches h-counter)
Despite the Game Gear having no physical controller ports, writes to the IO control register still need to be emulated, and thus the ports need to be present in ares, such that latching h-counter on 0-1 transition is still possible.
2022-08-25 09:51:06 +01:00
Luke Usher cdedb9b0f5 sn76489: fix additional data writes to noise channel
fixes broken audio in Micro Machines on the Master System
2022-08-24 23:44:23 +01:00
Luke Usher 9ff41ed45a ms: initialize ram to zero
Fixes hang in Shanghai II (Japan) for Game Gear
2022-08-24 22:01:32 +01:00
Luke Usher 56aa5dac0d ms: fix precision loss when latching hcounter
Internal Hcounter values (actually clock cycles) are larger than the range of a u8.

The actual Master System h-counter (0x00-0xff range) is calculated later.
2022-08-24 16:36:07 +01:00
Luke Usher ebde92c891 mia: add database entry for Chase H.Q (USA)
This is another Game Gear release that targets SMS mode.
2022-08-24 16:21:39 +01:00
Luke Usher 7dc55eeb54 mia: add database entry for Taito Chase H.Q (Japan)
This is another Game Gear release that targets SMS mode.
2022-08-24 14:10:01 +01:00
Luke Usher ec4e0050e7 sms: fix a crash when using a Game Gear in SMS mode. 2022-08-23 20:25:15 +01:00
Luke Usher 306d23f38b mia: game-gear doesn't support PAL/50hz mode, use NTSC-U instead 2022-08-23 16:49:50 +01:00
Luke Usher fa3dfa38cd ps1: [SPU] increase precision of volume input to amplify function
fixes pitch modulation, especially obvious in J-E-N-O-V-A from FF7.
2022-08-22 23:36:56 +01:00
Luke Usher a0601f1c79 Update issue templates 2022-08-22 15:29:37 +01:00
Luke Usher 19b56bc118 Update issue templates 2022-08-22 15:28:14 +01:00
Luke Usher 2f12a72cad Update issue templates 2022-08-22 15:26:23 +01:00
Luke Usher ed0fe5599f desktop-ui: allow hotkeys when unfocused if "allow input while focus is lost" is enabled 2022-08-20 21:33:26 +01:00
invertego 081408e961 md: latch bus request on reset 2022-08-20 19:44:10 +01:00
Luke Usher 56d6488b3d 6502: add some undocumented opcode variants
fixes Aladdin and Super Cards on nes/fc
2022-08-20 00:05:55 +01:00
Luke Usher 85f1fb6b7f gb: more DMG mode fixes;
fixes titlescreen in Pokemon Red/Blue/Yellow on GBC in DMG mode.
2022-08-18 22:40:57 +01:00
Luke Usher 34d6d465c4 gb: handle differences with bgEnable in DMG mode on CGB 2022-08-18 22:40:56 +01:00
Parashoe fcef806553 n64 setting: "Disable Video Interface Processing" 2022-08-18 13:54:49 +01:00
Luke Usher 61258a7063 gb: fix an oops in KEY0 2022-08-17 11:35:48 +01:00
Luke Usher b1d216b8af gb: allow CGB to run in DMG mode
This is not fully hardware accurate yet (lots of CGB only registers remain enabled)
but most of the rendering issues should be resolved now.
2022-08-17 11:16:22 +01:00
Luke Usher c53023e6c8 gb: fixes to mbc2
1. ram is mirrored between 0xa000-0xbfff, not 0xa000-0xa1ff
2. ram & bank registers are mapped through 0x0000-0x3fff, using only bit.8 to select between them
2022-08-17 00:23:00 +01:00
Luke Usher 637aa19a72 gb: display is disabled while stopped 2022-08-17 00:10:43 +01:00
Luke Usher 2cf4d8cac8 gb: don't increment div when stopped 2022-08-17 00:10:29 +01:00
Luke Usher 0071e8a7d4 gb: fix div initial value (validated via mooneye tests) 2022-08-17 00:10:15 +01:00
Luke Usher d546f147c3 hiro: remove DirectDraw backend
DirectDraw has not been updated by Microsoft since DirectX7.
It has been deprecated/not recommended for use since DirectX8, released in 2000.
2022-08-16 22:01:05 +01:00
Luke Usher b03a942bf4 desktop-ui: simplify some code paths by removing lesser-used functions
1. Grouping systems by vendor in the system menu can no longer be disabled.
2. It is no longer possible to hide emulator cores from the menu
3. It is no longer possible to use the higan file select dialog; only the native OS file picker is available.
2022-08-16 21:57:29 +01:00
Raphael Assenat 23416eae32 Add support for the Japanese HPD-200 Paddle
This makes games requiring a Paddle, such as Megumi Rescue,
playable with an analog stick, or using adapters USB adapters
supporting the HPD-200 Paddle.

"Prefer Japan" should be selected in tools->Boot Options before load
otherwise the game will expect the "Export Paddle" which is not
compatible.
2022-08-16 10:30:47 +01:00
invertego 2ade0e39de m68000: fix memory read order for A/SBCD, ADD/SUBX
This is especially important when both operands reference the same
register, as the first predecrement should affect the second read.
2022-08-15 13:11:26 +01:00
Chris Jalbert 62930f8da8 accidentally excluded two important components - fixed 2022-08-14 18:21:33 +01:00
Tobias Jakobi be1f424398 build: use pkg-config in ruby Makefile
Checking for libraries in /usr/lib does not work reliably, e.g.
on a x86-64 Gentoo the 64-bit libraries are located in /usr/lib64.

Use pkg-config to detect libraries, which should work much better
on any modern Linux distro and also covers Debian's multi-arch
setup.
2022-08-14 17:21:18 +01:00
invertego 003afbbb58 m68000: fix divs/divu negative flag 2022-08-14 16:58:39 +01:00
Chris Jalbert 3010c8a8c9 Issue 521: Basic Build Instructions 2022-08-14 16:58:28 +01:00
invertego 4a935893d2 md: preserve bank register on apu reset 2022-08-14 16:56:25 +01:00
Giovanni Bajo 32096b8e85 n64: use Neon instructions also in mame RDP 2022-08-11 22:23:43 +01:00
Luke Usher 3142bd3aef n64: change rsp.ipu.pc to u16 from u12
Using a native data type significantly improves performance of ares::Nintendo64::RSP::instructionEpilogue, making it jump from ~9-10% of cpu time to 5-6% of cpu time during execution
2022-08-06 23:56:51 +01:00
Luke Usher 9985c693bd n64: use xxh3 instead of crc32 for rsp cache
This is a significantly faster and stronger algorithm than crc32
2022-08-06 23:37:08 +01:00
Luke Usher 81e15695c8 n64: a little more performance tuning 2022-08-06 17:38:48 +01:00
Luke Usher f486867292 n64: move some function implementations from icache.cpp to cpu.hpp
Identified via profiling: this improves performance due to better compiler optimisation.
2022-08-06 17:38:48 +01:00
Giovanni Bajo ffa7040b6f n64: use sse2neon to emulate RSP with SIMD instructions on ARM64 2022-08-06 14:43:05 +01:00
Giovanni Bajo 247a269054 thirdparty: vendor sse2neon.h (a951d8760) 2022-08-06 14:43:05 +01:00
invertego 50b2a8f3ab mia: fix 32x eeprom 2022-08-05 09:27:59 +01:00
Luke Usher f1e36f2775 n64: split rsp recompiler cache into 256-byte sized blocks
Significantly increases the cache hit ratio due to games utilising multiple small ucode programs.
2022-08-04 20:14:36 +01:00
FitzRoyX 997fda03fe md: add sram entry for psy-o-blade 2022-08-04 15:52:19 +01:00
Luke Usher c6ecbe81b2 n64: restore old recompiler invalidate behavior 2022-08-04 09:13:49 +01:00
Luke Usher 9c9ef927d7 desktop-ui: re-order systems by vendor in the menu 2022-08-02 23:04:07 +01:00
Luke Usher 029ae18514 Squashed 'thirdparty/libchdr/' changes from 045f2a7f23..b3c1651d0f
b3c1651d0f Merge pull request #80 from scribam/vita
e802754335 vita: fix core_fseek/core_ftell

git-subtree-dir: thirdparty/libchdr
git-subtree-split: b3c1651d0fee6543968d916cb4e39c9b184c231f
2022-08-02 20:53:54 +01:00
Luke Usher dcfce18d7f Merge commit '029ae18514c23e538f79126326e4244e79f648ff' 2022-08-02 20:53:54 +01:00
Luke Usher 295c729ecf Merge commit '43e0dd770d5e32ec2b4794a19281e2aa22f45bd1' 2022-08-02 20:52:59 +01:00
Luke Usher 43e0dd770d Squashed 'ares/n64/vulkan/parallel-rdp/' changes from 808a89d23a..428884deab
428884deab Update to e6cf0e5ca257135acbcacfbdacd7cd3fdf3fb408
6879314041 Update to 9dacdc9d73c5febda2448fec9b393034fa5d9fe3

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: 428884deab9571cc04be756960e3649f678c8f63
2022-08-02 20:52:59 +01:00
Luke Usher 6e9c08b491 n64: fix an issue where invalidateRange was called with incorrect inputs 2022-08-02 19:50:11 +01:00
Luke Usher 1808e3f50c n64: make the recompiler invalidate single blocks rather than entire pools 2022-08-02 09:40:25 +01:00
Luke Usher 8f39e06900 Squashed 'thirdparty/libchdr/' changes from 6eeb6abc4..045f2a7f2
045f2a7f2 Merge pull request #74 from 2weak2live/raw
e23f32f37 Add support for reading LZMA and FLAC hunks in non-CD CHDs.

git-subtree-dir: thirdparty/libchdr
git-subtree-split: 045f2a7f23f5ac10c0122f6f56e42023dfb71e2a
2022-08-01 16:12:29 +01:00
Luke Usher 7742b49289 Merge commit '8f39e06900d298526f86b9c808520bd6236d4475' 2022-08-01 16:12:29 +01:00
Luke Usher 4750a7f224 Squashed 'thirdparty/sljit/' changes from 761d48d2b..7c37b7ebc
7c37b7ebc Rework x86-32 stack layout
09a58361f Improve sljit abi calling convention
58e36c06a Change skip checks to a macro
ac445a3ef Add sljit specific register argument calling convention
a6c8e5bf7 Remove fast call support on x86-32
a3785b330 Fix x86 mem address encoding
2f8e5c2f2 Implement ARM unaligned support
3e86c50e9 Fix PPC logical or/xor with flag set
d8af3f91b Remove sparc support
3055f116b Fix test50 alignment
41cb888d8 Start supporting unaligned memory access

git-subtree-dir: thirdparty/sljit
git-subtree-split: 7c37b7ebcf17a80f606100ae88e744dff22befe6
2022-08-01 16:12:20 +01:00
Luke Usher ff78815e90 Merge commit '4750a7f224641df4c98038c32a50fb92e8facc11' 2022-08-01 16:12:20 +01:00
Luke Usher 00a5c6864e Squashed 'ares/n64/vulkan/parallel-rdp/' changes from af9b4b00f..808a89d23
808a89d23 Update to f187ecba0df48e3185ac61e5516c34f903c7a095
8a2986a64 Update to a691960e8d4f72fea6a384fd89881cbdb8a78a0b
31e97d2f2 Update to 4137ff0771ca473b7610a1c8ea307d7a5141aee4

git-subtree-dir: ares/n64/vulkan/parallel-rdp
git-subtree-split: 808a89d23ae193e003b3e54de61a8172582168d1
2022-08-01 16:12:13 +01:00
Luke Usher a38572199d Merge commit '00a5c6864e20cd9c3a556e477abd2eb063c750a2' 2022-08-01 16:12:13 +01:00
Giovanni Bajo bcd501edf6 n64: fix AI DMA enable bit handling
Fix again Twisted Edge, that was recently broken when we reworked AI DMA
2022-08-01 09:05:40 +01:00
Giovanni Bajo 8d1ad79fdb n64: refactor AI::sample to always return a sample 2022-08-01 09:05:40 +01:00
invertego b2db7f16f6 ng: clean up screen thread on unload 2022-08-01 09:05:05 +01:00
Luke Usher 080b53a652 Revert "desktop-ui: optimize for generic x86-64-v2 rather than nehalem"
because Ubuntu 20.04 still has outdated GCC that doesn't support this flag;
people who get a performance hit from optimising for nehalem will just need to
self compile.

This reverts commit f7657dca4f.
2022-07-31 23:31:38 +01:00
Luke Usher 2b9fcab30f ci/desktop-ui: disable lto by default
The added compile time is not worth it when for many users this harms performance, rather than improving it.

TODO: Why?
2022-07-31 22:11:42 +01:00
Luke Usher 5565d2def4 attempt to fix ci builds for macOS 2022-07-31 11:27:09 +01:00
Luke Usher 9aa05bcbe3 nall: more LTO tuning 2022-07-30 19:26:51 +01:00
Simon Eriksson e0434ca82b n64: Add missing variables in RDP serialization 2022-07-29 23:53:52 +01:00
Luke Usher 9c5ecf52ce nall: tweak LTO settings for better performance in compiled builds
GameBoy core +10 FPS on average in my testing
2022-07-29 23:19:03 +01:00
Luke Usher 6d890244c1 desktop-ui: enable lto by default but allow it to be disabled with lto=false 2022-07-29 23:18:18 +01:00
Luke Usher f7657dca4f desktop-ui: optimize for generic x86-64-v2 rather than nehalem 2022-07-29 23:03:33 +01:00
Luke Usher eda2918f58 ng: fix games with non-power-of-two croms 2022-07-28 23:33:48 +01:00
Luke Usher d75c13107c ng: avoid syncing with the YM2610 more than necessary 2022-07-27 22:10:35 +01:00
Luke Usher 1a5984be70 ng: preliminary support for ADPCM-A channel 2022-07-27 14:53:39 +01:00
FitzRoyX e1711eb28a sfc: fix typo that broke bg4 2022-07-27 09:20:41 +01:00
Giovanni Bajo 1a564fa93e n64: fix AI IRQ
The AI IRQ should be generated when a buffer starts playing, not when
it finishes playing, as verified by hardware tests.

Fixes the audio in Hey Pikachu and possibly other games.
2022-07-26 22:22:15 +01:00
Luke Usher 6118facaf2 ng: refactor rom loading
Fixes Twinkle Star Sprites + possibly others
2022-07-23 22:34:39 +01:00
Luke Usher d52a3b9dfc ng: fix loading of roms that use .ep instead of .p for program roms 2022-07-22 23:16:26 +01:00
Luke Usher 38495197d2 ng: remove leftover debug logging 2022-07-22 23:09:44 +01:00
Luke Usher dd6a448bc4 ng: fix FM/SSG audio playback
ADPCM playback is still unsupported, so a lot of audio will be missing.
2022-07-22 22:56:57 +01:00
Luke Usher 1cea9ad1d9 mia: cleanup old famicom-disk source file 2022-07-22 20:02:56 +01:00
remutro 14c9dc04fd Rename all instances of Famicom Disk to Famicom Disk System 2022-07-22 14:35:15 +01:00
remutro 9e983e720f match emulator name with expected mia media definition to fix auto-detection of fds 2022-07-22 14:35:15 +01:00
Luke Usher b2dd52c0a4 ps1: fix a crash when using out of range vram co-ordinates 2022-07-21 22:24:36 +01:00
Luke Usher 9a3d949fc2 ms: implement support for NTSC-U/NTSC-J Game Gear variants 2022-07-21 21:43:46 +01:00
invertego 00819b70c0 a26: fix buffer overflows 2022-07-18 01:31:27 +01:00
Ralakimus cd83a939e8 32X: Implement preliminary Sega/SSF2 mapper support (see #628) 2022-07-15 13:51:32 +01:00
599 changed files with 143170 additions and 116926 deletions

No files matched your search

+27
View File
@@ -0,0 +1,27 @@
---
name: Bug report
about: Report a bug *not* relating to game compatibility
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,21 @@
---
name: Game Issue Report
about: Report an issue running a specific game in ares
title: "[system] Game"
labels: bug
assignees: ''
---
Before submitting an issue, please use the search to make sure an issue doesn't already exist.
**Describe the issue**
A clear and concise description of what the bug is.
**Rom Information**
Specify the impacted rom filename and any associated hashes (sha256 preferred)
```
file: Some Game (REGION).ext
sha256: <hash>
```
+56 -8
View File
@@ -13,12 +13,6 @@ jobs:
- ares
platform:
- name: windows
os: windows-latest
compiler: g++
shell: 'msys2 {0}'
msystem: mingw64
msys-env: x86_64
- name: windows-clang
os: windows-latest
compiler: clang++
shell: 'msys2 {0}'
@@ -49,9 +43,63 @@ jobs:
run: |
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgtksourceview2.0-dev libgtk2.0-dev libao-dev libopenal-dev
- uses: actions/checkout@v2
- name: "macOS: Import Certificate"
if: runner.os == 'macOS'
uses: devbotsxyz/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 }}
keychain-name: ares-macos-keychain
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
- name: Checkout source code
uses: actions/checkout@v2
- name: "macOS: recover cache"
if: runner.os == 'macOS'
uses: actions/cache@v3
env:
cache-name: moltenvk
with:
path: thirdparty/MoltenVK/MoltenVK
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
- name: Install macOS Dependencies
if: runner.os == 'macOS'
run: |
brew install make ninja
echo "MAKE=gmake" >> $GITHUB_ENV
pushd thirdparty/MoltenVK
./build-moltenvk.sh
popd
- name: Make
run: make -j4 -C desktop-ui build=optimized local=false lto=true compiler=${{ matrix.platform.compiler }}
if: runner.os != 'macOS'
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler=${{ matrix.platform.compiler }}
- name: "macOS: Make universal app"
if: runner.os == 'macOS'
run: scripts/macos-make-universal.sh
env:
MAKEFLAGS: -j3
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
MACOS_KEYCHAIN_NAME: ares-macos-keychain
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
- name: "Windows: sign"
if: runner.os == 'Windows'
uses: DanaBear/code-sign-action@98c79121b376beab8d6a9484f445089db4461bca
with:
certificate: '${{ secrets.WIN_CERTIFICATE }}'
password: '${{ secrets.WIN_PASSWORD }}'
certificatesha1: '${{ secrets.WIN_CERTHASH }}'
certificatename: '${{ secrets.WIN_CERTNAME }}'
folder: 'desktop-ui/out'
timestampUrl: 'http://timestamp.digicert.com'
- name: "macOS: notarize"
if: runner.os == 'macOS'
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
xcrun stapler staple desktop-ui/out/ares.app
env:
MACOS_NOTARIZATION_USERNAME: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
MACOS_NOTARIZATION_TEAMID: ${{ secrets.MACOS_NOTARIZATION_TEAMID }}
- name: Upload
uses: actions/upload-artifact@v2
with:
-30
View File
@@ -1,30 +0,0 @@
name: 'Split Subtrees'
on:
push:
branches:
- master
tags:
- '*'
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
jobs:
packages_split:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package:
- nall
- hiro
- ruby
- libco
steps:
- uses: actions/checkout@v2
- if: "!startsWith(github.ref, 'refs/tags/')"
uses: "symplify/[email protected]"
with:
package_directory: '${{ matrix.package }}'
repository_organization: 'ares-emulator'
repository_name: '${{ matrix.package }}'
user_name: "Luke Usher"
user_email: "[email protected]"
+4
View File
@@ -1,2 +1,6 @@
obj/
out/
obj-amd64/
obj-arm64/
out-amd64/
out-arm64/
+4 -4
View File
@@ -1,6 +1,6 @@
all: desktop
targets = all clean install uninstall verbose
desktop:
$(MAKE) -C desktop-ui
$(targets):
@$(MAKE) -C desktop-ui $@
.PHONY: all desktop
.PHONY: $(targets)
+244 -1
View File
@@ -145,4 +145,247 @@ Gailly and Mark Adler; it does not include third-party code.
If you redistribute modified sources, we would appreciate that you include in
the file ChangeLog history information documenting your changes. Please read
the FAQ for more information on the distribution of modified source versions.
----------------------------------------------------------------------
----------------------------------------------------------------------
----------------------------------------------------------------------
xxHash - Extremely Fast Hash algorithm
Header File
Copyright (C) 2012-2021 Yann Collet
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
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
OWNER 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.
You can contact the author at:
- xxHash homepage: https://www.xxhash.com
- xxHash source repository: https://github.com/Cyan4973/xxHash
----------------------------------------------------------------------
MoltenVK
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----------------------------------------------------------------------
+73 -1
View File
@@ -1,4 +1,4 @@
![ares logo](https://ares-emulator.github.io/images/logo.png)
![ares logo](https://github.com/ares-emulator/ares/blob/master/ares/ares/resource/logo.png)
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/higan-emu/ares/blob/master/LICENSE)
@@ -16,3 +16,75 @@ Nightly Builds
Automated, untested builds of ares are available for Windows and macOS as a [pre-release](https://github.com/higan-emu/ares/releases/tag/nightly).
Only the latest nightly build is kept.
Prerequisites
-------------
#### *nix building
g++ make pkg-config libgtk2.0-dev libcanberra-gtk-module libgl-dev libasound2-dev
By default, GTK2 is used, but support for GTK3 is available. You will need to install the additional package `libgtk-3-dev` as well
as specifying the command line option `hiro=gtk3` at compile time.
#### Windows building
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` 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.
Compilation
-----------
Check out the source code by running this command:
git clone https://github.com/ares-emulator/ares.git
From the root of the project directory run:
make -j4 build=release
that builds with build type of type 'release'.
`-j4` indicates number of parallel build processes, and shouldn't be set higher than N-1 cores on your processor. Specifying this option can significantly decrease the time to build this project. There are multiple build types available (debug, etc.). Most additional options can be
found in nall's make file (nall/GNUmakefile).
To start compilation from the beginning, run the following prior to compiling:
make clean
Build Output
------------
There is a single binary produced at the end of compilation which can be found in `desktop-ui/out`. On OS's besides Linux, the `Database` & `Shader` directories are copied over here as well. On Linux, running `make install` after compilation will copy these directories and binary into suitable locations (see desktop-ui/GNUmakefile for details). Alternatively, these directories can be copied from `ares/Shaders/*` and `mia/Database/*`.
Command-line options
--------------------
When started from the command-line, ares accepts a few options.
```
Usage: ./ares [options] game
--help Displays available options and exit
--fullscreen Start in full screen mode
--system system Specify the system name
```
The --system option is useful when the system type cannot be auto-detected.
--fullscreen will only have an effect if a game is also passed in argument.
Example:
`ares --system MSX examples.rom --fullscreen`
High-level Components
---------------------
* __ares__: emulator cores and component implementations
* __desktop-ui__: main GUI implementation for this project
* __hiro__: cross-platform GUI toolkit that utilizes native APIs on supported platforms
* __nall__: Near's alternative to the C++ standard library
* __ruby__: interface between a hiro application and platform-specific APIs for emulator video, audio, and input
* __mia__: internal ROM database and ROM/image loader
* __libco__: cooperative multithreading library
+4
View File
@@ -15,6 +15,10 @@ 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
+2 -2
View File
@@ -16,7 +16,7 @@ auto TIA::load(Node::Object parent) -> void {
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
screen->colors(1 << 7, {&TIA::color, this});
screen->setSize(160, vlines());
screen->setScale(2.0, 1.0);
screen->setScale(4.0, 2.0);
screen->setAspect(1.0, 1.0);
screen->setViewport(0, 0, 160, vlines());
@@ -44,7 +44,7 @@ auto TIA::scanline() -> void {
cpu.rdyLine(1);
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
auto x = io.hcounter - 68;
if(x >= 0) pixel(x);
if(x >= 0 && io.vcounter < vlines()) pixel(x);
writeQueue.step();
runAudio();
step();
+2 -2
View File
@@ -38,7 +38,7 @@ using namespace nall;
namespace ares {
static const string Name = "ares";
static const string Version = "129";
static const string Version = "130";
static const string Copyright = "ares team, Near";
static const string License = "ISC";
static const string LicenseURI = "https://opensource.org/licenses/ISC";
@@ -47,7 +47,7 @@ namespace ares {
//incremented only when serialization format changes
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
static const string SerializerVersion = "129";
static const string SerializerVersion = "130";
namespace VFS {
using Pak = shared_pointer<vfs::directory>;
+4 -4
View File
@@ -4,19 +4,19 @@ struct Debug {
auto reset() -> void;
template<typename... P> auto unhandled(P&&... p) -> void {
return _unhandled({forward<P>(p)...});
return _unhandled({std::forward<P>(p)...});
}
template<typename... P> auto unimplemented(P&&... p) -> void {
return _unimplemented({forward<P>(p)...});
return _unimplemented({std::forward<P>(p)...});
}
template<typename... P> auto unusual(P&&... p) -> void {
return _unusual({forward<P>(p)...});
return _unusual({std::forward<P>(p)...});
}
template<typename... P> auto unverified(P&&... p) -> void {
return _unverified({forward<P>(p)...});
return _unverified({std::forward<P>(p)...});
}
private:
+1 -1
View File
@@ -25,7 +25,7 @@ struct Stream : Audio {
template<typename... P>
auto frame(P&&... p) -> void {
if(runAhead()) return;
f64 samples[sizeof...(p)] = {forward<P>(p)...};
f64 samples[sizeof...(p)] = {std::forward<P>(p)...};
write(samples);
}
+1
View File
@@ -1,4 +1,5 @@
struct Input : Object {
DeclareClass(Input, "input")
using Object::Object;
u64 lastPoll;
};
+2 -2
View File
@@ -33,7 +33,7 @@ struct Object : shared_pointer_this<Object> {
template<typename T, typename... P>
auto prepend(P&&... p) -> Node::Object {
using Type = typename T::type;
return prepend(shared_pointer<Type>::create(forward<P>(p)...));
return prepend(shared_pointer<Type>::create(std::forward<P>(p)...));
}
auto append(Node::Object node) -> Node::Object {
@@ -47,7 +47,7 @@ struct Object : shared_pointer_this<Object> {
template<typename T, typename... P>
auto append(P&&... p) -> Node::Object {
using Type = typename T::type;
return append(shared_pointer<Type>::create(forward<P>(p)...));
return append(shared_pointer<Type>::create(std::forward<P>(p)...));
}
auto remove(Node::Object node) -> void {
+4 -4
View File
@@ -82,10 +82,10 @@ protected:
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
function<n64 (n32)> _color;
unique_pointer<u32> _inputA;
unique_pointer<u32> _inputB;
unique_pointer<u32> _output;
unique_pointer<u32> _rotate;
unique_pointer<u32[]> _inputA;
unique_pointer<u32[]> _inputB;
unique_pointer<u32[]> _output;
unique_pointer<u32[]> _rotate;
unique_pointer<u32[]> _palette;
vector<Node::Video::Sprite> _sprites;
+1 -1
View File
@@ -92,7 +92,7 @@ inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
co_switch(thread.handle());
}
//convenience: allow synchronizing multiple threads with one function call.
if constexpr(sizeof...(p) > 0) synchronize(forward<P>(p)...);
if constexpr(sizeof...(p) > 0) synchronize(std::forward<P>(p)...);
}
inline auto Thread::serialize(serializer& s) -> void {
+2
View File
@@ -196,9 +196,11 @@ auto AY38910::write(n8 data) -> void {
envelope.output = !envelope.attacking ? 15 : 0;
break;
case 14:
portA.data = data;
writeIO(0, data);
break;
case 15:
portB.data = data;
writeIO(1, data);
break;
}
+4
View File
@@ -63,6 +63,10 @@ auto SN76489::write(n8 data) -> void {
case 0: tone0.pitch.bit(4,9) = data.bit(0,5); break;
case 1: tone1.pitch.bit(4,9) = data.bit(0,5); break;
case 2: tone2.pitch.bit(4,9) = data.bit(0,5); noise.pitch = tone2.pitch; break;
case 3: noise.rate = data.bit(0,1);
noise.enable = data.bit(2);
noise.lfsr = 0x8000;
break;
}
}
}
+53 -16
View File
@@ -1,47 +1,84 @@
auto YM2610::read(n2 address) -> n8 {
switch(address) {
case 0: return fm.readStatus();
case 1: return ssg.read();
}
debug(unimplemented, "[YM2610] read ", hex(address));
return 0;
}
auto YM2610::write(n2 address, n8 data) -> void {
switch(address) {
case 0: register = 0x000 | data; return;
case 0: {
registerAddress = 0x000 | data;
if (registerAddress <= 0xf) ssg.select(registerAddress);
else fm.writeAddress(registerAddress);
return;
}
case 1: return writeLower(data);
case 2: register = 0x100 | data; return;
case 2: registerAddress = 0x100 | data; fm.writeAddress(registerAddress); return;
case 3: return writeUpper(data);
}
}
auto YM2610::writeLower(n8 data) -> void {
switch(register) {
switch(registerAddress) {
case 0x000 ... 0x00f:
ssg.select(register);
ssg.write(data);
return;
case 0x010 ... 0x01b:
//ADPCM-B
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 0x016 ... 0x018: debug(unimplemented, "[YM2610::ADPCMB] Write ", hex(registerAddress), " = ", data);
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:
//EOS
debug(unimplemented, "[YM2610::EOS] Write ", hex(registerAddress), " = ", data);
return;
case 0x01d ... 0x1ff:
fm.writeAddress(register);
fm.writeData(data);
return;
}
unreachable;
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
}
auto YM2610::writeUpper(n8 data) -> void {
switch(register) {
case 0x000 ... 0x12f:
//ADPCM-A
return;
case 0x130 ... 0x1ff:
fm.writeAddress(register);
fm.writeData(data);
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 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 0x110 ... 0x115: pcmA.channels[registerAddress - 0x110].startAddress.bit( 8, 15) = data; return;
case 0x118 ... 0x11d: pcmA.channels[registerAddress - 0x118].startAddress.bit(16, 23) = data; return;
case 0x120 ... 0x125: pcmA.channels[registerAddress - 0x120].endAddress.bit ( 8, 15) = data; return;
case 0x128 ... 0x12d: pcmA.channels[registerAddress - 0x128].endAddress.bit (16, 23) = data; return;
case 0x130 ... 0x1ff: fm.writeAddress(registerAddress); fm.writeData(data); return;
}
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
}
+149
View File
@@ -0,0 +1,149 @@
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;
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 {
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;
}
}
}
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 {
currentAddress = startAddress;
currentNibble = 0;
decodeAccumulator = 0;
previousAccumulator = 0;
decodePosition = 0;
decodeStep = 127;
playing = 1;
}
+37 -1
View File
@@ -1,5 +1,41 @@
auto YM2610::serialize(serializer& s) -> void {
s(register);
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].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(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);
}
+3
View File
@@ -4,11 +4,14 @@
namespace ares {
#include "io.cpp"
#include "pcm.cpp"
#include "serialization.cpp"
auto YM2610::power() -> void {
fm.power();
ssg.power();
pcmA.power();
pcmB.power();
}
}
+65 -1
View File
@@ -20,10 +20,74 @@ struct YM2610 {
//serialization.cpp
auto serialize(serializer&) -> void;
virtual auto readPCMA(u32 address) -> u8 { return 0; }
virtual auto readPCMB(u32 address) -> u8 { return 0; }
protected:
n9 register;
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 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 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};
};
}
+30 -29
View File
@@ -1,9 +1,6 @@
auto YM2612::Channel::Operator::trigger(bool state) -> void {
auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
if(keyOn == state) return; //no change
keyOn = state;
envelope.state = Release;
updateEnvelope();
if(keyOn) {
//restart phase and envelope generators
@@ -13,42 +10,46 @@ auto YM2612::Channel::Operator::trigger(bool state) -> void {
updateEnvelope();
if(envelope.rate >= 62) {
//skip attack and possibly decay stages
//skip attack phase
envelope.value = 0;
envelope.state = envelope.sustainLevel ? Decay : Sustain;
updateEnvelope();
}
} else if(ssg.enable && ssg.attack != ssg.invert) {
//SSG-EG key-off
envelope.value = 0x200 - envelope.value;
} else {
envelope.state = Release;
updateEnvelope();
if(ssg.enable && ssg.attack != ssg.invert) {
//SSG-EG key-off
envelope.value = 0x200 - envelope.value;
}
}
updateLevel();
}
auto YM2612::Channel::Operator::runEnvelope() -> void {
u32 sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x3f0;
updateKeyState(keyLine);
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
u32 value = ym2612.envelope.clock >> envelope.divider;
u32 step = envelope.steps >> ((~value & 7) << 2) & 0xf;
if(ssg.enable) step <<= 2; //SSG results in a 4x faster envelope
u32 sustain = envelope.sustainLevel < 15 ? envelope.sustainLevel << 5 : 0x1f << 5;
if(envelope.state == Attack) {
u32 next = envelope.value + (~u16(envelope.value) * step >> 4) & 0x3ff;
if(next <= envelope.value) {
envelope.value = next;
} else {
envelope.value = 0;
envelope.state = envelope.value < sustain ? Decay : Sustain;
if(envelope.value == 0) {
envelope.state = Decay;
updateEnvelope();
} else if(envelope.rate < 62) {
// will stop updating if attack rate is increased to upper threshold during attack phase (confirmed behavior)
envelope.value += ~u16(envelope.value) * step >> 4;
}
} else if(!ssg.enable || envelope.value < 0x200) {
envelope.value = min(envelope.value + step, 0x3ff);
}
if(envelope.state != Attack) {
if(envelope.state == Decay && envelope.value >= sustain) {
envelope.state = Sustain;
updateEnvelope();
}
if(ssg.enable) step = envelope.value < 0x200 ? step << 2 : 0; //SSG results in a 4x faster envelope
envelope.value = min(envelope.value + step, 0x3ff);
}
updateLevel();
@@ -59,18 +60,19 @@ auto YM2612::Channel::Operator::runPhase() -> void {
if(!(ssg.enable && envelope.value >= 0x200)) return; //SSG loop check
if(!ssg.hold && !ssg.alternate) phase.value = 0;
if(!ssg.hold || ssg.attack == ssg.invert) ssg.invert ^= ssg.alternate;
if(!(ssg.hold && ssg.invert)) ssg.invert ^= ssg.alternate;
if(envelope.state == Attack) {
//do nothing; SSG is meant to skip the attack phase
} else if(envelope.state != Release && !ssg.hold) {
//if still looping, reset the envelope
envelope.state = Attack;
if(envelope.attackRate >= 62) {
envelope.value = 0;
envelope.state = envelope.sustainLevel ? Decay : Sustain;
}
updateEnvelope();
if(envelope.rate >= 62) {
//skip attack phase
envelope.value = 0;
}
} else if(envelope.state == Release || (ssg.hold && ssg.attack == ssg.invert)) {
//clear envelope once finished
envelope.value = 0x3ff;
@@ -115,11 +117,9 @@ auto YM2612::Channel::Operator::updatePhase() -> void {
u32 tuning = detune & 3 ? detunes[(detune & 3) - 1][ksr & 7] >> (3 - (ksr >> 3)) : 0;
u32 lfo = ym2612.lfo.clock >> 2 & 0x1f;
u32 pm = 4 * vibratos[channel.vibrato][lfo & 15] * (-lfo >> 4);
u32 msb = 10;
while(msb > 4 && ~pitch.value & 1 << msb) msb--;
s32 pm = (pitch.value * vibratos[channel.vibrato][lfo & 15] >> 9) * (lfo > 15 ? -1 : 1);
phase.delta = pitch.value + (pm >> 10 - msb) << 6 >> 7 - octave.value;
phase.delta = pitch.value + pm << 6 >> 7 - octave.value;
phase.delta = (!detune.bit(2) ? phase.delta + tuning : phase.delta - tuning) & 0x1ffff;
phase.delta = (multiple ? phase.delta * multiple : phase.delta >> 1) & 0xfffff;
}
@@ -147,6 +147,7 @@ auto YM2612::Channel::power() -> void {
for(auto& op : operators) {
op.keyOn = 0;
op.keyLine = 0;
op.lfoEnable = 0;
op.detune = 0;
op.multiple = 0;
+15 -10
View File
@@ -14,6 +14,8 @@ auto YM2612::writeData(n8 data) -> void {
case 0x022: {
lfo.rate = data.bit(0,2);
lfo.enable = data.bit(3);
lfo.clock = 0;
lfo.divider = 0;
break;
}
@@ -37,11 +39,12 @@ auto YM2612::writeData(n8 data) -> void {
//timer control
case 0x027: {
//d6,d7 = mode (unimplemented; treated as mode 0 always)
//reload period on 0->1 transition
if(!timerA.enable && data.bit(0)) timerA.counter = timerA.period;
if(!timerB.enable && data.bit(1)) timerB.counter = timerB.period;
if(!timerB.enable && data.bit(1)) {
timerB.counter = timerB.period;
timerB.divider = 0;
}
timerA.enable = data.bit(0);
timerB.enable = data.bit(1);
@@ -51,6 +54,7 @@ auto YM2612::writeData(n8 data) -> void {
if(data.bit(4)) timerA.line = 0;
if(data.bit(5)) timerB.line = 0;
//d7 is enable bit for CSM mode (not yet implemented)
channels[2].mode = data.bit(6,7);
for(auto& op : channels[2].operators) op.updatePitch();
@@ -64,10 +68,10 @@ auto YM2612::writeData(n8 data) -> void {
if(index == 3 || index == 7) break;
if(index >= 4) index--;
channels[index][0].trigger(data.bit(4));
channels[index][1].trigger(data.bit(5));
channels[index][2].trigger(data.bit(6));
channels[index][3].trigger(data.bit(7));
channels[index][0].keyLine = data.bit(4);
channels[index][1].keyLine = data.bit(5);
channels[index][2].keyLine = data.bit(6);
channels[index][3].keyLine = data.bit(7);
break;
}
@@ -145,10 +149,11 @@ auto YM2612::writeData(n8 data) -> void {
//SSG-EG
case 0x090: {
op.ssg.hold = data.bit(0);
op.ssg.alternate = data.bit(1);
op.ssg.attack = data.bit(2);
op.ssg.enable = data.bit(3);
op.ssg.hold = op.ssg.enable ? data.bit(0) : 0;
op.ssg.alternate = op.ssg.enable ? data.bit(1) : 0;
op.ssg.attack = op.ssg.enable ? data.bit(2) : 0;
channel[index].updateLevel();
break;
}
@@ -42,6 +42,7 @@ auto YM2612::Channel::serialize(serializer& s) -> void {
auto YM2612::Channel::Operator::serialize(serializer& s) -> void {
s(keyOn);
s(keyLine);
s(lfoEnable);
s(detune);
s(multiple);
+2 -2
View File
@@ -27,7 +27,7 @@ auto YM2612::clock() -> array<i16[2]> {
auto wave = [&](u32 n, u32 modulation) -> s32 {
s32 x = (modulation >> 1) + (op[n].phase.value >> 10);
s32 y = sine[x & 0x3ff] + op[n].outputLevel;
return y < 0x2000 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0;
return y < 0x1a00 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0; // -78 dB floor
};
s32 feedback = modMask & op[0].output + op[0].prior >> 9 - channel.feedback;
@@ -124,7 +124,7 @@ auto YM2612::clock() -> array<i16[2]> {
if(++envelope.divider == 3) {
envelope.divider = 0;
envelope.clock++;
if(!++envelope.clock) ++envelope.clock; // 12-bit counter: 1..4095 - zero-value is skipped (confirmed behavior)
}
for(auto& channel : channels) {
+3 -2
View File
@@ -36,7 +36,7 @@ protected:
} dac;
struct Envelope {
n32 clock = 0;
n12 clock = 0;
n32 divider = 0;
} envelope;
@@ -91,7 +91,7 @@ protected:
Operator(Channel& channel) : channel(channel), ym2612(channel.ym2612) {}
//channel.cpp
auto trigger(bool) -> void;
auto updateKeyState(bool) -> void;
auto runEnvelope() -> void;
auto runPhase() -> void;
@@ -105,6 +105,7 @@ protected:
auto serialize(serializer&) -> void;
n1 keyOn = 0;
n1 keyLine = 0;
n1 lfoEnable = 0;
n3 detune = 0;
n4 multiple = 0;
@@ -1,7 +1,7 @@
auto M68000::instructionABCD(EffectiveAddress from, EffectiveAddress with) -> void {
if(from.mode == DataRegisterDirect) idle(2);
auto target = read<Byte, Hold, Fast>(with);
auto source = read<Byte>(from);
auto target = read<Byte, Hold, Fast>(with);
auto result = source + target + r.x;
bool c = false;
bool v = false;
@@ -98,8 +98,8 @@ template<u32 Size> auto M68000::instructionADDX(EffectiveAddress from, Effective
if constexpr(Size == Long) {
if(from.mode == DataRegisterDirect) idle(4);
}
auto target = read<Size, Hold, Fast>(with);
auto source = read<Size>(from);
auto target = read<Size, Hold, Fast>(with);
auto result = ADD<Size, Extend>(source, target);
prefetch();
write<Size>(with, result);
@@ -491,8 +491,8 @@ auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
return;
}
r.z = quotient == 0;
r.n = quotient < 0;
r.z = clip<Word>(quotient) == 0;
r.n = sign<Word>(quotient) < 0;
idle(ticks);
write<Long>(with, dividend | quotient);
@@ -531,8 +531,8 @@ auto M68000::instructionDIVU(EffectiveAddress from, DataRegister with) -> void {
ticks += force ? 6 : carry ? 4 : 2;
quotient = quotient << 1 | carry;
r.z = quotient == 0;
r.n = quotient < 0;
r.z = clip<Word>(quotient) == 0;
r.n = sign<Word>(quotient) < 0;
idle(ticks);
write<Long>(with, dividend | quotient);
@@ -1134,8 +1134,8 @@ auto M68000::instructionRTS() -> void {
auto M68000::instructionSBCD(EffectiveAddress from, EffectiveAddress with) -> void {
if(from.mode == DataRegisterDirect) idle(2);
auto target = read<Byte, Hold, Fast>(with);
auto source = read<Byte>(from);
auto target = read<Byte, Hold, Fast>(with);
auto result = target - source - r.x;
bool c = false;
bool v = false;
@@ -1256,8 +1256,8 @@ template<u32 Size> auto M68000::instructionSUBX(EffectiveAddress from, Effective
if constexpr(Size == Long) {
if(from.mode == DataRegisterDirect) idle(4);
}
auto target = read<Size, Hold, Fast>(with);
auto source = read<Size>(from);
auto target = read<Size, Hold, Fast>(with);
auto result = SUB<Size, Extend>(source, target);
prefetch();
write<Size>(with, result);
@@ -17,6 +17,16 @@ auto MOS6502::algorithmADC(n8 i) -> n8 {
return o;
}
auto MOS6502::algorithmALR(n8 i) -> n8 {
n8 o = A & i;
C = o.bit(0);
o >>= 1;
Z = o == 0;
N = o.bit(7);
return o;
}
auto MOS6502::algorithmAND(n8 i) -> n8 {
n8 o = A & i;
Z = o == 0;
@@ -24,6 +34,29 @@ auto MOS6502::algorithmAND(n8 i) -> n8 {
return o;
}
auto MOS6502::algorithmANC(n8 i) -> n8 {
n8 o = A & i;
Z = o == 0;
N = o.bit(7);
C = N;
return o;
}
auto MOS6502::algorithmARR(n8 i) -> n8 {
n8 o = A & i;
Z = o == 0;
N = o.bit(7);
bool c = C;
o = c << 7 | o >> 1;
Z = o == 0;
N = o.bit(7);
C = o.bit(6);
V = o.bit(6) ^ o.bit(5);
return o;
}
auto MOS6502::algorithmASL(n8 i) -> n8 {
C = i.bit(7);
i <<= 1;
@@ -32,6 +65,23 @@ auto MOS6502::algorithmASL(n8 i) -> n8 {
return i;
}
auto MOS6502::algorithmATX(n8 i) -> n8 {
A |= 0xff; //TODO: OR value may differ on non-NES platforms
n8 o = A & i;
X = o;
Z = o == 0;
N = o.bit(7);
return o;
}
auto MOS6502::algorithmAXS(n8 i) -> n8 {
n9 o = (A & X) - i;
C = !o.bit(8);
Z = n8(o) == 0;
N = o.bit(7);
return o;
}
auto MOS6502::algorithmBIT(n8 i) -> n8 {
Z = (A & i) == 0;
V = i.bit(6);
@@ -142,3 +192,15 @@ auto MOS6502::algorithmSBC(n8 i) -> n8 {
N = o.bit(7);
return o;
}
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;
}
@@ -23,23 +23,29 @@ auto MOS6502::instruction() -> void {
op(0x04, NoOperationZeroPage)
op(0x05, ZeroPageRead, fp(ORA), A)
op(0x06, ZeroPageModify, fp(ASL))
op(0x07, ZeroPageModify, fp(SLO))
op(0x08, PushP)
op(0x09, Immediate, fp(ORA), A)
op(0x0a, Implied, fp(ASL), A)
op(0x0b, Immediate, fp(ANC), A)
op(0x0c, NoOperationAbsolute)
op(0x0d, AbsoluteRead, fp(ORA), A)
op(0x0e, AbsoluteModify, fp(ASL))
op(0x0f, AbsoluteModify, fp(SLO))
op(0x10, Branch, N == 0)
op(0x11, IndirectYRead, fp(ORA), A)
op(0x14, NoOperationZeroPage, X)
op(0x15, ZeroPageRead, fp(ORA), A, X)
op(0x16, ZeroPageModify, fp(ASL), X)
op(0x17, ZeroPageModify, fp(SLO), X)
op(0x18, Clear, C)
op(0x19, AbsoluteRead, fp(ORA), A, Y)
op(0x1a, NoOperation)
op(0x1b, AbsoluteModify, fp(SLO), Y)
op(0x1c, NoOperationAbsolute, X)
op(0x1d, AbsoluteRead, fp(ORA), A, X)
op(0x1e, AbsoluteModify, fp(ASL), X)
op(0x1f, AbsoluteModify, fp(SLO), X)
op(0x20, CallAbsolute)
op(0x21, IndirectXRead, fp(AND), A)
op(0x24, ZeroPageRead, fp(BIT), A)
@@ -48,6 +54,7 @@ auto MOS6502::instruction() -> void {
op(0x28, PullP)
op(0x29, Immediate, fp(AND), A)
op(0x2a, Implied, fp(ROL), A)
op(0x2b, Immediate, fp(ANC), A)
op(0x2c, AbsoluteRead, fp(BIT), A)
op(0x2d, AbsoluteRead, fp(AND), A)
op(0x2e, AbsoluteModify, fp(ROL))
@@ -70,6 +77,7 @@ auto MOS6502::instruction() -> void {
op(0x48, Push, A)
op(0x49, Immediate, fp(EOR), A)
op(0x4a, Implied, fp(LSR), A)
op(0x4b, Immediate, fp(ALR), A)
op(0x4c, JumpAbsolute)
op(0x4d, AbsoluteRead, fp(EOR), A)
op(0x4e, AbsoluteModify, fp(LSR))
@@ -92,6 +100,7 @@ auto MOS6502::instruction() -> void {
op(0x68, Pull, A)
op(0x69, Immediate, fp(ADC), A)
op(0x6a, Implied, fp(ROR), A)
op(0x6b, Immediate, fp(ARR), A)
op(0x6c, JumpIndirect)
op(0x6d, AbsoluteRead, fp(ADC), A)
op(0x6e, AbsoluteModify, fp(ROR))
@@ -136,11 +145,13 @@ auto MOS6502::instruction() -> void {
op(0xa8, Transfer, A, Y, 1)
op(0xa9, Immediate, fp(LD), A)
op(0xaa, Transfer, A, X, 1)
op(0xab, Immediate, fp(ATX), A)
op(0xac, AbsoluteRead, fp(LD), Y)
op(0xad, AbsoluteRead, fp(LD), A)
op(0xae, AbsoluteRead, fp(LD), X)
op(0xb0, Branch, C == 1)
op(0xb1, IndirectYRead, fp(LD), A)
op(0xb3, IndirectYRead, fp(LD), A, X)
op(0xb4, ZeroPageRead, fp(LD), Y, X)
op(0xb5, ZeroPageRead, fp(LD), A, X)
op(0xb6, ZeroPageRead, fp(LD), X, Y)
@@ -159,6 +170,7 @@ auto MOS6502::instruction() -> void {
op(0xc8, Implied, fp(INC), Y)
op(0xc9, Immediate, fp(CMP), A)
op(0xca, Implied, fp(DEC), X)
op(0xcb, Immediate, fp(AXS), X)
op(0xcc, AbsoluteRead, fp(CPY), Y)
op(0xcd, AbsoluteRead, fp(CMP), A)
op(0xce, AbsoluteModify, fp(DEC))
@@ -182,6 +194,7 @@ auto MOS6502::instruction() -> void {
op(0xe8, Implied, fp(INC), X)
op(0xe9, Immediate, fp(SBC), A)
op(0xea, NoOperation)
op(0xeb, Immediate, fp(SBC), A)
op(0xec, AbsoluteRead, fp(CPX), X)
op(0xed, AbsoluteRead, fp(SBC), A)
op(0xee, AbsoluteModify, fp(INC))
@@ -199,6 +212,7 @@ auto MOS6502::instruction() -> void {
}
//unimplemented instruction
debug(unusual, "[6502] unimplemented opcode ", hex(code));
return instructionNoOperation();
}
@@ -112,6 +112,11 @@ auto MOS6502::instructionIndirectYRead(fp alu, n8& data) -> void {
L data = ALU(read(absolute + Y));
}
auto MOS6502::instructionIndirectYRead(fp alu, n8& data, n8& data2) -> void {
instructionIndirectYRead(alu, data);
data2 = data;
}
auto MOS6502::instructionIndirectYWrite(n8& data) -> void {
auto zeroPage = operand();
n16 absolute = load(zeroPage + 0);
@@ -31,7 +31,12 @@ struct MOS6502 {
using fp = auto (MOS6502::*)(n8) -> n8;
auto algorithmADC(n8) -> n8;
auto algorithmAND(n8) -> n8;
auto algorithmANC(n8) -> n8;
auto algorithmARR(n8) -> n8;
auto algorithmASL(n8) -> n8;
auto algorithmATX(n8) -> n8;
auto algorithmALR(n8) -> n8;
auto algorithmAXS(n8) -> n8;
auto algorithmBIT(n8) -> n8;
auto algorithmCMP(n8) -> n8;
auto algorithmCPX(n8) -> n8;
@@ -45,6 +50,7 @@ struct MOS6502 {
auto algorithmROL(n8) -> n8;
auto algorithmROR(n8) -> n8;
auto algorithmSBC(n8) -> n8;
auto algorithmSLO(n8) -> n8;
//instruction.cpp
auto interrupt() -> void;
@@ -66,6 +72,7 @@ struct MOS6502 {
auto instructionIndirectXRead(fp alu, n8& data) -> void;
auto instructionIndirectXWrite(n8& data) -> void;
auto instructionIndirectYRead(fp alu, n8& data) -> void;
auto instructionIndirectYRead(fp, n8& data, n8& data2) -> void;
auto instructionIndirectYWrite(n8& data) -> void;
auto instructionJumpAbsolute() -> void;
auto instructionJumpIndirect() -> void;
@@ -1,8 +1,8 @@
template<typename... P>
auto SH2::hint(P&&... p) const -> string {
if(1) return {};
if(1) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
return {forward<P>(p)...};
if(1) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
return {std::forward<P>(p)...};
}
auto SH2::disassembleInstruction() -> string {
+7 -13
View File
@@ -34,22 +34,16 @@ auto SH2::push(u32 data) -> void {
auto SH2::interrupt(u8 level, u8 vector) -> void {
push(SR);
push(PC);
push(PC - 4);
jump(readLong(VBR + vector * 4) + 4);
SR.I = level;
}
auto SH2::exception(u8 vector) -> void {
push(SR);
push(PC - 2);
jump(readLong(VBR + vector * 4) + 4);
}
auto SH2::addressErrorCPU() -> void {
static constexpr u8 vector = 0x09;
SP &= ~3; //not accurate; but prevents infinite recursion
push(SR);
push(PC);
push(PC - 4);
jump(readLong(VBR + vector * 4) + 4);
}
@@ -57,23 +51,23 @@ auto SH2::addressErrorDMA() -> void {
static constexpr u8 vector = 0x0a;
SP &= ~3; //not accurate; but prevents infinite recursion
push(SR);
push(PC);
push(PC - 4);
jump(readLong(VBR + vector * 4) + 4);
}
auto SH2::illegalInstruction() -> void {
if(inDelaySlot()) return illegalSlotInstruction();
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
static constexpr u8 vector = 0x04;
push(SR);
push(PC);
push(PC - 4);
jump(readLong(VBR + vector * 4) + 4);
debug(unusual, "[SH2] illegal instruction: 0x", hex(busReadWord(PC - 4), 4L), " @ 0x", hex(PC - 4));
}
auto SH2::illegalSlotInstruction() -> void {
debug(unusual, "[SH2] illegal slot instruction: 0x", hex(busReadWord(PC - 4), 4L));
static constexpr u8 vector = 0x06;
push(SR);
push(PC - 2);
push(PPC - 4);
jump(readLong(VBR + vector * 4) + 4);
debug(unusual, "[SH2] illegal slot instruction: 0x", hex(busReadWord(PC - 4), 4L));
}
+1 -1
View File
@@ -37,7 +37,7 @@ auto SH2::instruction() -> void {
do {
auto block = recompiler.block(PC - 4);
block->execute(*this);
} while (CCR <= recompiler.min_cycles);
} while (CCR < cyclesUntilSync);
step(CCR);
CCR = 0;
+12 -12
View File
@@ -503,18 +503,21 @@ auto SH2::MOVI(u32 i, u32 n) -> void {
}
//MOV.W @(disp,PC),Rn
auto SH2::MOVWI(u32 d, u32 n) -> void {
R[n] = (s16)readWord(PC + d * 2);
noinline auto SH2::MOVWI(u32 d, u32 n) -> void {
u32 pc = inDelaySlot() ? PPC - 2 : PC;
R[n] = (s16)readWord(pc + d * 2);
}
//MOV.L @(disp,PC),Rn
auto SH2::MOVLI(u32 d, u32 n) -> void {
R[n] = readLong((PC & ~3) + d * 4);
noinline auto SH2::MOVLI(u32 d, u32 n) -> void {
u32 pc = inDelaySlot() ? PPC - 2 : PC;
R[n] = readLong((pc & ~3) + d * 4);
}
//MOVA @(disp,PC),R0
auto SH2::MOVA(u32 d) -> void {
R[0] = (PC & ~3) + d * 4 - inDelaySlot() * 2;
noinline auto SH2::MOVA(u32 d) -> void {
u32 pc = inDelaySlot() ? PPC - 2 : PC;
R[0] = (pc & ~3) + d * 4;
}
//MOVT Rn
@@ -602,7 +605,7 @@ auto SH2::ROTR(u32 n) -> void {
//RTE
auto SH2::RTE() -> void {
delaySlot(readLong(SP + 0));
delaySlot(readLong(SP + 0) + 4);
SR = readLong(SP + 4);
SP += 8;
}
@@ -800,10 +803,7 @@ auto SH2::TAS(u32 n) -> void {
) return (void)(exceptions |= AddressErrorCPU);
}
auto cacheEnable = cache.enable;
cache.enable = 0;
u8 b = readByte(R[n]);
cache.enable = cacheEnable;
u8 b = readByte<Bus::Internal>(R[n]);
SR.T = b == 0;
writeByte(R[n], b | 0x80);
}
@@ -811,7 +811,7 @@ auto SH2::TAS(u32 n) -> void {
//TRAPA #imm
auto SH2::TRAPA(u32 i) -> void {
push(SR);
push(PC + 2);
push(PC - 2);
branch(readLong(VBR + i * 4) + 4);
}
+103 -87
View File
@@ -74,13 +74,15 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
return block;
}
#define readByte &SH2::readByte
#define readWord &SH2::readWord
#define readLong &SH2::readLong
#define writeByte &SH2::writeByte
#define writeWord &SH2::writeWord
#define writeLong &SH2::writeLong
#define illegal &SH2::illegalInstruction
#define readB &SH2::readByte<>
#define readW &SH2::readWord<>
#define readL &SH2::readLong<>
#define writeB &SH2::writeByte<>
#define writeW &SH2::writeWord<>
#define writeL &SH2::writeLong<>
#define readB &SH2::readByte<>
#define writeB &SH2::writeByte<>
#define illegal &SH2::illegalInstruction
auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
#define n (opcode >> 8 & 0x00f)
@@ -97,7 +99,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x04: {
add32(reg(1), Rn, R0);
mov32(reg(2), Rm);
call(writeByte);
call(writeB);
return 0;
}
@@ -105,7 +107,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x05: {
add32(reg(1), Rn, R0);
mov32(reg(2), Rm);
call(writeWord);
call(writeW);
return 0;
}
@@ -113,7 +115,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x06: {
add32(reg(1), Rn, R0);
mov32(reg(2), Rm);
call(writeLong);
call(writeL);
return 0;
}
@@ -126,7 +128,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.B @(R0,Rm),Rn
case 0x0c: {
add32(reg(1), Rm, R0);
call(readByte);
call(readB);
mov32_s8(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -135,7 +137,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.W @(R0,Rm),Rn
case 0x0d: {
add32(reg(1), Rm, R0);
call(readWord);
call(readW);
mov32_s16(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -144,7 +146,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.L @(R0,Rm),Rn
case 0x0e: {
add32(reg(1), Rm, R0);
call(readLong);
call(readL);
mov32(Rn, reg(0));
return 0;
}
@@ -153,11 +155,11 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x0f: {
mov32(reg(1), Rn);
add32(Rn, reg(1), imm(4));
call(readLong);
call(readL);
mov32(sreg(2), reg(0));
mov32(reg(1), Rm);
add32(Rm, reg(1), imm(4));
call(readLong);
call(readL);
mov64_s32(reg(0), reg(0));
mov64_s32(reg(1), sreg(2));
mul64(reg(0), reg(0), reg(1));
@@ -174,7 +176,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x10 ... 0x1f: {
add32(reg(1), Rn, imm(d4*4));
mov32(reg(2), Rm);
call(writeLong);
call(writeL);
return 0;
}
@@ -182,7 +184,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x20: {
mov32(reg(1), Rn);
mov32(reg(2), Rm);
call(writeByte);
call(writeB);
return 0;
}
@@ -190,7 +192,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x21: {
mov32(reg(1), Rn);
mov32(reg(2), Rm);
call(writeWord);
call(writeW);
return 0;
}
@@ -198,7 +200,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x22: {
mov32(reg(1), Rn);
mov32(reg(2), Rm);
call(writeLong);
call(writeL);
return 0;
}
@@ -207,7 +209,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
mov32(reg(2), Rm);
sub32(reg(1), Rn, imm(1));
mov32(Rn, reg(1));
call(writeByte);
call(writeB);
return 0;
}
@@ -216,7 +218,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
mov32(reg(2), Rm);
sub32(reg(1), Rn, imm(2));
mov32(Rn, reg(1));
call(writeWord);
call(writeW);
return 0;
}
@@ -225,7 +227,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
mov32(reg(2), Rm);
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
call(writeLong);
call(writeL);
return 0;
}
@@ -435,11 +437,11 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x4f: {
mov32(reg(1), Rn);
add32(Rn, reg(1), imm(2));
call(readWord);
call(readW);
mov32(sreg(2), reg(0));
mov32(reg(1), Rm);
add32(Rm, reg(1), imm(2));
call(readWord);
call(readW);
mov64_s16(reg(0), reg(0));
mov64_s16(reg(1), sreg(2));
mul64(reg(0), reg(0), reg(1));
@@ -454,7 +456,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.L @(disp,Rm),Rn
case 0x50 ... 0x5f: {
add32(reg(1), Rm, imm(d4*4));
call(readLong);
call(readL);
mov32(Rn, reg(0));
return 0;
}
@@ -462,7 +464,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.B @Rm,Rn
case 0x60: {
mov32(reg(1), Rm);
call(readByte);
call(readB);
mov32_s8(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -471,7 +473,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.W @Rm,Rn
case 0x61: {
mov32(reg(1), Rm);
call(readWord);
call(readW);
mov32_s16(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -480,7 +482,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.L @Rm,Rn
case 0x62: {
mov32(reg(1), Rm);
call(readLong);
call(readL);
mov32(Rn, reg(0));
return 0;
}
@@ -497,7 +499,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
if(n != m) {
add32(Rm, reg(1), imm(1));
}
call(readByte);
call(readB);
mov32_s8(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -509,7 +511,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
if(n != m) {
add32(Rm, reg(1), imm(2));
}
call(readWord);
call(readW);
mov32_s16(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -521,7 +523,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
if (n != m) {
add32(Rm, reg(1), imm(4));
}
call(readLong);
call(readL);
mov32(Rn, reg(0));
return 0;
}
@@ -611,8 +613,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.W @(disp,PC),Rn
case 0x90 ... 0x9f: {
add32(reg(1), PC, imm(d8*2));
call(readWord);
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
mov32(reg(0), PC);
auto tail = jump();
setLabel(delay);
sub32(reg(0), PPC, imm(2));
setLabel(tail);
add32(reg(1), reg(0), imm(d8*2));
call(readW);
mov32_s16(reg(0), reg(0));
mov32(Rn, reg(0));
return 0;
@@ -637,9 +645,15 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.L @(disp,PC),Rn
case 0xd0 ... 0xdf: {
and32(reg(1), PC, imm(~3));
add32(reg(1), reg(1), imm(d8*4));
call(readLong);
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
mov32(reg(0), PC);
auto tail = jump();
setLabel(delay);
sub32(reg(0), PPC, imm(2));
setLabel(tail);
and32(reg(0), reg(0), imm(~3));
add32(reg(1), reg(0), imm(d8*4));
call(readL);
mov32(Rn, reg(0));
return 0;
}
@@ -673,7 +687,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x80: {
add32(reg(1), Rm, imm(d4));
mov32(reg(2), R0);
call(writeByte);
call(writeB);
return 0;
}
@@ -681,14 +695,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x81: {
add32(reg(1), Rm, imm(d4*2));
mov32(reg(2), R0);
call(writeWord);
call(writeW);
return 0;
}
//MOV.B @(disp,Rn),R0
case 0x84: {
add32(reg(1), Rm, imm(d4));
call(readByte);
call(readB);
mov32_s8(reg(0), reg(0));
mov32(R0, reg(0));
return 0;
@@ -697,7 +711,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.W @(disp,Rn),R0
case 0x85: {
add32(reg(1), Rm, imm(d4*2));
call(readWord);
call(readW);
mov32_s16(reg(0), reg(0));
mov32(R0, reg(0));
return 0;
@@ -750,7 +764,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0xc0: {
add32(reg(1), GBR, imm(d8));
mov32(reg(2), R0);
call(writeByte);
call(writeB);
return 0;
}
@@ -758,7 +772,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0xc1: {
add32(reg(1), GBR, imm(d8*2));
mov32(reg(2), R0);
call(writeWord);
call(writeW);
return 0;
}
@@ -766,7 +780,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0xc2: {
add32(reg(1), GBR, imm(d8*4));
mov32(reg(2), R0);
call(writeLong);
call(writeL);
return 0;
}
@@ -775,13 +789,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
getSR(reg(2));
sub32(reg(1), R15, imm(4));
mov32(R15, reg(1));
call(writeLong);
add32(reg(2), PC, imm(2));
call(writeL);
sub32(reg(2), PC, imm(2));
sub32(reg(1), R15, imm(4));
mov32(R15, reg(1));
call(writeLong);
call(writeL);
add32(reg(1), VBR, imm(i * 4));
call(readLong);
call(readL);
add32(reg(0), reg(0), imm(4));
mov32(PPC, reg(0));
mov32(PPM, imm(Branch::Take));
@@ -791,7 +805,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.B @(disp,GBR),R0
case 0xc4: {
add32(reg(1), GBR, imm(d8));
call(readByte);
call(readB);
mov32_s8(reg(0), reg(0));
mov32(R0, reg(0));
return 0;
@@ -800,7 +814,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.W @(disp,GBR),R0
case 0xc5: {
add32(reg(1), GBR, imm(d8*2));
call(readWord);
call(readW);
mov32_s16(reg(0), reg(0));
mov32(R0, reg(0));
return 0;
@@ -809,19 +823,22 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//MOV.L @(disp,GBR),R0
case 0xc6: {
add32(reg(1), GBR, imm(d8*4));
call(readLong);
call(readL);
mov32(R0, reg(0));
return 0;
}
//MOVA @(disp,PC),R0
case 0xc7: {
and32(reg(0), PC, imm(~3));
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
mov32(reg(0), PC);
auto tail = jump();
setLabel(delay);
sub32(reg(0), PPC, imm(2));
setLabel(tail);
and32(reg(0), reg(0), imm(~3));
add32(reg(0), reg(0), imm(d8*4));
mov32(R0, reg(0));
auto skip = cmp32_jump(PPM, imm(0), flag_eq);
sub32(R0, R0, imm(2));
setLabel(skip);
return 0;
}
@@ -854,7 +871,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0xcc: {
mov32(reg(1), GBR);
add32(reg(1), reg(1), R0);
call(readByte);
call(readB);
test32(reg(0), imm(i), set_z);
mov32_f(T, flag_z);
return 0;
@@ -863,30 +880,30 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//AND.B #imm,@(R0,GBR)
case 0xcd: {
add32(reg(1), GBR, R0);
call(readByte);
call(readB);
and32(reg(2), reg(0), imm(i));
add32(reg(1), GBR, R0);
call(writeByte);
call(writeB);
return 0;
}
//XOR.B #imm,@(R0,GBR)
case 0xce: {
add32(reg(1), GBR, R0);
call(readByte);
call(readB);
xor32(reg(2), reg(0), imm(i));
add32(reg(1), GBR, R0);
call(writeByte);
call(writeB);
return 0;
}
//OR.B #imm,@(R0,GBR)
case 0xcf: {
add32(reg(1), GBR, R0);
call(readByte);
call(readB);
or32(reg(2), reg(0), imm(i));
add32(reg(1), GBR, R0);
call(writeByte);
call(writeB);
return 0;
}
@@ -987,7 +1004,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
mov32(reg(2), MACH);
call(writeLong);
call(writeL);
return 0;
}
@@ -996,7 +1013,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
getSR(reg(2));
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
call(writeLong);
call(writeL);
return 0;
}
@@ -1026,7 +1043,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x406: {
mov32(reg(1), Rn);
add32(Rn, reg(1), imm(4));
call(readLong);
call(readL);
mov32(MACH, reg(0));
return 0;
}
@@ -1035,7 +1052,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x407: {
mov32(reg(1), Rn);
add32(Rn, reg(1), imm(4));
call(readLong);
call(readL);
setSR(reg(0));
return 0;
}
@@ -1092,7 +1109,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
mov32(reg(2), MACL);
call(writeLong);
call(writeL);
return 0;
}
@@ -1101,7 +1118,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
mov32(reg(2), GBR);
call(writeLong);
call(writeL);
return 0;
}
@@ -1116,7 +1133,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x416: {
mov32(reg(1), Rm);
add32(Rm, reg(1), imm(4));
call(readLong);
call(readL);
mov32(MACL, reg(0));
return 0;
}
@@ -1125,7 +1142,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x417: {
mov32(reg(1), Rm);
add32(Rm, reg(1), imm(4));
call(readLong);
call(readL);
mov32(GBR, reg(0));
return 0;
}
@@ -1150,16 +1167,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//TAS @Rn
case 0x41b: {
and32(reg(1), Rn, imm(0x1fff'ffff));
or32(reg(1), reg(1), imm(0x2000'0000));
call(readByte);
mov32(reg(1), Rn);
call(&SH2::readByte<Bus::Internal>);
test32(reg(0), reg(0), set_z);
mov32_f(T, flag_z);
or32(reg(0), reg(0), imm(0x80));
mov32(reg(2), reg(0));
and32(reg(1), Rn, imm(0x1fff'ffff));
or32(reg(1), reg(1), imm(0x2000'0000));
call(writeByte);
mov32(reg(1), Rn);
call(writeB);
return 0;
}
@@ -1188,7 +1203,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
mov32(reg(2), PR);
call(writeLong);
call(writeL);
return 0;
}
@@ -1197,7 +1212,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
sub32(reg(1), Rn, imm(4));
mov32(Rn, reg(1));
mov32(reg(2), VBR);
call(writeLong);
call(writeL);
return 0;
}
@@ -1228,7 +1243,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x426: {
mov32(reg(1), Rm);
add32(Rm, reg(1), imm(4));
call(readLong);
call(readL);
mov32(PR, reg(0));
return 0;
}
@@ -1237,7 +1252,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x427: {
mov32(reg(1), Rm);
add32(Rm, reg(1), imm(4));
call(readLong);
call(readL);
mov32(VBR, reg(0));
return 0;
}
@@ -1337,12 +1352,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x002b: {
mov32(reg(1), R15);
add32(R15, reg(1), imm(4));
call(readLong);
call(readL);
add32(reg(0), reg(0), imm(4));
mov32(PPC, reg(0));
mov32(PPM, imm(Branch::Slot));
mov32(reg(1), R15);
add32(R15, reg(1), imm(4));
call(readLong);
call(readL);
setSR(reg(0));
return 1;
}
@@ -1422,10 +1438,10 @@ auto SH2::Recompiler::setSR(reg src) -> void {
#undef ET
#undef ID
#undef readByte
#undef readWord
#undef readLong
#undef writeByte
#undef writeWord
#undef writeLong
#undef readB
#undef readW
#undef readL
#undef writeB
#undef writeW
#undef writeL
#undef illegal
+2 -1
View File
@@ -45,11 +45,12 @@ auto SH2::power(bool reset) -> void {
ET = 0;
ID = 0;
exceptions = !reset ? ResetCold : ResetWarm;
cyclesUntilSync = 0;
cache = {*this};
intc = {*this};
dmac = {*this};
sci = {*this};
sci = {*this, reset ? sci.link : maybe<SH2&>()};
wdt = {};
ubc = {};
frt = {*this};
+3 -2
View File
@@ -29,7 +29,6 @@ struct SH2 {
auto exceptionHandler() -> void;
auto push(u32 data) -> void;
auto interrupt(u8 level, u8 vector) -> void;
auto exception(u8 vector) -> void;
auto addressErrorCPU() -> void;
auto addressErrorDMA() -> void;
auto illegalInstruction() -> void;
@@ -246,6 +245,9 @@ struct SH2 {
};
u32 exceptions = 0; //delayed exception flags
s32 cyclesUntilSync = 0;
s32 minCyclesBetweenSyncs = 0;
struct Recompiler : recompiler::generic {
SH2& self;
Recompiler(SH2& self) : self(self), generic(allocator) {}
@@ -276,7 +278,6 @@ struct SH2 {
bump_allocator allocator;
Pool* pools[1 << 24];
int min_cycles = 0;
} recompiler{*this};
#include "sh7604/sh7604.hpp"
+71 -23
View File
@@ -1,8 +1,10 @@
auto SH2::readByte(u32 address) -> u32 {
template<u32 Origin> auto SH2::readByte(u32 address) -> u32 {
switch(address >> 29) {
case Area::Cached: {
if(likely(cache.enable)) return cache.read<Byte>(address);
if constexpr(Origin == Bus::Cache) {
if(likely(cache.enable)) return cache.read<Byte>(address);
}
return busReadByte(address & 0x1fff'ffff);
}
@@ -11,7 +13,10 @@ auto SH2::readByte(u32 address) -> u32 {
}
case Area::Data: {
return cache.readData<Byte>(address);
if constexpr(Origin == Bus::Cache) {
return cache.readData<Byte>(address);
}
break;
}
case Area::IO: {
@@ -28,7 +33,7 @@ auto SH2::readByte(u32 address) -> u32 {
return 0;
}
auto SH2::readWord(u32 address) -> u32 {
template<u32 Origin> auto SH2::readWord(u32 address) -> u32 {
if constexpr(Accuracy::AddressErrors) {
if(unlikely(address & 1)) return exceptions |= AddressErrorCPU, 0;
}
@@ -36,7 +41,9 @@ auto SH2::readWord(u32 address) -> u32 {
switch(address >> 29) {
case Area::Cached: {
if(likely(cache.enable)) return cache.read<Word>(address);
if constexpr(Origin == Bus::Cache) {
if(likely(cache.enable)) return cache.read<Word>(address);
}
return busReadWord(address & 0x1fff'fffe);
}
@@ -45,7 +52,10 @@ auto SH2::readWord(u32 address) -> u32 {
}
case Area::Data: {
return cache.readData<Word>(address);
if constexpr(Origin == Bus::Cache) {
return cache.readData<Word>(address);
}
break;
}
case Area::IO: {
@@ -60,7 +70,7 @@ auto SH2::readWord(u32 address) -> u32 {
return 0;
}
auto SH2::readLong(u32 address) -> u32 {
template<u32 Origin> auto SH2::readLong(u32 address) -> u32 {
if constexpr(Accuracy::AddressErrors) {
if(unlikely(address & 3)) return exceptions |= AddressErrorCPU, 0;
}
@@ -68,7 +78,9 @@ auto SH2::readLong(u32 address) -> u32 {
switch(address >> 29) {
case Area::Cached: {
if(likely(cache.enable)) return cache.read<Long>(address);
if constexpr(Origin == Bus::Cache) {
if(likely(cache.enable)) return cache.read<Long>(address);
}
return busReadLong(address & 0x1fff'fffc);
}
@@ -77,11 +89,17 @@ auto SH2::readLong(u32 address) -> u32 {
}
case Area::Address: {
return cache.readAddress(address);
if constexpr(Origin == Bus::Cache) {
return cache.readAddress(address);
}
break;
}
case Area::Data: {
return cache.readData<Long>(address);
if constexpr(Origin == Bus::Cache) {
return cache.readData<Long>(address);
}
break;
}
case Area::IO: {
@@ -104,7 +122,7 @@ auto SH2::readLong(u32 address) -> u32 {
return 0;
}
auto SH2::writeByte(u32 address, u32 data) -> void {
template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
if constexpr(Accuracy::Recompiler) {
recompiler.invalidate(address);
}
@@ -112,20 +130,29 @@ auto SH2::writeByte(u32 address, u32 data) -> void {
switch(address >> 29) {
case Area::Cached: {
if(likely(cache.enable)) cache.write<Byte>(address, data);
if constexpr(Origin == Bus::Cache) {
if(likely(cache.enable)) cache.write<Byte>(address, data);
}
return busWriteByte(address & 0x1fff'ffff, data);
}
case Area::Uncached: {
cyclesUntilSync = 0;
return busWriteByte(address & 0x1fff'ffff, data);
}
case Area::Purge: {
return cache.purge(address);
if constexpr(Origin == Bus::Cache) {
return cache.purge(address);
}
break;
}
case Area::Data: {
return cache.writeData<Byte>(address, data);
if constexpr(Origin == Bus::Cache) {
return cache.writeData<Byte>(address, data);
}
break;
}
case Area::IO: {
@@ -140,7 +167,7 @@ auto SH2::writeByte(u32 address, u32 data) -> void {
}
}
auto SH2::writeWord(u32 address, u32 data) -> void {
template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
if constexpr(Accuracy::AddressErrors) {
if(unlikely(address & 1)) return (void)(exceptions |= AddressErrorCPU);
}
@@ -152,20 +179,29 @@ auto SH2::writeWord(u32 address, u32 data) -> void {
switch(address >> 29) {
case Area::Cached: {
if(likely(cache.enable)) cache.write<Word>(address, data);
if constexpr(Origin == Bus::Cache) {
if(likely(cache.enable)) cache.write<Word>(address, data);
}
return busWriteWord(address & 0x1fff'fffe, data);
}
case Area::Uncached: {
cyclesUntilSync = 0;
return busWriteWord(address & 0x1fff'fffe, data);
}
case Area::Purge: {
return cache.purge(address);
if constexpr(Origin == Bus::Cache) {
return cache.purge(address);
}
break;
}
case Area::Data: {
return cache.writeData<Word>(address, data);
if constexpr(Origin == Bus::Cache) {
return cache.writeData<Word>(address, data);
}
break;
}
case Area::IO: {
@@ -177,7 +213,7 @@ auto SH2::writeWord(u32 address, u32 data) -> void {
}
}
auto SH2::writeLong(u32 address, u32 data) -> void {
template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
if constexpr(Accuracy::AddressErrors) {
if(unlikely(address & 3)) return (void)(exceptions |= AddressErrorCPU);
}
@@ -190,24 +226,36 @@ auto SH2::writeLong(u32 address, u32 data) -> void {
switch(address >> 29) {
case Area::Cached: {
if(likely(cache.enable)) cache.write<Long>(address, data);
if constexpr(Origin == Bus::Cache) {
if(likely(cache.enable)) cache.write<Long>(address, data);
}
return busWriteLong(address & 0x1fff'fffc, data);
}
case Area::Uncached: {
cyclesUntilSync = 0;
return busWriteLong(address & 0x1fff'fffc, data);
}
case Area::Purge: {
return cache.purge(address);
if constexpr(Origin == Bus::Cache) {
return cache.purge(address);
}
break;
}
case Area::Address: {
return cache.writeAddress(address, data);
if constexpr(Origin == Bus::Cache) {
return cache.writeAddress(address, data);
}
break;
}
case Area::Data: {
return cache.writeData<Long>(address, data);
if constexpr(Origin == Bus::Cache) {
return cache.writeData<Long>(address, data);
}
break;
}
case Area::IO: {
+7 -7
View File
@@ -18,7 +18,7 @@ auto SH2::DMAC::transfer(bool c) -> void {
switch(chcr[c].ts) {
case 0: //8-bit
self->writeByte(dar[c], self->readByte(sar[c]));
self->writeByte<Bus::Internal>(dar[c], self->readByte<Bus::Internal>(sar[c]));
break;
case 1: //16-bit
if constexpr(Accuracy::AddressErrors) {
@@ -27,7 +27,7 @@ auto SH2::DMAC::transfer(bool c) -> void {
break;
}
}
self->writeWord(dar[c], self->readWord(sar[c]));
self->writeWord<Bus::Internal>(dar[c], self->readWord<Bus::Internal>(sar[c]));
break;
case 2: //32-bit
if constexpr(Accuracy::AddressErrors) {
@@ -36,7 +36,7 @@ auto SH2::DMAC::transfer(bool c) -> void {
break;
}
}
self->writeLong(dar[c], self->readLong(sar[c]));
self->writeLong<Bus::Internal>(dar[c], self->readLong<Bus::Internal>(sar[c]));
break;
case 3: //32-bit x4
if constexpr(Accuracy::AddressErrors) {
@@ -45,10 +45,10 @@ auto SH2::DMAC::transfer(bool c) -> void {
break;
}
}
self->writeLong(dar[c] + 0, self->readLong(sar[c] + 0));
self->writeLong(dar[c] + 4, self->readLong(sar[c] + 4));
self->writeLong(dar[c] + 8, self->readLong(sar[c] + 8));
self->writeLong(dar[c] + 12, self->readLong(sar[c] + 12));
self->writeLong<Bus::Internal>(dar[c] + 0, self->readLong<Bus::Internal>(sar[c] + 0));
self->writeLong<Bus::Internal>(dar[c] + 4, self->readLong<Bus::Internal>(sar[c] + 4));
self->writeLong<Bus::Internal>(dar[c] + 8, self->readLong<Bus::Internal>(sar[c] + 8));
self->writeLong<Bus::Internal>(dar[c] + 12, self->readLong<Bus::Internal>(sar[c] + 12));
sar[c] += 16; //always increments regardless of chcr[c].sm
tcr[c] -= 3; //always decrements by four instead of by one
break;
+5 -3
View File
@@ -553,7 +553,7 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
sci.scr.te = data.bit(5);
sci.scr.rie = data.bit(6);
sci.scr.tie = data.bit(7);
if(!te && sci.scr.te) sci.run();
if(te && !sci.scr.te) sci.ssr.tdre = 1;
return;
}
@@ -563,7 +563,8 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
return;
//SSR: serial status register
case 0xffff'fe04:
case 0xffff'fe04: {
bool tdre = sci.ssr.tdre;
sci.ssr.mpbt = data.bit(0);
//sci.ssr.mpb = data.bit(1) = readonly;
//sci.ssr.tend = data.bit(2) = readonly;
@@ -572,8 +573,9 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
sci.ssr.orer &= data.bit(5);
sci.ssr.rdrf &= data.bit(6);
sci.ssr.tdre &= data.bit(7);
if(sci.scr.te) sci.run();
if(tdre && !sci.ssr.tdre) sci.run();
return;
}
//RDR: receive data register
case 0xffff'fe05:
+11 -7
View File
@@ -5,6 +5,10 @@
//struct SH2 {
enum : u32 { Byte, Word, Long };
struct Bus {
enum : u32 { Cache, Internal, Peripheral };
};
struct Area { enum : u32 {
Cached = 0,
Uncached = 1,
@@ -15,12 +19,12 @@
};};
//bus.cpp
auto readByte(u32 address) -> u32;
auto readWord(u32 address) -> u32;
auto readLong(u32 address) -> u32;
auto writeByte(u32 address, u32 data) -> void;
auto writeWord(u32 address, u32 data) -> void;
auto writeLong(u32 address, u32 data) -> void;
template<u32 Origin = Bus::Cache> auto readByte(u32 address) -> u32;
template<u32 Origin = Bus::Cache> auto readWord(u32 address) -> u32;
template<u32 Origin = Bus::Cache> auto readLong(u32 address) -> u32;
template<u32 Origin = Bus::Cache> auto writeByte(u32 address, u32 data) -> void;
template<u32 Origin = Bus::Cache> auto writeWord(u32 address, u32 data) -> void;
template<u32 Origin = Bus::Cache> auto writeLong(u32 address, u32 data) -> void;
//io.cpp
auto internalReadByte(u32 address, n8 data = 0) -> n8;
@@ -197,7 +201,7 @@
n1 fer; //framing error
n1 orer; //overrun error
n1 rdrf; //receive data register full
n1 tdre; //transmit data register empty
n1 tdre = 1; //transmit data register empty
} ssr;
n8 brr = 0xff; //bit rate register
n8 tdr = 0xff; //transmit data register
+22 -17
View File
@@ -139,22 +139,28 @@ template<u32 size> auto V30MZ::RCR(u16 x, u5 y) -> u16 {
return result & mask;
}
template<u32 size> auto V30MZ::ROL(u16 x, u4 y) -> u16 {
PSW.CY = (x << y) & (1 << bits);
u16 result = ((x << y) | (x >> (bits - y))) & mask;
template<u32 size> auto V30MZ::ROL(u16 x, u5 y) -> u16 {
u16 result = x;
for(u32 n = 0; n < y; n++) {
PSW.CY = result & sign;
result = (result << 1) | PSW.CY;
}
PSW.V = (x ^ result) & sign;
return result;
return result & mask;
}
template<u32 size> auto V30MZ::ROR(u16 x, u4 y) -> u16 {
PSW.CY = (x >> (y - 1)) & 1;
u16 result = ((x >> y) | (x << (bits - y))) & mask;
template<u32 size> auto V30MZ::ROR(u16 x, u5 y) -> u16 {
u16 result = x;
for(u32 n = 0; n < y; n++) {
PSW.CY = result & 1;
result = (PSW.CY ? sign : 0) | (result >> 1);
}
PSW.V = (x ^ result) & sign;
return result;
return result & mask;
}
template<u32 size> auto V30MZ::SAL(u16 x, u5 y) -> u16 {
PSW.CY = (x << y) & (1 << bits);
if(y != 0) PSW.CY = (x << y) & (1 << bits);
u16 result = (x << y) & mask;
PSW.P = parity(result);
PSW.Z = result == 0;
@@ -164,13 +170,12 @@ template<u32 size> auto V30MZ::SAL(u16 x, u5 y) -> u16 {
}
template<u32 size> auto V30MZ::SAR(u16 x, u5 y) -> u16 {
if(y & 16) {
PSW.CY = x & sign;
return 0 - PSW.CY;
u16 result = x;
for(u32 n = 0; n < y; n++) {
PSW.CY = result & 1;
result = (result & sign) | (result >> 1);
}
PSW.CY = (x >> (y - 1)) & 1;
u16 result = (x >> y) & mask;
if(x & sign) result |= mask << (bits - y);
result &= mask;
PSW.P = parity(result);
PSW.Z = result == 0;
PSW.S = result & sign;
@@ -183,7 +188,7 @@ template<u32 size> auto V30MZ::SBB(u16 x, u16 y) -> u16 {
}
template<u32 size> auto V30MZ::SHL(u16 x, u5 y) -> u16 {
PSW.CY = (x << y) & (1 << bits);
if(y != 0) PSW.CY = (x << y) & (1 << bits);
u16 result = (x << y) & mask;
PSW.P = parity(result);
PSW.Z = result == 0;
@@ -193,7 +198,7 @@ template<u32 size> auto V30MZ::SHL(u16 x, u5 y) -> u16 {
}
template<u32 size> auto V30MZ::SHR(u16 x, u5 y) -> u16 {
PSW.CY = (x >> (y - 1)) & 1;
if(y != 0) PSW.CY = (x >> (y - 1)) & 1;
u16 result = (x >> y) & mask;
PSW.P = parity(result);
PSW.Z = result == 0;
+2 -2
View File
@@ -121,8 +121,8 @@ struct V30MZ {
template<u32> auto OR (u16, u16) -> u16;
template<u32> auto RCL (u16, u5 ) -> u16;
template<u32> auto RCR (u16, u5 ) -> u16;
template<u32> auto ROL (u16, u4 ) -> u16;
template<u32> auto ROR (u16, u4 ) -> u16;
template<u32> auto ROL (u16, u5 ) -> u16;
template<u32> auto ROR (u16, u5 ) -> u16;
template<u32> auto SAL (u16, u5 ) -> u16;
template<u32> auto SAR (u16, u5 ) -> u16;
template<u32> auto SBB (u16, u16) -> u16;
+97 -92
View File
@@ -17,9 +17,10 @@ auto Z80::instruction() -> void {
if(code == 0xcb && prefix != Prefix::hl) {
WZ = HL + (i8)operand();
wait(2); //+1 fetch, +1 memory read
n8 opcd = operand(); // pseudo opcode fetch (3 cycles)
wait(2);
//R is not incremented here
instructionCBd(WZ, opcode());
instructionCBd(WZ, opcd);
} else if(code == 0xcb) {
R.bit(0,6)++;
instructionCB(opcode());
@@ -242,7 +243,7 @@ auto Z80::instruction(n8 code) -> void {
op(0xca, JP_c_nn, ZF == 1)
//op(0xcb, cb:)
op(0xcc, CALL_c_nn, ZF == 1)
op(0xcd, CALL_nn)
op(0xcd, CALL_c_nn, true)
op(0xce, ADC_a_n)
op(0xcf, RST_o, 1)
op(0xd0, RET_c, CF == 0)
@@ -297,6 +298,8 @@ auto Z80::instruction(n8 code) -> void {
}
auto Z80::instructionCB(n8 code) -> void {
n8 _;
switch(code) {
op(0x00, RLC_r, B)
op(0x01, RLC_r, C)
@@ -304,7 +307,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x03, RLC_r, E)
op(0x04, RLC_r, H)
op(0x05, RLC_r, L)
op(0x06, RLC_irr, _HL)
op(0x06, RLC_irr_r, _HL, _)
op(0x07, RLC_r, A)
op(0x08, RRC_r, B)
op(0x09, RRC_r, C)
@@ -312,7 +315,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x0b, RRC_r, E)
op(0x0c, RRC_r, H)
op(0x0d, RRC_r, L)
op(0x0e, RRC_irr, _HL)
op(0x0e, RRC_irr_r, _HL, _)
op(0x0f, RRC_r, A)
op(0x10, RL_r, B)
op(0x11, RL_r, C)
@@ -320,7 +323,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x13, RL_r, E)
op(0x14, RL_r, H)
op(0x15, RL_r, L)
op(0x16, RL_irr, _HL)
op(0x16, RL_irr_r, _HL, _)
op(0x17, RL_r, A)
op(0x18, RR_r, B)
op(0x19, RR_r, C)
@@ -328,7 +331,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x1b, RR_r, E)
op(0x1c, RR_r, H)
op(0x1d, RR_r, L)
op(0x1e, RR_irr, _HL)
op(0x1e, RR_irr_r, _HL, _)
op(0x1f, RR_r, A)
op(0x20, SLA_r, B)
op(0x21, SLA_r, C)
@@ -336,7 +339,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x23, SLA_r, E)
op(0x24, SLA_r, H)
op(0x25, SLA_r, L)
op(0x26, SLA_irr, _HL)
op(0x26, SLA_irr_r, _HL, _)
op(0x27, SLA_r, A)
op(0x28, SRA_r, B)
op(0x29, SRA_r, C)
@@ -344,7 +347,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x2b, SRA_r, E)
op(0x2c, SRA_r, H)
op(0x2d, SRA_r, L)
op(0x2e, SRA_irr, _HL)
op(0x2e, SRA_irr_r, _HL, _)
op(0x2f, SRA_r, A)
op(0x30, SLL_r, B)
op(0x31, SLL_r, C)
@@ -352,7 +355,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x33, SLL_r, E)
op(0x34, SLL_r, H)
op(0x35, SLL_r, L)
op(0x36, SLL_irr, _HL)
op(0x36, SLL_irr_r, _HL, _)
op(0x37, SLL_r, A)
op(0x38, SRL_r, B)
op(0x39, SRL_r, C)
@@ -360,7 +363,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x3b, SRL_r, E)
op(0x3c, SRL_r, H)
op(0x3d, SRL_r, L)
op(0x3e, SRL_irr, _HL)
op(0x3e, SRL_irr_r, _HL, _)
op(0x3f, SRL_r, A)
op(0x40, BIT_o_r, 0, B)
op(0x41, BIT_o_r, 0, C)
@@ -368,7 +371,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x43, BIT_o_r, 0, E)
op(0x44, BIT_o_r, 0, H)
op(0x45, BIT_o_r, 0, L)
op(0x46, BIT_o_irr, 0, _HL)
op(0x46, BIT_o_irr_r, 0, _HL, _)
op(0x47, BIT_o_r, 0, A)
op(0x48, BIT_o_r, 1, B)
op(0x49, BIT_o_r, 1, C)
@@ -376,7 +379,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x4b, BIT_o_r, 1, E)
op(0x4c, BIT_o_r, 1, H)
op(0x4d, BIT_o_r, 1, L)
op(0x4e, BIT_o_irr, 1, _HL)
op(0x4e, BIT_o_irr_r, 1, _HL, _)
op(0x4f, BIT_o_r, 1, A)
op(0x50, BIT_o_r, 2, B)
op(0x51, BIT_o_r, 2, C)
@@ -384,7 +387,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x53, BIT_o_r, 2, E)
op(0x54, BIT_o_r, 2, H)
op(0x55, BIT_o_r, 2, L)
op(0x56, BIT_o_irr, 2, _HL)
op(0x56, BIT_o_irr_r, 2, _HL, _)
op(0x57, BIT_o_r, 2, A)
op(0x58, BIT_o_r, 3, B)
op(0x59, BIT_o_r, 3, C)
@@ -392,7 +395,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x5b, BIT_o_r, 3, E)
op(0x5c, BIT_o_r, 3, H)
op(0x5d, BIT_o_r, 3, L)
op(0x5e, BIT_o_irr, 3, _HL)
op(0x5e, BIT_o_irr_r, 3, _HL, _)
op(0x5f, BIT_o_r, 3, A)
op(0x60, BIT_o_r, 4, B)
op(0x61, BIT_o_r, 4, C)
@@ -400,7 +403,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x63, BIT_o_r, 4, E)
op(0x64, BIT_o_r, 4, H)
op(0x65, BIT_o_r, 4, L)
op(0x66, BIT_o_irr, 4, _HL)
op(0x66, BIT_o_irr_r, 4, _HL, _)
op(0x67, BIT_o_r, 4, A)
op(0x68, BIT_o_r, 5, B)
op(0x69, BIT_o_r, 5, C)
@@ -408,7 +411,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x6b, BIT_o_r, 5, E)
op(0x6c, BIT_o_r, 5, H)
op(0x6d, BIT_o_r, 5, L)
op(0x6e, BIT_o_irr, 5, _HL)
op(0x6e, BIT_o_irr_r, 5, _HL, _)
op(0x6f, BIT_o_r, 5, A)
op(0x70, BIT_o_r, 6, B)
op(0x71, BIT_o_r, 6, C)
@@ -416,7 +419,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x73, BIT_o_r, 6, E)
op(0x74, BIT_o_r, 6, H)
op(0x75, BIT_o_r, 6, L)
op(0x76, BIT_o_irr, 6, _HL)
op(0x76, BIT_o_irr_r, 6, _HL, _)
op(0x77, BIT_o_r, 6, A)
op(0x78, BIT_o_r, 7, B)
op(0x79, BIT_o_r, 7, C)
@@ -424,7 +427,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x7b, BIT_o_r, 7, E)
op(0x7c, BIT_o_r, 7, H)
op(0x7d, BIT_o_r, 7, L)
op(0x7e, BIT_o_irr, 7, _HL)
op(0x7e, BIT_o_irr_r, 7, _HL, _)
op(0x7f, BIT_o_r, 7, A)
op(0x80, RES_o_r, 0, B)
op(0x81, RES_o_r, 0, C)
@@ -432,7 +435,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x83, RES_o_r, 0, E)
op(0x84, RES_o_r, 0, H)
op(0x85, RES_o_r, 0, L)
op(0x86, RES_o_irr, 0, _HL)
op(0x86, RES_o_irr_r, 0, _HL, _)
op(0x87, RES_o_r, 0, A)
op(0x88, RES_o_r, 1, B)
op(0x89, RES_o_r, 1, C)
@@ -440,7 +443,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x8b, RES_o_r, 1, E)
op(0x8c, RES_o_r, 1, H)
op(0x8d, RES_o_r, 1, L)
op(0x8e, RES_o_irr, 1, _HL)
op(0x8e, RES_o_irr_r, 1, _HL, _)
op(0x8f, RES_o_r, 1, A)
op(0x90, RES_o_r, 2, B)
op(0x91, RES_o_r, 2, C)
@@ -448,7 +451,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x93, RES_o_r, 2, E)
op(0x94, RES_o_r, 2, H)
op(0x95, RES_o_r, 2, L)
op(0x96, RES_o_irr, 2, _HL)
op(0x96, RES_o_irr_r, 2, _HL, _)
op(0x97, RES_o_r, 2, A)
op(0x98, RES_o_r, 3, B)
op(0x99, RES_o_r, 3, C)
@@ -456,7 +459,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0x9b, RES_o_r, 3, E)
op(0x9c, RES_o_r, 3, H)
op(0x9d, RES_o_r, 3, L)
op(0x9e, RES_o_irr, 3, _HL)
op(0x9e, RES_o_irr_r, 3, _HL, _)
op(0x9f, RES_o_r, 3, A)
op(0xa0, RES_o_r, 4, B)
op(0xa1, RES_o_r, 4, C)
@@ -464,7 +467,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xa3, RES_o_r, 4, E)
op(0xa4, RES_o_r, 4, H)
op(0xa5, RES_o_r, 4, L)
op(0xa6, RES_o_irr, 4, _HL)
op(0xa6, RES_o_irr_r, 4, _HL, _)
op(0xa7, RES_o_r, 4, A)
op(0xa8, RES_o_r, 5, B)
op(0xa9, RES_o_r, 5, C)
@@ -472,7 +475,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xab, RES_o_r, 5, E)
op(0xac, RES_o_r, 5, H)
op(0xad, RES_o_r, 5, L)
op(0xae, RES_o_irr, 5, _HL)
op(0xae, RES_o_irr_r, 5, _HL, _)
op(0xaf, RES_o_r, 5, A)
op(0xb0, RES_o_r, 6, B)
op(0xb1, RES_o_r, 6, C)
@@ -480,7 +483,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xb3, RES_o_r, 6, E)
op(0xb4, RES_o_r, 6, H)
op(0xb5, RES_o_r, 6, L)
op(0xb6, RES_o_irr, 6, _HL)
op(0xb6, RES_o_irr_r, 6, _HL, _)
op(0xb7, RES_o_r, 6, A)
op(0xb8, RES_o_r, 7, B)
op(0xb9, RES_o_r, 7, C)
@@ -488,7 +491,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xbb, RES_o_r, 7, E)
op(0xbc, RES_o_r, 7, H)
op(0xbd, RES_o_r, 7, L)
op(0xbe, RES_o_irr, 7, _HL)
op(0xbe, RES_o_irr_r, 7, _HL, _)
op(0xbf, RES_o_r, 7, A)
op(0xc0, SET_o_r, 0, B)
op(0xc1, SET_o_r, 0, C)
@@ -496,7 +499,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xc3, SET_o_r, 0, E)
op(0xc4, SET_o_r, 0, H)
op(0xc5, SET_o_r, 0, L)
op(0xc6, SET_o_irr, 0, _HL)
op(0xc6, SET_o_irr_r, 0, _HL, _)
op(0xc7, SET_o_r, 0, A)
op(0xc8, SET_o_r, 1, B)
op(0xc9, SET_o_r, 1, C)
@@ -504,7 +507,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xcb, SET_o_r, 1, E)
op(0xcc, SET_o_r, 1, H)
op(0xcd, SET_o_r, 1, L)
op(0xce, SET_o_irr, 1, _HL)
op(0xce, SET_o_irr_r, 1, _HL, _)
op(0xcf, SET_o_r, 1, A)
op(0xd0, SET_o_r, 2, B)
op(0xd1, SET_o_r, 2, C)
@@ -512,7 +515,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xd3, SET_o_r, 2, E)
op(0xd4, SET_o_r, 2, H)
op(0xd5, SET_o_r, 2, L)
op(0xd6, SET_o_irr, 2, _HL)
op(0xd6, SET_o_irr_r, 2, _HL, _)
op(0xd7, SET_o_r, 2, A)
op(0xd8, SET_o_r, 3, B)
op(0xd9, SET_o_r, 3, C)
@@ -520,7 +523,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xdb, SET_o_r, 3, E)
op(0xdc, SET_o_r, 3, H)
op(0xdd, SET_o_r, 3, L)
op(0xde, SET_o_irr, 3, _HL)
op(0xde, SET_o_irr_r, 3, _HL, _)
op(0xdf, SET_o_r, 3, A)
op(0xe0, SET_o_r, 4, B)
op(0xe1, SET_o_r, 4, C)
@@ -528,7 +531,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xe3, SET_o_r, 4, E)
op(0xe4, SET_o_r, 4, H)
op(0xe5, SET_o_r, 4, L)
op(0xe6, SET_o_irr, 4, _HL)
op(0xe6, SET_o_irr_r, 4, _HL, _)
op(0xe7, SET_o_r, 4, A)
op(0xe8, SET_o_r, 5, B)
op(0xe9, SET_o_r, 5, C)
@@ -536,7 +539,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xeb, SET_o_r, 5, E)
op(0xec, SET_o_r, 5, H)
op(0xed, SET_o_r, 5, L)
op(0xee, SET_o_irr, 5, _HL)
op(0xee, SET_o_irr_r, 5, _HL, _)
op(0xef, SET_o_r, 5, A)
op(0xf0, SET_o_r, 6, B)
op(0xf1, SET_o_r, 6, C)
@@ -544,7 +547,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xf3, SET_o_r, 6, E)
op(0xf4, SET_o_r, 6, H)
op(0xf5, SET_o_r, 6, L)
op(0xf6, SET_o_irr, 6, _HL)
op(0xf6, SET_o_irr_r, 6, _HL, _)
op(0xf7, SET_o_r, 6, A)
op(0xf8, SET_o_r, 7, B)
op(0xf9, SET_o_r, 7, C)
@@ -552,7 +555,7 @@ auto Z80::instructionCB(n8 code) -> void {
op(0xfb, SET_o_r, 7, E)
op(0xfc, SET_o_r, 7, H)
op(0xfd, SET_o_r, 7, L)
op(0xfe, SET_o_irr, 7, _HL)
op(0xfe, SET_o_irr_r, 7, _HL, _)
op(0xff, SET_o_r, 7, A)
}
}
@@ -625,70 +628,70 @@ auto Z80::instructionCBd(n16 addr, n8 code) -> void {
op(0x3d, SRL_irr_r, addr, L)
op(0x3e, SRL_irr_r, addr, _)
op(0x3f, SRL_irr_r, addr, A)
op(0x40, BIT_o_irr_r, 0, addr, B)
op(0x41, BIT_o_irr_r, 0, addr, C)
op(0x42, BIT_o_irr_r, 0, addr, D)
op(0x43, BIT_o_irr_r, 0, addr, E)
op(0x44, BIT_o_irr_r, 0, addr, H)
op(0x45, BIT_o_irr_r, 0, addr, L)
op(0x40, BIT_o_irr_r, 0, addr, _)
op(0x41, BIT_o_irr_r, 0, addr, _)
op(0x42, BIT_o_irr_r, 0, addr, _)
op(0x43, BIT_o_irr_r, 0, addr, _)
op(0x44, BIT_o_irr_r, 0, addr, _)
op(0x45, BIT_o_irr_r, 0, addr, _)
op(0x46, BIT_o_irr_r, 0, addr, _)
op(0x47, BIT_o_irr_r, 0, addr, A)
op(0x48, BIT_o_irr_r, 1, addr, B)
op(0x49, BIT_o_irr_r, 1, addr, C)
op(0x4a, BIT_o_irr_r, 1, addr, D)
op(0x4b, BIT_o_irr_r, 1, addr, E)
op(0x4c, BIT_o_irr_r, 1, addr, H)
op(0x4d, BIT_o_irr_r, 1, addr, L)
op(0x47, BIT_o_irr_r, 0, addr, _)
op(0x48, BIT_o_irr_r, 1, addr, _)
op(0x49, BIT_o_irr_r, 1, addr, _)
op(0x4a, BIT_o_irr_r, 1, addr, _)
op(0x4b, BIT_o_irr_r, 1, addr, _)
op(0x4c, BIT_o_irr_r, 1, addr, _)
op(0x4d, BIT_o_irr_r, 1, addr, _)
op(0x4e, BIT_o_irr_r, 1, addr, _)
op(0x4f, BIT_o_irr_r, 1, addr, A)
op(0x50, BIT_o_irr_r, 2, addr, B)
op(0x51, BIT_o_irr_r, 2, addr, C)
op(0x52, BIT_o_irr_r, 2, addr, D)
op(0x53, BIT_o_irr_r, 2, addr, E)
op(0x54, BIT_o_irr_r, 2, addr, H)
op(0x55, BIT_o_irr_r, 2, addr, L)
op(0x4f, BIT_o_irr_r, 1, addr, _)
op(0x50, BIT_o_irr_r, 2, addr, _)
op(0x51, BIT_o_irr_r, 2, addr, _)
op(0x52, BIT_o_irr_r, 2, addr, _)
op(0x53, BIT_o_irr_r, 2, addr, _)
op(0x54, BIT_o_irr_r, 2, addr, _)
op(0x55, BIT_o_irr_r, 2, addr, _)
op(0x56, BIT_o_irr_r, 2, addr, _)
op(0x57, BIT_o_irr_r, 2, addr, A)
op(0x58, BIT_o_irr_r, 3, addr, B)
op(0x59, BIT_o_irr_r, 3, addr, C)
op(0x5a, BIT_o_irr_r, 3, addr, D)
op(0x5b, BIT_o_irr_r, 3, addr, E)
op(0x5c, BIT_o_irr_r, 3, addr, H)
op(0x5d, BIT_o_irr_r, 3, addr, L)
op(0x57, BIT_o_irr_r, 2, addr, _)
op(0x58, BIT_o_irr_r, 3, addr, _)
op(0x59, BIT_o_irr_r, 3, addr, _)
op(0x5a, BIT_o_irr_r, 3, addr, _)
op(0x5b, BIT_o_irr_r, 3, addr, _)
op(0x5c, BIT_o_irr_r, 3, addr, _)
op(0x5d, BIT_o_irr_r, 3, addr, _)
op(0x5e, BIT_o_irr_r, 3, addr, _)
op(0x5f, BIT_o_irr_r, 3, addr, A)
op(0x60, BIT_o_irr_r, 4, addr, B)
op(0x61, BIT_o_irr_r, 4, addr, C)
op(0x62, BIT_o_irr_r, 4, addr, D)
op(0x63, BIT_o_irr_r, 4, addr, E)
op(0x64, BIT_o_irr_r, 4, addr, H)
op(0x65, BIT_o_irr_r, 4, addr, L)
op(0x5f, BIT_o_irr_r, 3, addr, _)
op(0x60, BIT_o_irr_r, 4, addr, _)
op(0x61, BIT_o_irr_r, 4, addr, _)
op(0x62, BIT_o_irr_r, 4, addr, _)
op(0x63, BIT_o_irr_r, 4, addr, _)
op(0x64, BIT_o_irr_r, 4, addr, _)
op(0x65, BIT_o_irr_r, 4, addr, _)
op(0x66, BIT_o_irr_r, 4, addr, _)
op(0x67, BIT_o_irr_r, 4, addr, A)
op(0x68, BIT_o_irr_r, 5, addr, B)
op(0x69, BIT_o_irr_r, 5, addr, C)
op(0x6a, BIT_o_irr_r, 5, addr, D)
op(0x6b, BIT_o_irr_r, 5, addr, E)
op(0x6c, BIT_o_irr_r, 5, addr, H)
op(0x6d, BIT_o_irr_r, 5, addr, L)
op(0x67, BIT_o_irr_r, 4, addr, _)
op(0x68, BIT_o_irr_r, 5, addr, _)
op(0x69, BIT_o_irr_r, 5, addr, _)
op(0x6a, BIT_o_irr_r, 5, addr, _)
op(0x6b, BIT_o_irr_r, 5, addr, _)
op(0x6c, BIT_o_irr_r, 5, addr, _)
op(0x6d, BIT_o_irr_r, 5, addr, _)
op(0x6e, BIT_o_irr_r, 5, addr, _)
op(0x6f, BIT_o_irr_r, 5, addr, A)
op(0x70, BIT_o_irr_r, 6, addr, B)
op(0x71, BIT_o_irr_r, 6, addr, C)
op(0x72, BIT_o_irr_r, 6, addr, D)
op(0x73, BIT_o_irr_r, 6, addr, E)
op(0x74, BIT_o_irr_r, 6, addr, H)
op(0x75, BIT_o_irr_r, 6, addr, L)
op(0x6f, BIT_o_irr_r, 5, addr, _)
op(0x70, BIT_o_irr_r, 6, addr, _)
op(0x71, BIT_o_irr_r, 6, addr, _)
op(0x72, BIT_o_irr_r, 6, addr, _)
op(0x73, BIT_o_irr_r, 6, addr, _)
op(0x74, BIT_o_irr_r, 6, addr, _)
op(0x75, BIT_o_irr_r, 6, addr, _)
op(0x76, BIT_o_irr_r, 6, addr, _)
op(0x77, BIT_o_irr_r, 6, addr, A)
op(0x78, BIT_o_irr_r, 7, addr, B)
op(0x79, BIT_o_irr_r, 7, addr, C)
op(0x7a, BIT_o_irr_r, 7, addr, D)
op(0x7b, BIT_o_irr_r, 7, addr, E)
op(0x7c, BIT_o_irr_r, 7, addr, H)
op(0x7d, BIT_o_irr_r, 7, addr, L)
op(0x77, BIT_o_irr_r, 6, addr, _)
op(0x78, BIT_o_irr_r, 7, addr, _)
op(0x79, BIT_o_irr_r, 7, addr, _)
op(0x7a, BIT_o_irr_r, 7, addr, _)
op(0x7b, BIT_o_irr_r, 7, addr, _)
op(0x7c, BIT_o_irr_r, 7, addr, _)
op(0x7d, BIT_o_irr_r, 7, addr, _)
op(0x7e, BIT_o_irr_r, 7, addr, _)
op(0x7f, BIT_o_irr_r, 7, addr, A)
op(0x7f, BIT_o_irr_r, 7, addr, _)
op(0x80, RES_o_irr_r, 0, addr, B)
op(0x81, RES_o_irr_r, 0, addr, C)
op(0x82, RES_o_irr_r, 0, addr, D)
@@ -821,6 +824,8 @@ auto Z80::instructionCBd(n16 addr, n8 code) -> void {
}
auto Z80::instructionED(n8 code) -> void {
n8 _;
switch(code) {
op(0x40, IN_r_ic, B)
op(0x41, OUT_ic_r, B)
@@ -870,7 +875,7 @@ auto Z80::instructionED(n8 code) -> void {
op(0x6d, RETI)
op(0x6e, IM_o, 0)
op(0x6f, RLD)
op(0x70, IN_ic)
op(0x70, IN_r_ic, _)
op(0x71, OUT_ic)
op(0x72, SBC_hl_rr, SP)
op(0x73, LD_inn_rr, SP)
+47 -84
View File
@@ -24,9 +24,9 @@ auto Z80::instructionADC_a_r(n8& x) -> void { Q = 1;
auto Z80::instructionADC_hl_rr(n16& x) -> void { Q = 1;
WZ = HL + 1;
wait(4);
auto lo = ADD(HL >> 0, x >> 0, CF);
wait(3);
auto lo = ADD(HL >> 0, x >> 0, CF);
wait(4);
auto hi = ADD(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
ZF = HL == 0;
@@ -47,9 +47,9 @@ auto Z80::instructionADD_a_r(n8& x) -> void { Q = 1;
auto Z80::instructionADD_hl_rr(n16& x) -> void { Q = 1;
WZ = HL + 1;
bool vf = VF, zf = ZF, sf = SF;
wait(4);
auto lo = ADD(HL >> 0, x >> 0);
wait(3);
auto lo = ADD(HL >> 0, x >> 0);
wait(4);
auto hi = ADD(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
VF = vf, ZF = zf, SF = sf; //restore unaffected flags
@@ -67,14 +67,9 @@ auto Z80::instructionAND_a_r(n8& x) -> void { Q = 1;
A = AND(A, x);
}
auto Z80::instructionBIT_o_irr(n3 bit, n16& addr) -> void { Q = 1;
BIT(bit, read(addr));
XF = WZH.bit(3);
YF = WZH.bit(5);
}
auto Z80::instructionBIT_o_irr_r(n3 bit, n16& addr, n8& x) -> void { Q = 1;
x = BIT(bit, read(addr));
wait(1);
XF = WZH.bit(3);
YF = WZH.bit(5);
}
@@ -91,13 +86,6 @@ auto Z80::instructionCALL_c_nn(bool c) -> void { Q = 0;
PC = WZ;
}
auto Z80::instructionCALL_nn() -> void { Q = 0;
WZ = operands();
wait(1);
push(PC);
PC = WZ;
}
auto Z80::instructionCCF() -> void {
if(Q) XF = 0, YF = 0;
HF = CF;
@@ -225,8 +213,10 @@ auto Z80::instructionEI() -> void { Q = 0;
auto Z80::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;
}
@@ -245,7 +235,6 @@ auto Z80::instructionHALT() -> void { Q = 0;
}
auto Z80::instructionIM_o(n2 code) -> void { Q = 0;
wait(4);
IM = code;
}
@@ -260,11 +249,6 @@ auto Z80::instructionIN_r_ic(n8& x) -> void { Q = 1;
WZ = BC + 1;
}
auto Z80::instructionIN_ic() -> void { Q = 1;
IN(in(BC));
WZ = BC + 1;
}
auto Z80::instructionINC_irr(n16& x) -> void { Q = 1;
auto addr = displace(x);
auto data = read(addr);
@@ -351,7 +335,7 @@ auto Z80::instructionLD_a_inn() -> void { Q = 0;
auto Z80::instructionLD_a_irr(n16& x) -> void { Q = 0;
WZ = x;
A = read(displace(WZ));
A = read(x);
WZ++;
}
@@ -370,14 +354,16 @@ auto Z80::instructionLD_inn_rr(n16& x) -> void { Q = 0;
auto Z80::instructionLD_irr_a(n16& x) -> void { Q = 0;
WZ = x;
write(displace(WZ), A);
write(x, A);
WZL++;
WZH = A;
}
auto Z80::instructionLD_irr_n(n16& x) -> void { Q = 0;
auto addr = displace(x);
write(addr, operand());
auto addr = displace(x,0); // special case: defer wait cycles (overlap with n fetch)
auto data = operand();
if(&x == &ix.word || &x == &iy.word) wait(2);
write(addr, data);
}
auto Z80::instructionLD_irr_r(n16& x, n8& y) -> void { Q = 0;
@@ -561,12 +547,10 @@ auto Z80::instructionPUSH_rr(n16& x) -> void { Q = 0;
push(x);
}
auto Z80::instructionRES_o_irr(n3 bit, n16& addr) -> void { Q = 1;
write(addr, RES(bit, read(addr)));
}
auto Z80::instructionRES_o_irr_r(n3 bit, n16& addr, n8& x) -> void { Q = 1;
write(addr, x = RES(bit, read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = RES(bit, data));
}
auto Z80::instructionRES_o_r(n3 bit, n8& x) -> void { Q = 1;
@@ -574,7 +558,6 @@ auto Z80::instructionRES_o_r(n3 bit, n8& x) -> void { Q = 1;
}
auto Z80::instructionRET() -> void { Q = 0;
wait(1);
WZ = pop();
PC = WZ;
}
@@ -598,12 +581,10 @@ auto Z80::instructionRETN() -> void { Q = 0;
IFF1 = IFF2;
}
auto Z80::instructionRL_irr(n16& addr) -> void { Q = 1;
write(addr, RL(read(addr)));
}
auto Z80::instructionRL_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = RL(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = RL(data));
}
auto Z80::instructionRL_r(n8& x) -> void { Q = 1;
@@ -621,12 +602,10 @@ auto Z80::instructionRLA() -> void { Q = 1;
YF = A.bit(5);
}
auto Z80::instructionRLC_irr(n16& addr) -> void { Q = 1;
write(addr, RLC(read(addr)));
}
auto Z80::instructionRLC_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = RLC(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = RLC(data));
}
auto Z80::instructionRLC_r(n8& x) -> void { Q = 1;
@@ -647,9 +626,8 @@ auto Z80::instructionRLCA() -> void { Q = 1;
auto Z80::instructionRLD() -> void { Q = 1;
WZ = HL + 1;
auto data = read(HL);
wait(1);
wait(4);
write(HL, (data << 4) | (A & 0x0f));
wait(3);
A = (A & 0xf0) | (data >> 4);
NF = 0;
@@ -661,12 +639,10 @@ auto Z80::instructionRLD() -> void { Q = 1;
SF = A.bit(7);
}
auto Z80::instructionRR_irr(n16& addr) -> void { Q = 1;
write(addr, RR(read(addr)));
}
auto Z80::instructionRR_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = RR(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = RR(data));
}
auto Z80::instructionRR_r(n8& x) -> void { Q = 1;
@@ -684,12 +660,10 @@ auto Z80::instructionRRA() -> void { Q = 1;
YF = A.bit(5);
}
auto Z80::instructionRRC_irr(n16& addr) -> void { Q = 1;
write(addr, RRC(read(addr)));
}
auto Z80::instructionRRC_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = RRC(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = RRC(data));
}
auto Z80::instructionRRC_r(n8& x) -> void { Q = 1;
@@ -710,9 +684,8 @@ auto Z80::instructionRRCA() -> void { Q = 1;
auto Z80::instructionRRD() -> void { Q = 1;
WZ = HL + 1;
auto data = read(HL);
wait(1);
wait(4);
write(HL, (data >> 4) | (A << 4));
wait(3);
A = (A & 0xf0) | (data & 0x0f);
NF = 0;
@@ -745,9 +718,9 @@ auto Z80::instructionSBC_a_r(n8& x) -> void { Q = 1;
auto Z80::instructionSBC_hl_rr(n16& x) -> void { Q = 1;
WZ = HL + 1;
wait(4);
auto lo = SUB(HL >> 0, x >> 0, CF);
wait(3);
auto lo = SUB(HL >> 0, x >> 0, CF);
wait(4);
auto hi = SUB(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
ZF = HL == 0;
@@ -763,60 +736,50 @@ auto Z80::instructionSCF() -> void {
Q = 1;
}
auto Z80::instructionSET_o_irr(n3 bit, n16& addr) -> void { Q = 1;
write(addr, SET(bit, read(addr)));
}
auto Z80::instructionSET_o_irr_r(n3 bit, n16& addr, n8& x) -> void { Q = 1;
write(addr, x = SET(bit, read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = SET(bit, data));
}
auto Z80::instructionSET_o_r(n3 bit, n8& x) -> void { Q = 1;
x = SET(bit, x);
}
auto Z80::instructionSLA_irr(n16& addr) -> void { Q = 1;
write(addr, SLA(read(addr)));
}
auto Z80::instructionSLA_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = SLA(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = SLA(data));
}
auto Z80::instructionSLA_r(n8& x) -> void { Q = 1;
x = SLA(x);
}
auto Z80::instructionSLL_irr(n16& addr) -> void { Q = 1;
write(addr, SLL(read(addr)));
}
auto Z80::instructionSLL_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = SLL(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = SLL(data));
}
auto Z80::instructionSLL_r(n8& x) -> void { Q = 1;
x = SLL(x);
}
auto Z80::instructionSRA_irr(n16& addr) -> void { Q = 1;
write(addr, SRA(read(addr)));
}
auto Z80::instructionSRA_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = SRA(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = SRA(data));
}
auto Z80::instructionSRA_r(n8& x) -> void { Q = 1;
x = SRA(x);
}
auto Z80::instructionSRL_irr(n16& addr) -> void { Q = 1;
write(addr, SRL(read(addr)));
}
auto Z80::instructionSRL_irr_r(n16& addr, n8& x) -> void { Q = 1;
write(addr, x = SRL(read(addr)));
auto data = read(addr);
wait(1);
write(addr, x = SRL(data));
}
auto Z80::instructionSRL_r(n8& x) -> void { Q = 1;
+2 -2
View File
@@ -27,10 +27,10 @@ auto Z80::pop() -> n16 {
return data | read(SP++) << 8;
}
auto Z80::displace(n16& x) -> n16 {
auto Z80::displace(n16& x, u32 wclocks) -> n16 {
if(&x != &ix.word && &x != &iy.word) return x;
auto d = operand();
wait(5);
wait(wclocks);
WZ = x + (i8)d;
return WZ;
}
+39 -18
View File
@@ -31,29 +31,31 @@ auto Z80::power(MOSFET mosfet) -> void {
HALT = 0;
IFF1 = 0;
IFF2 = 0;
IM = 1;
IM = 0;
}
auto Z80::irq(bool maskable, n16 pc, n8 extbus) -> bool {
auto Z80::reset() -> void {
prefix = Prefix::hl;
ir.word = 0x0000;
WZ = PC = 0x0000;
EI = 0;
HALT = 0;
IFF1 = 0;
IFF2 = 0;
IM = 0;
}
auto Z80::irq(n8 extbus) -> bool {
//do not execute maskable interrupts if disabled or immediately after EI instruction
if(maskable && (!IFF1 || EI)) return false;
if(!IFF1 || EI) return false;
R.bit(0,6)++;
push(PC);
switch(maskable ? IM : (n2)1) {
switch(IM) {
case 1: extbus = 0xff;
case 0: {
//external data bus ($ff = RST $38)
WZ = extbus;
wait(extbus | 0x38 == 0xff ? 6 : 7);
break;
}
case 1: {
//constant address
WZ = pc;
wait(maskable ? 7 : 5);
wait(6);
instruction(WZ);
break;
}
@@ -63,18 +65,37 @@ auto Z80::irq(bool maskable, n16 pc, n8 extbus) -> bool {
WZL = read(address + 0);
WZH = read(address + 1);
wait(7);
push(PC);
PC = WZ;
break;
}
}
PC = WZ;
IFF1 = 0;
if(maskable) IFF2 = 0;
IFF2 = 0;
HALT = 0;
if(P) PF = 0;
P = 0;
Q = 0;
return true;
}
auto Z80::nmi() -> bool {
R.bit(0,6)++;
push(PC);
WZ = 0x66;
wait(5);
PC = WZ;
IFF1 = 0;
HALT = 0;
if(P) PF = 0;
P = 0;
Q = 0;
return true;
}
+4 -2
View File
@@ -22,8 +22,10 @@ struct Z80 {
//z80.cpp
auto power(MOSFET = MOSFET::NMOS) -> void;
auto reset() -> void;
auto irq(bool maskable, n16 vector = 0x0000, n8 extbus = 0xff) -> bool;
auto irq(n8 extbus = 0xff) -> bool;
auto nmi() -> bool;
auto parity(n8) const -> bool;
//memory.cpp
@@ -33,7 +35,7 @@ struct Z80 {
auto operands() -> n16;
auto push(n16) -> void;
auto pop() -> n16;
auto displace(n16&) -> n16;
auto displace(n16&, u32 wclocks = 5) -> n16;
auto read(n16 address) -> n8;
auto write(n16 address, n8 data) -> void;
auto in(n16 address) -> n8;
+2 -2
View File
@@ -27,13 +27,13 @@ auto CPU::main() -> void {
if(state.nmiPending) {
state.nmiPending = 0; //edge-sensitive
debugger.interrupt("NMI");
irq(0, 0x0066, 0xff);
nmi();
}
if(state.irqLine) {
//level-sensitive
debugger.interrupt("IRQ");
irq(1, 0x0038, 0xff);
irq();
}
debugger.instruction();
+2 -2
View File
@@ -10,7 +10,7 @@ FDS fds;
auto FDS::load(Node::Object parent) -> void {
port = parent->append<Node::Port>("Disk Slot");
port->setFamily("Famicom Disk");
port->setFamily("Famicom Disk System");
port->setType("Floppy Disk");
port->setHotSwappable(true);
port->setAllocate([&](auto name) { return allocate(port); });
@@ -35,7 +35,7 @@ auto FDS::unload() -> void {
}
auto FDS::allocate(Node::Port parent) -> Node::Peripheral {
return node = parent->append<Node::Peripheral>("Famicom Disk");
return node = parent->append<Node::Peripheral>("Famicom Disk System");
}
auto FDS::connect() -> void {
+7 -9
View File
@@ -24,7 +24,7 @@ struct MBC2 : Interface {
return rom.read(io.rom.bank << 14 | (n14)address);
}
if(address >= 0xa000 && address <= 0xa1ff) {
if(address >= 0xa000 && address <= 0xbfff) {
if(!ram || !io.ram.enable) return 0xff;
auto pair = ram.read(address.bit(1,8));
if((address & 1) == 0) data = 0xf0 | pair.bit(0,3);
@@ -36,18 +36,16 @@ struct MBC2 : Interface {
}
auto write(n16 address, n8 data) -> void override {
if(address >= 0x0000 && address <= 0x1fff) {
if(address >= 0x0000 && address <= 0x3fff) {
if(!address.bit(8)) io.ram.enable = data.bit(0,3) == 0x0a;
if(address.bit(8)) {
io.rom.bank = data.bit(0,3);
if(!io.rom.bank) io.rom.bank = 0x01;
}
return;
}
if(address >= 0x2000 && address <= 0x3fff) {
if(address.bit(8)) io.rom.bank = data.bit(0,3);
if(!io.rom.bank) io.rom.bank = 0x01;
return;
}
if(address >= 0xa000 && address <= 0xa1ff) {
if(address >= 0xa000 && address <= 0xbfff) {
if(!ram || !io.ram.enable) return;
auto pair = ram.read(address.bit(1,8));
if((address & 1) == 0) pair.bit(0,3) = data.bit(0,3);
+3 -23
View File
@@ -134,30 +134,10 @@ auto CPU::power() -> void {
status = {};
//note: this may not be accurate, DIV values at startup may be due to LCD delays?
//but probably not, since "CPU CGB" differs from "CPU CGB [A-E]"
// TODO: Validate this for models other than dmg ABC/mgb
status.div = 8;
//0146~0149
if(version->latch() == "DMG-CPU" ) status.div = 0x0146;
//ffe6~ffe9
if(version->latch() == "DMG-CPU A" ) status.div = 0xffe6;
if(version->latch() == "DMG-CPU B" ) status.div = 0xffe6;
if(version->latch() == "DMG-CPU C" ) status.div = 0xffe6;
if(version->latch() == "CPU MGB" ) status.div = 0xffe6;
if(version->latch() == "SGB-CPU 01") status.div = 0xffe6; //unconfirmed
if(version->latch() == "CPU SGB2" ) status.div = 0xffe6; //unconfirmed
//0206~0209
if(version->latch() == "CPU CGB" ) status.div = 0x0206;
//fffa~fffd
if(version->latch() == "CPU CGB A" ) status.div = 0xfffa;
if(version->latch() == "CPU CGB B" ) status.div = 0xfffa;
if(version->latch() == "CPU CGB C" ) status.div = 0xfffa;
if(version->latch() == "CPU CGB D" ) status.div = 0xfffa;
if(version->latch() == "CPU CGB E" ) status.div = 0xfffa;
if(Model::GameBoyColor()) status.cgbMode = 1;
}
}
+6 -2
View File
@@ -110,6 +110,10 @@ struct CPU : SM83, Thread {
//$ff0f IF
n5 interruptFlag;
//$ff4c KEY0
n1 cgbMode;
n1 opriEnable;
//$ff4d KEY1
n1 speedSwitch;
n1 speedDouble;
@@ -124,8 +128,8 @@ struct CPU : SM83, Thread {
n7 dmaLength;
n1 hdmaActive;
//$ff6c ???
n1 ff6c;
//$ff6c OPRI
n1 opri;
//$ff70 SVBK
n3 wramBank = 1;
+24 -17
View File
@@ -87,33 +87,33 @@ auto CPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
return data;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff4d && cycle == 2) { //KEY1
data.bit(0) = status.speedSwitch;
data.bit(7) = status.speedDouble;
return data;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff55 && cycle == 2) { //HDMA5
data.bit(0,6) = status.dmaLength;
data.bit(7) = !status.hdmaActive;
return data;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff56 && cycle == 2) { //RP
//unemulated
return 0x02;
}
if(Model::GameBoyColor())
if(address == 0xff6c && cycle == 2) { //???
data.bit(0) = status.ff6c;
if(address == 0xff6c && cycle == 2) { //OPRI
data.bit(0) = status.opri;
return data;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff70 && cycle == 2) { //SVBK
return status.wramBank;
}
@@ -207,37 +207,44 @@ auto CPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && cartridge.bootromEnable)
if(address == 0xff4c && cycle == 2) { //KEY0
status.cgbMode = !(data & 0x0c);
status.opriEnable = data.bit(3);
return;
}
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff4d && cycle == 2) { //KEY1
status.speedSwitch = data.bit(0);
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff51 && cycle == 2) { //HDMA1
status.dmaSource.bit(8,15) = data.bit(0,7);
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff52 && cycle == 2) { //HDMA2
status.dmaSource.bit(4,7) = data.bit(4,7);
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff53 && cycle == 2) { //HDMA3
status.dmaTarget.bit(8,15) = data.bit(0,7);
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff54 && cycle == 2) { //HDMA4
status.dmaTarget.bit(4,7) = data.bit(4,7);
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff55 && cycle == 2) { //HDMA5
//1->0 transistion stops an active HDMA (and does not trigger GDMA)
if(status.hdmaActive && !data.bit(7)) {
@@ -261,14 +268,14 @@ auto CPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff56 && cycle == 2) { //RP
return;
}
if(Model::GameBoyColor())
if(address == 0xff6c && cycle == 2) { //???
status.ff6c = data.bit(0);
if(Model::GameBoyColor() && (cartridge.bootromEnable || status.opriEnable))
if(address == 0xff6c && cycle == 2) { //OPRI
status.opri = data.bit(0);
return;
}
@@ -296,7 +303,7 @@ auto CPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && status.cgbMode)
if(address == 0xff70 && cycle == 2) { //SVBK
status.wramBank = data.bit(0,3);
return;
+3 -1
View File
@@ -28,6 +28,8 @@ auto CPU::serialize(serializer& s) -> void {
s(status.interruptFlag);
s(status.cgbMode);
s(status.opriEnable);
s(status.speedSwitch);
s(status.speedDouble);
@@ -36,7 +38,7 @@ auto CPU::serialize(serializer& s) -> void {
s(status.dmaLength);
s(status.hdmaActive);
s(status.ff6c);
s(status.opri);
s(status.wramBank);
+1 -1
View File
@@ -8,7 +8,7 @@ auto CPU::step() -> void {
auto CPU::step(u32 clocks) -> void {
for(auto n : range(clocks)) {
status.div++;
if(!r.stop) status.div++;
if((n4 )status.div == 0) timer262144hz();
if((n6 )status.div == 0) timer65536hz();
if((n8 )status.div == 0) timer16384hz();
+39 -14
View File
@@ -13,6 +13,8 @@
//0x07: palette#
auto PPU::readTileCGB(bool select, u32 x, u32 y, n16& tiledata, n8& attributes) -> void {
if(!cpu.status.cgbMode) return readTileDMG(select, x, y, tiledata);
n14 tilemapAddress = 0x1800 + (select << 10);
tilemapAddress += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
@@ -35,6 +37,8 @@ auto PPU::readTileCGB(bool select, u32 x, u32 y, n16& tiledata, n8& attributes)
}
auto PPU::readObjectCGB(i16 y, n8 tile, n8 attributes, n16& tiledata) -> void {
if(!cpu.status.cgbMode) return readObjectDMG(y, tile, attributes, tiledata);
const s32 Height = (status.obSize == 0 ? 8 : 16);
tile &= ~status.obSize;
y = status.ly - y;
@@ -65,6 +69,9 @@ auto PPU::scanlineCGB() -> void {
if(++sprites == 10) break;
}
//sort by X-coordinate if opri (DMG priority) is enabled
if(cpu.status.opri) sort(sprite, sprites, [](auto l, auto r) { return l.x < r.x; });
}
auto PPU::runCGB() -> void {
@@ -72,22 +79,34 @@ auto PPU::runCGB() -> void {
ob = {};
n15 color = 0x7fff;
runBackgroundCGB();
if(cpu.status.cgbMode || status.bgEnable) runBackgroundCGB();
if(latch.windowDisplayEnable) runWindowCGB();
if(status.obEnable) runObjectsCGB();
if(ob.palette == 0) {
color = bg.color;
} else if(bg.palette == 0) {
color = ob.color;
} else if(status.bgEnable == 0) {
color = ob.color;
} else if(bg.priority) {
color = bg.color;
} else if(ob.priority) {
color = ob.color;
if(cpu.status.cgbMode ) {
if(ob.palette == 0) {
color = bg.color;
} else if(bg.palette == 0) {
color = ob.color;
} else if(status.bgEnable == 0) {
color = ob.color;
} else if(bg.priority) {
color = bg.color;
} else if(ob.priority) {
color = ob.color;
} else {
color = bg.color;
}
} else {
color = bg.color;
if(ob.palette == 0) {
color = bg.color;
} else if(bg.palette == 0) {
color = ob.color;
} else if(ob.priority) {
color = ob.color;
} else {
color = bg.color;
}
}
if(Model::GameBoyColor()) {
@@ -107,15 +126,17 @@ auto PPU::runBackgroundCGB() -> void {
index.bit(1) = background.tiledata.bit(15 - tileX);
n5 palette = background.attributes.bit(0,2) << 2 | index;
if(!cpu.status.cgbMode) palette = bgp[index];
bg.color = bgpd[palette];
bg.palette = index;
bg.priority = background.attributes.bit(7);
if(cpu.status.cgbMode) bg.priority = background.attributes.bit(7);
}
auto PPU::runWindowCGB() -> void {
if(status.ly < status.wy) return;
if(px + 7 < status.wx) return;
if(px + 7 == status.wx) latch.wy++;
if(!cpu.status.cgbMode && !status.bgEnable) return;
n8 scrollY = latch.wy - 1;
n8 scrollX = px + 7 - latch.wx;
@@ -128,9 +149,11 @@ auto PPU::runWindowCGB() -> void {
index.bit(1) = window.tiledata.bit(15 - tileX);
n5 palette = window.attributes.bit(0,2) << 2 | index;
if(!cpu.status.cgbMode) palette = bgp[index];
bg.color = bgpd[palette];
bg.palette = index;
bg.priority = window.attributes.bit(7);
if(cpu.status.cgbMode) bg.priority = window.attributes.bit(7);
}
auto PPU::runObjectsCGB() -> void {
@@ -148,6 +171,8 @@ auto PPU::runObjectsCGB() -> void {
if(index == 0) continue;
n5 palette = s.attributes.bit(0,2) << 2 | index;
if(!cpu.status.cgbMode) palette = obp[index];
ob.color = obpd[palette];
ob.palette = index;
ob.priority = !s.attributes.bit(7);
+6 -6
View File
@@ -91,7 +91,7 @@ auto PPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
return status.wx;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && cpu.status.cgbMode)
if(address == 0xff4f && cycle == 2) { //VBK
return status.vramBank;
}
@@ -103,7 +103,7 @@ auto PPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
return data;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
if(address == 0xff69 && cycle == 2) { //BGPD
return bgpd[status.bgpi >> 1].byte(status.bgpi & 1);
}
@@ -115,7 +115,7 @@ auto PPU::readIO(u32 cycle, n16 address, n8 data) -> n8 {
return data;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
if(address == 0xff6b && cycle == 2) { //OBPD
return obpd[status.obpi >> 1].byte(status.obpi & 1);
}
@@ -236,7 +236,7 @@ auto PPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && cpu.status.cgbMode)
if(address == 0xff4f && cycle == 2) { //VBK
status.vramBank = data.bit(0);
return;
@@ -249,7 +249,7 @@ auto PPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
return;
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
if(address == 0xff69 && cycle == 2) { //BGPD
bgpd[status.bgpi >> 1].byte(status.bgpi & 1) = data;
if(status.bgpiIncrement) status.bgpi++;
@@ -262,7 +262,7 @@ auto PPU::writeIO(u32 cycle, n16 address, n8 data) -> void {
status.obpiIncrement = data.bit(7);
}
if(Model::GameBoyColor())
if(Model::GameBoyColor() && ((!cpu.status.cgbMode && cartridge.bootromEnable) || cpu.status.cgbMode))
if(address == 0xff6b && cycle == 2) { //OBPD
obpd[status.obpi >> 1].byte(status.obpi & 1) = data;
if(status.obpiIncrement) status.obpi++;
+1 -1
View File
@@ -81,7 +81,7 @@ auto PPU::unload() -> void {
}
auto PPU::main() -> void {
if(!status.displayEnable) {
if(!status.displayEnable || cpu.r.stop) {
step(456 * 154);
if(screen) screen->frame();
scheduler.exit(Event::Frame);
+4 -2
View File
@@ -18,11 +18,12 @@ auto PPU::compareLYC() const -> bool {
auto ly = status.ly;
auto lyc = status.lyc;
if(Model::GameBoy() || Model::SuperGameBoy()) {
if(Model::GameBoy() || Model::SuperGameBoy() || !cpu.status.cgbMode) {
auto lx = status.lx >> 2;
if(ly != 0 && lx == 0) return 0;
if(ly == 153 && lx == 2) return 0;
if(ly == 153 && lx >= 3) return lyc == 0;
return lyc == ly;
}
if(Model::GameBoyColor() && cpu.lowSpeed()) {
@@ -42,9 +43,10 @@ auto PPU::compareLYC() const -> bool {
auto PPU::getLY() const -> n8 {
auto ly = status.ly;
if(Model::GameBoy() || Model::SuperGameBoy()) {
if(Model::GameBoy() || Model::SuperGameBoy() || !cpu.status.cgbMode) {
auto lx = status.lx >> 2;
if(ly == 153 && lx >= 1) return 0;
return ly;
}
if(Model::GameBoyColor() && cpu.lowSpeed()) {
+4 -3
View File
@@ -13,7 +13,7 @@ Cartridge& cartridge = cartridgeSlot.cartridge;
Cartridge::Cartridge() {
mrom.data = new n8[mrom.size = 32 * 1024 * 1024];
sram.data = new n8[sram.size = 32 * 1024];
eeprom.data = new n8[eeprom.size = 8 * 1024];
eeprom.data = new n8[eeprom.size = 16 * 1024];
flash.data = new n8[flash.size = 128 * 1024];
}
@@ -50,9 +50,9 @@ auto Cartridge::connect() -> void {
if(auto fp = pak->read("save.eeprom")) {
has.eeprom = true;
eeprom.size = min(8_KiB, fp->size());
eeprom.size = min(16_KiB, fp->size());
eeprom.bits = eeprom.size <= 512 ? 6 : 14;
if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size
if(eeprom.size == 0) eeprom.size = 16384, eeprom.bits = 0; //auto-detect size
eeprom.mask = mrom.size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
eeprom.test = mrom.size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
for(auto n : range(eeprom.size)) eeprom.data[n] = 0xff;
@@ -96,6 +96,7 @@ auto Cartridge::save() -> void {
}
if(auto fp = pak->write("save.eeprom")) {
if(fp->size() < eeprom.size) fp->resize(eeprom.size);
fp->write({eeprom.data, eeprom.size});
}
+5 -2
View File
@@ -13,10 +13,13 @@ auto Cartridge::EEPROM::read() -> bool {
//EEPROM size auto-detection
if(bits == 0 && mode == Mode::ReadAddress) {
print("EEPROM address bits: ", --addressbits, "\n");
bits = addressbits == 6 ? 6 : 14;
size = 8192;
bits = addressbits;
size = 1 << addressbits;
mode = Mode::ReadData;
offset = 0;
print("EEPROM size: ", size, "\n");
for(auto n : range(size)) data[n] = 0xff;
//fallthrough
}
+7 -4
View File
@@ -28,13 +28,13 @@ auto APU::main() -> void {
if(state.nmiLine) {
state.nmiLine = 0; //edge-sensitive
debugger.interrupt("NMI");
irq(0, 0x0066, 0xff);
nmi();
}
if(state.intLine) {
//level-sensitive
debugger.interrupt("IRQ");
irq(1, 0x0038, 0xff);
irq();
}
debugger.instruction();
@@ -69,10 +69,13 @@ auto APU::power(bool reset) -> void {
Z80::power();
Thread::create(system.frequency() / 15.0, {&APU::main, this});
if(!reset) {
Z80::power();
ram.fill();
state.resLine = 0;
state.busreqLine = 0;
state.busreqLatch = 0;
} else {
Z80::reset();
}
state.nmiLine = 0;
state.intLine = 0;
@@ -81,11 +84,11 @@ auto APU::power(bool reset) -> void {
}
auto APU::restart() -> void {
Z80::power();
Z80::reset();
Thread::restart({&APU::main, this});
state.nmiLine = 0;
state.intLine = 0;
state.bank = 0;
state.busreqLatch = state.busreqLine;
opn2.power(true);
}
+12 -4
View File
@@ -13,7 +13,7 @@ inline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
if(address >= 0x400000 && address <= 0x7fffff) {
waitRefreshExternal();
if(!cartridge.bootable()) {
if(!cartridge.bootable() || !MegaCD()) {
data = cartridge.read(upper, lower, address, data);
} else {
data = mcd.readExternal(upper, lower, address, data);
@@ -22,7 +22,11 @@ inline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
}
if(address >= 0x800000 && address <= 0x9fffff) {
data = m32x.readExternal(upper, lower, address, data);
if(!Mega32X()) {
data = cartridge.read(upper, lower, address, data);
} else {
data = m32x.readExternal(upper, lower, address, data);
}
return data;
}
@@ -71,7 +75,7 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
if(address >= 0x400000 && address <= 0x7fffff) {
waitRefreshExternal();
if(!cartridge.bootable()) {
if(!cartridge.bootable() || !MegaCD()) {
cartridge.write(upper, lower, address, data);
} else {
mcd.writeExternal(upper, lower, address, data);
@@ -80,7 +84,11 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
}
if(address >= 0x800000 && address <= 0x9fffff) {
m32x.writeExternal(upper, lower, address, data);
if(!Mega32X()) {
cartridge.write(upper, lower, address, data);
} else {
m32x.writeExternal(upper, lower, address, data);
}
return;
}
+2 -2
View File
@@ -29,7 +29,7 @@ struct Banked : Interface {
}
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramEnable) {
return wram[address >> 1];
@@ -59,7 +59,7 @@ struct Banked : Interface {
return data = rom[offset >> 1];
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
//emulating ramWritable will break commercial software:
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
//hence RAM ends up always being writable, and many games fail to set d1=1
+1
View File
@@ -1,5 +1,6 @@
namespace Board {
#include "standard.cpp"
#include "linear.cpp"
#include "banked.cpp"
#include "svp.cpp"
+2 -2
View File
@@ -11,8 +11,8 @@ struct Interface {
virtual auto save() -> void {}
virtual auto main() -> void;
virtual auto step(u32 clocks) -> void;
virtual auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 { return data; }
virtual auto write(n1 upper, n1 lower, n22 address, n16 data) -> void {}
virtual auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 { return data; }
virtual auto write(n1 upper, n1 lower, n24 address, n16 data) -> void {}
virtual auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { return data; }
virtual auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void {}
virtual auto vblank(bool line) -> void {}
+2 -2
View File
@@ -11,7 +11,7 @@ struct GameGenie : Interface {
auto save() -> void override {
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(enable) {
for(auto& code : codes) {
if(code.enable && code.address == address) return data = code.data;
@@ -21,7 +21,7 @@ struct GameGenie : Interface {
return data = rom[address >> 1];
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(enable) {
if(slot.connected()) return slot.cartridge.write(upper, lower, address, data);
}
+7 -7
View File
@@ -1,25 +1,25 @@
struct JCart : Linear {
using Linear::Linear;
struct JCart : Standard {
using Standard::Standard;
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(address >= 0x380000) {
data.bit( 0, 5) = 0b111111; //controller port 1
data.bit( 6) = jcartSelect; //TH state
data.bit( 8,13) = 0b111111; //controller port 2
data.bit(14) = 0; //forced low
}
return Linear::read(upper, lower, address, data);
return Standard::read(upper, lower, address, data);
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(address >= 0x380000) {
jcartSelect = data.bit(0);
}
return Linear::write(upper, lower, address, data);
return Standard::write(upper, lower, address, data);
}
auto serialize(serializer& s) -> void override {
Linear::serialize(s);
Standard::serialize(s);
s(jcartSelect);
}
+2 -2
View File
@@ -29,7 +29,7 @@ struct Linear : Interface {
}
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramEnable) {
return wram[address >> 1];
@@ -57,7 +57,7 @@ struct Linear : Interface {
return rom[address >> 1];
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
//emulating ramWritable will break commercial software:
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
//hence RAM ends up always being writable, and many games fail to set d1=1
+37 -91
View File
@@ -1,104 +1,63 @@
struct Mega32X : Interface {
using Interface::Interface;
Memory::Writable<n16> wram;
Memory::Writable<n8 > uram;
Memory::Writable<n8 > lram;
u32 sramAddr, sramSize;
M24C m24c;
unique_pointer<Board::Interface> board;
auto load() -> void override {
if(auto fp = pak->read("program.rom")) {
m32x.rom.allocate(fp->size() >> 1);
for(auto address : range(fp->size() >> 1)) {
m32x.rom.program(address, fp->readm(2L));
}
if(pak) {
board = new Board::Standard(*cartridge);
} else {
board = new Board::Interface(*cartridge);
}
if(auto fp = pak->read("save.ram")) {
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
}
board->pak = pak;
board->load();
if(auto fp = pak->read("save.eeprom")) {
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
rsda = fp->attribute("rsda").natural();
wsda = fp->attribute("wsda").natural();
wscl = fp->attribute("wscl").natural();
}
cartridge->child = *board;
}
auto save() -> void override {
if(auto fp = pak->write("save.ram")) {
Interface::save(wram, uram, lram, "save.ram");
}
if(auto fp = pak->write("save.eeprom")) {
Interface::save(m24c, "save.eeprom");
}
board->save();
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram) {
return wram[address >> 1];
}
if(uram) {
return uram[address >> 1] * 0x0101;
}
if(lram) {
return lram[address >> 1] * 0x0101;
}
if(m24c && eepromEnable) {
if(upper && rsda >> 3 == 1) data.bit(rsda) = m24c.read();
if(lower && rsda >> 3 == 0) data.bit(rsda) = m24c.read();
return data;
}
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(!m32x.io.adapterEnable) {
return board->read(upper, lower, address, data);
}
return m32x.readExternal(upper, lower, address, data);
if(address >= 0x000000 && address <= 0x0000ff) {
return m32x.readExternal(upper, lower, address, data);
}
if(m32x.dreq.vram) {
return board->read(upper, lower, address, data);
}
return data;
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram) {
if(upper) wram[address >> 1].byte(1) = data.byte(1);
if(lower) wram[address >> 1].byte(0) = data.byte(0);
return;
}
if(uram) {
if(upper) uram[address >> 1] = data;
return;
}
if(lram) {
if(lower) lram[address >> 1] = data;
return;
}
if(m24c) {
if(m32x.rom.size() * 2 > 0x200000 && upper && lower) {
eepromEnable = !data.bit(0);
return;
}
if(upper && wscl >> 3 == 1) m24c.clock = data.bit(wscl);
if(upper && wsda >> 3 == 1) m24c.data = data.bit(wsda);
if(lower && wscl >> 3 == 0) m24c.clock = data.bit(wscl);
if(lower && wsda >> 3 == 0) m24c.data = data.bit(wsda);
return m24c.write();
}
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(!m32x.io.adapterEnable) {
return board->write(upper, lower, address, data);
}
return m32x.writeExternal(upper, lower, address, data);
if(address >= 0x000000 && address <= 0x0000ff) {
return m32x.writeExternal(upper, lower, address, data);
}
if(m32x.dreq.vram) {
return board->write(upper, lower, address, data);
}
}
auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
data = board->readIO(upper, lower, address, data);
return m32x.readExternalIO(upper, lower, address, data);
}
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void override {
board->writeIO(upper, lower, address, data);
return m32x.writeExternalIO(upper, lower, address, data);
}
@@ -111,23 +70,10 @@ struct Mega32X : Interface {
}
auto power(bool reset) -> void override {
eepromEnable = m32x.rom.size() * 2 <= 0x200000;
m24c.power();
board->power(reset);
}
auto serialize(serializer& s) -> void override {
s(wram);
s(uram);
s(lram);
s(m24c);
s(eepromEnable);
s(rsda);
s(wsda);
s(wscl);
s(*board);
}
n1 eepromEnable;
n4 rsda;
n4 wsda;
n4 wscl;
};
+157
View File
@@ -0,0 +1,157 @@
struct Standard : Interface {
using Interface::Interface;
Memory::Readable<n16> rom;
Memory::Writable<n16> wram;
Memory::Writable<n8 > uram;
Memory::Writable<n8 > lram;
u32 sramAddr, sramSize;
M24C m24c;
auto load() -> void override {
Interface::load(rom, "program.rom");
if(auto fp = pak->read("save.ram")) {
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
ramEnable = fp->attribute("enable").boolean() ? 1 : 0;
}
if(auto fp = pak->read("save.eeprom")) {
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
rsda = fp->attribute("rsda").natural();
wsda = fp->attribute("wsda").natural();
wscl = fp->attribute("wscl").natural();
}
}
auto save() -> void override {
if(auto fp = pak->write("save.ram")) {
Interface::save(wram, uram, lram, "save.ram");
}
if(auto fp = pak->write("save.eeprom")) {
Interface::save(m24c, "save.eeprom");
}
}
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(address >= 0x400000) {
if(address >= rom.size()*2) return data;
return rom[address >> 1];
}
auto bank = romBank[address >> 19];
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && (ramEnable || bank > 0x3f)) {
return wram[address >> 1];
}
if(uram && (ramEnable || bank > 0x3f)) {
return uram[address >> 1] * 0x0101;
}
if(lram && (ramEnable || bank > 0x3f)) {
return lram[address >> 1] * 0x0101;
}
if(m24c && (eepromEnable || bank > 0x3f)) {
if(upper && rsda >> 3 == 1) data.bit(rsda) = m24c.read();
if(lower && rsda >> 3 == 0) data.bit(rsda) = m24c.read();
return data;
}
}
if(bank > 0x3f) return data;
n24 offset = bank << 19 | (n19)address;
if(offset >= rom.size()*2) return data;
return rom[offset >> 1];
}
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramWritable) {
if(upper) wram[address >> 1].byte(1) = data.byte(1);
if(lower) wram[address >> 1].byte(0) = data.byte(0);
return;
}
if(uram && ramWritable) {
if(upper) uram[address >> 1] = data;
return;
}
if(lram && ramWritable) {
if(lower) lram[address >> 1] = data;
return;
}
if(m24c) {
if(wscl == 8 && upper && lower) { // control via word write (32Mbit Acclaim mappers only)
eepromEnable = !data.bit(0);
return;
}
if(upper && wscl >> 3 == 1) m24c.clock = data.bit(wscl);
if(upper && wsda >> 3 == 1) m24c.data = data.bit(wsda);
if(lower && wscl >> 3 == 0) m24c.clock = data.bit(wscl);
if(lower && wsda >> 3 == 0) m24c.data = data.bit(wsda);
return m24c.write();
}
}
}
auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
return data;
}
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(!lower) {
//todo: unconfirmed
debug(unusual, "[Cartridge::Banked::writeIO] lower=0");
return;
}
if(address == 0xa130f0) {
ramEnable = data.bit(0);
ramWritable = !data.bit(1);
}
if(address == 0xa130f2) romBank[1] = data.bit(0,5);
if(address == 0xa130f4) romBank[2] = data.bit(0,5);
if(address == 0xa130f6) romBank[3] = data.bit(0,5);
if(address == 0xa130f8) romBank[4] = data.bit(0,5);
if(address == 0xa130fa) romBank[5] = data.bit(0,5);
if(address == 0xa130fc) romBank[6] = data.bit(0,5);
if(address == 0xa130fe) romBank[7] = data.bit(0,5);
}
auto power(bool reset) -> void override {
for(auto index : range(8)) {
if(rom.size()*2 > index << 19)
romBank[index] = index;
else
romBank[index] = ~0;
}
ramWritable = 1;
eepromEnable = m24c && wscl != 8 ? 1 : 0; // for 32Mbit Acclaim mappers (670125 & 670127) [id based on wscl]
m24c.power();
}
auto serialize(serializer& s) -> void override {
s(romBank);
s(wram);
s(uram);
s(lram);
s(m24c);
s(ramEnable);
s(ramWritable);
s(eepromEnable);
s(rsda);
s(wsda);
s(wscl);
}
n7 romBank[8];
n1 ramEnable = 0;
n1 ramWritable;
n1 eepromEnable;
n4 rsda;
n4 wsda;
n4 wscl;
};
+2 -2
View File
@@ -53,7 +53,7 @@ struct SVP : Interface, SSP1601 {
SSP1601::instruction();
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16 override {
if(address >= 0x000000 && address <= 0x1fffff) {
data = rom[address >> 1];
}
@@ -77,7 +77,7 @@ struct SVP : Interface, SSP1601 {
return data;
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(address >= 0x300000 && address <= 0x37ffff) {
if(upper) dram[address >> 1].byte(1) = data.byte(1);
if(lower) dram[address >> 1].byte(0) = data.byte(0);
+13 -6
View File
@@ -25,12 +25,10 @@ auto Cartridge::connect() -> void {
board = new Board::SVP(*this);
} else if(pak->attribute("label") == "Game Genie") {
board = new Board::GameGenie(*this);
} else if(pak->read("program.rom") && pak->read("program.rom")->size() > 0x400000) {
board = new Board::Banked(*this);
} else if(pak->attribute("jcart").boolean()) {
board = new Board::JCart(*this);
} else {
board = new Board::Linear(*this);
board = new Board::Standard(*this);
}
board->pak = pak;
board->load();
@@ -43,6 +41,7 @@ auto Cartridge::disconnect() -> void {
board->unload();
board->pak.reset();
board.reset();
child.reset();
pak.reset();
node.reset();
information = {};
@@ -63,16 +62,24 @@ auto Cartridge::step(u32 clocks) -> void {
}
auto Cartridge::power(bool reset) -> void {
if(!board) board = new Board::Interface(*this);
if(!board) {
if(Mega32X()) {
board = new Board::Mega32X{*this};
} else {
board = new Board::Interface(*this);
}
board->load();
}
Thread::create(board->frequency(), {&Cartridge::main, this});
board->power(reset);
}
auto Cartridge::read(n1 upper, n1 lower, n22 address, n16 data) -> n16 {
auto Cartridge::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
return board->read(upper, lower, address, data);
}
auto Cartridge::write(n1 upper, n1 lower, n22 address, n16 data) -> void {
auto Cartridge::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
return board->write(upper, lower, address, data);
}
+3 -2
View File
@@ -19,8 +19,8 @@ struct Cartridge : Thread {
auto step(u32 clocks) -> void;
auto power(bool reset) -> void;
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16;
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void;
auto read(n1 upper, n1 lower, n24 address, n16 data) -> n16;
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void;
auto readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16;
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void;
@@ -38,6 +38,7 @@ struct Cartridge : Thread {
} information;
unique_pointer<Board::Interface> board;
maybe<Board::Interface&> child;
};
#include "slot.hpp"
+1
View File
@@ -5,5 +5,6 @@ namespace ares::MegaDrive {
#include "port.cpp"
#include "control-pad/control-pad.cpp"
#include "fighting-pad/fighting-pad.cpp"
#include "mega-mouse/mega-mouse.cpp"
}
+1
View File
@@ -26,3 +26,4 @@ struct Controller {
#include "port.hpp"
#include "control-pad/control-pad.hpp"
#include "fighting-pad/fighting-pad.hpp"
#include "mega-mouse/mega-mouse.hpp"
@@ -0,0 +1,147 @@
MegaMouse::MegaMouse(Node::Port parent) {
node = parent->append<Node::Peripheral>("Mega Mouse");
x = node->append<Node::Input::Axis>("X");
y = node->append<Node::Input::Axis>("Y");
left = node->append<Node::Input::Button>("Left");
right = node->append<Node::Input::Button>("Right");
middle = node->append<Node::Input::Button>("Middle");
start = node->append<Node::Input::Button>("Start");
status[0] = 0x0; // TH high
status[1] = 0xB; // TH low
status[2] = 0xF; // 1st nibble
status[3] = 0xF; // 2nd
// status[4..9] // 3rd .. 8th nibbles
// Is is said that some games do not do the hanshake correctly
// and rely heavily on timing, so we try to mimick the real
// timing closely.
// One game has a programming error where it polls for TL low
// instead of TL high for one of the nibbles so it falls rights
// through and works by chance. With a 1MHz clock here, vertical
// cursor motion was intermittent. So 2MHz is used.
double timerfreq = 2'000'000;
// time between falling edge of clock (TR) and
// handshake on TL.
//
// For first nibble, the measured time is min. 12us, max 34us
// For folling nibbles are available in 12-14 us.
t_handshake = 14 * timerfreq / 1000000;
// data changes a bit before the TL handshake occurs,
// precisely 4.8us before. But for some games, 4.8 does not
// work correclty and it helps to have the data update sooner. So
// 10 is used here.
t_data = 10 * timerfreq / 1000000;
Thread::create(timerfreq, {&MegaMouse::main, this});
Thread::synchronize(cpu);
}
MegaMouse::~MegaMouse() {
Thread::destroy();
}
auto MegaMouse::main() -> void {
// process clocking after building the data to
// cause at least some lag. In real life, it takes
// about 10us to the mouse to react and change TL
// to reflect TR...
if (timeout)
{
timeout--;
if (timeout == t_data) {
index++;
if (index > 9) {
index = 1;
}
}
if (timeout == 0) {
tl = tr;
}
}
Thread::step(1);
Thread::synchronize(cpu);
}
auto MegaMouse::readData() -> n8 {
n8 data;
if (th) {
// When TH is high, the mouse drives a constant on the data pins.
data.bit(0,3) = status[0];
}
else {
// When TH is low, output is clocked
data.bit(0,3) = status[index];
}
data.bit(4) = tl;
data.bit(5) = tr;
data.bit(6) = th;
data.bit(7) = latch;
return data;
}
auto MegaMouse::writeData(n8 data) -> void {
// Falling TH
if (!data.bit(6) && th) {
// When TH falls low, make sure the second nibble is driven. The
// index is otherwise controlled by clocking on TR.
index = 1;
platform->input(x);
platform->input(y);
platform->input(left);
platform->input(right);
platform->input(middle);
platform->input(start);
// Button byte
status[4] = 0;
status[5].bit(0) = left->value();
status[5].bit(1) = right->value();
status[5].bit(2) = middle->value();
status[5].bit(3) = start->value();
i16 ax = x->value();
i16 ay = -y->value();
if (ax > maxspeed) {
ax = maxspeed;
} else if (ax < -maxspeed) {
ax = -maxspeed;
}
if (ay > maxspeed) {
ay = maxspeed;
} else if (ay < -maxspeed) {
ay = -maxspeed;
}
// X byte
status[4].bit(0) = ax.bit(8);
status[6] = ax.bit(4,7);
status[7] = ax.bit(0,3);
// Y byte
status[4].bit(1) = ay.bit(8);
status[8] = ay.bit(4,7);
status[9] = ay.bit(0,3);
}
// Clock in is TR. The mouse reacts and ACKs by updating TL,
// but not instantly.
tr = data.bit(5);
if (tr != tl) {
timeout = t_handshake;
}
latch = data.bit(7);
th = data.bit(6);
}
@@ -0,0 +1,28 @@
struct MegaMouse : Controller, Thread {
Node::Input::Axis x;
Node::Input::Axis y;
Node::Input::Button left;
Node::Input::Button right;
Node::Input::Button middle;
Node::Input::Button start;
MegaMouse(Node::Port);
~MegaMouse();
auto main() -> void;
auto readData() -> n8 override;
auto writeData(n8 data) -> void override;
private:
n1 th = 1;
n1 tr = 1;
n1 tl = 1;
n1 latch;
n8 index = 0;
n4 status[10];
s16 maxspeed = 255;
n32 timeout = 0;
n32 t_data;
n32 t_handshake;
};
+2 -1
View File
@@ -13,7 +13,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
port->setAllocate([&](auto name) { return allocate(name); });
port->setDisconnect([&] { return disconnect(); });
port->setSupported({"Control Pad", "Fighting Pad"});
port->setSupported({"Control Pad", "Fighting Pad", "Mega Mouse"});
}
auto ControllerPort::unload() -> void {
@@ -24,6 +24,7 @@ auto ControllerPort::unload() -> void {
auto ControllerPort::allocate(string name) -> Node::Peripheral {
if(name == "Control Pad" ) device = new ControlPad(port);
if(name == "Fighting Pad") device = new FightingPad(port);
if(name == "Mega Mouse") device = new MegaMouse(port);
if(device) return device->node;
return {};
}
+17 -17
View File
@@ -1,9 +1,5 @@
auto M32X::readExternal(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
if(!Mega32X()) return data;
if(!io.adapterEnable) {
return rom[address >> 1];
}
if(!Mega32X() || !io.adapterEnable) return data;
if(address >= 0x000000 && address <= 0x0000ff) {
if(address == 0x70) return io.vectorLevel4 >> 16;
@@ -11,32 +7,29 @@ auto M32X::readExternal(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
return vectors[address >> 1];
}
if(address >= 0x000100 && address <= 0x3fffff) {
if(dreq.vram) return rom[address >> 1];
}
if(address >= 0x840000 && address <= 0x87ffff) {
return vdp.bbram[address >> 1 & 0xffff];
}
if(address >= 0x880000 && address <= 0x8fffff) {
if(!dreq.vram) return rom[address >> 1 & 0x3ffff];
if(!dreq.vram) {
address &= 0x7ffff;
return cartridge.child->read(upper, lower, address, data);
}
}
if(address >= 0x900000 && address <= 0x9fffff) {
address = io.romBank * 0x100000 | address & 0x0fffff;
if(!dreq.vram) return rom[address >> 1];
if(!dreq.vram) {
address = io.romBank * 0x100000 | address & 0x0fffff;
return cartridge.child->read(upper, lower, address, data);
}
}
return data;
}
auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {
if(!Mega32X()) return;
if(!io.adapterEnable) {
return;
}
if(!Mega32X() || !io.adapterEnable) return;
if(address >= 0x000000 && address <= 0x0000ff) {
if(address == 0x70 && upper) io.vectorLevel4.byte(3) = data.byte(1);
@@ -64,4 +57,11 @@ auto M32X::writeExternal(n1 upper, n1 lower, n24 address, n16 data) -> void {
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
return;
}
if(address >= 0x900000 && address <= 0x9fffff) {
if(!dreq.vram) {
address = io.romBank * 0x100000 | address & 0x0fffff;
return cartridge.child->write(upper, lower, address, data);
}
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
if(shs.active()) shs.step(1);
}
return rom[address >> 1];
return cartridge.child->read(upper, lower, address, data);
}
if(address >= 0x0400'0000 && address <= 0x0403'ffff) {
+12 -3
View File
@@ -11,14 +11,17 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
if(address == 0xa15100) {
data.bit( 0) = io.adapterEnable;
data.bit( 1) = io.adapterReset;
data.bit(2, 6) = 0;
data.bit( 7) = io.resetEnable;
data.bit(8, 14) = 0;
data.bit(15) = vdp.framebufferAccess;
}
//interrupt control
if(address == 0xa15102) {
data.bit(0) = shm.irq.cmd.active;
data.bit(1) = shs.irq.cmd.active;
data.bit(0) = shm.irq.cmd.active;
data.bit(1) = shs.irq.cmd.active;
data.bit(2, 15) = 0;
}
//bank set
@@ -153,7 +156,13 @@ auto M32X::writeExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
if(address == 0xa15100) {
if(lower) {
io.adapterEnable = data.bit(0);
io.adapterReset = data.bit(1);
if(data.bit(1) == 0) {
shm.power(true);
shs.power(true);
}
io.adapterReset = data.bit(1);
}
if(upper) {
vdp.framebufferAccess = data.bit(15);
+1 -1
View File
@@ -15,7 +15,7 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
}
data.bit( 7) = io.hintVblank;
data.bit( 8) = !(bool)cartridge.node; //0 = cartridge connected
data.bit( 9) = io.adapterEnable | 1;
data.bit( 9) = io.adapterEnable;
data.bit(15) = vdp.framebufferAccess;
}
-1
View File
@@ -34,7 +34,6 @@ auto M32X::unload() -> void {
shs.unload();
vdp.unload();
pwm.unload(node);
rom.reset();
vectors.reset();
sdram.reset();
node.reset();
-1
View File
@@ -2,7 +2,6 @@
struct M32X {
Node::Object node;
Memory::Readable<n16> rom;
Memory::Readable<n16> vectors;
Memory::Writable<n16> sdram;
+12 -2
View File
@@ -14,6 +14,8 @@ auto M32X::SH7604::unload() -> void {
}
auto M32X::SH7604::main() -> void {
if(!m32x.io.adapterReset) return step(cyclesUntilSync);
if(!SH2::inDelaySlot() && !regs.ID) {
if(irq.vres.active && irq.vres.enable) {
debugger.interrupt("VRES");
@@ -48,12 +50,20 @@ auto M32X::SH7604::main() -> void {
auto M32X::SH7604::step(u32 clocks) -> void {
Thread::step(clocks);
Thread::synchronize(cpu);
cyclesUntilSync -= clocks;
if(cyclesUntilSync <= 0) {
if(m32x.shm.active()) Thread::synchronize(m32x.shs, cpu);
if(m32x.shs.active()) Thread::synchronize(m32x.shm, cpu);
while(cyclesUntilSync <= 0) {
cyclesUntilSync += minCyclesBetweenSyncs;
}
}
}
auto M32X::SH7604::power(bool reset) -> void {
Thread::create(23'000'000, {&M32X::SH7604::main, this});
recompiler.min_cycles = 22;
minCyclesBetweenSyncs = 20;
SH2::power(reset);
irq = {};
irq.vres.enable = 1;
+1 -1
View File
@@ -29,7 +29,7 @@ auto M32X::VDP::power(bool reset) -> void {
}
auto M32X::VDP::scanline(u32 pixels[1280], u32 y) -> void {
if(!Mega32X() || !pixels) return;
if(!Mega32X() || !pixels || y >= (lines ? 240 : 224)) return;
if(mode == 1) return scanlineMode1(pixels, y);
if(mode == 2) return scanlineMode2(pixels, y);
if(mode == 3) return scanlineMode3(pixels, y);
+1 -1
View File
@@ -24,7 +24,7 @@ auto VDP::load(Node::Object parent) -> void {
screen->colors(1 << 16, {&VDP::color, this});
screen->setSize(1280, 480);
screen->setScale(0.25, 0.5);
screen->setAspect(1.0, 1.0);
screen->setAspect(32, 35);
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
if(value == 0) screen->setSize(1280, 448);
+1 -1
View File
@@ -38,7 +38,7 @@ auto VDP::load(Node::Object parent) -> void {
screen->colors(1 << 16, {&VDP::color, this});
screen->setSize(1280, 480);
screen->setScale(0.25, 0.5);
screen->setAspect(1.0, 1.0);
screen->setAspect(32, 35);
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
if(value == 0) screen->setSize(1280, 448);
+5
View File
@@ -5,5 +5,10 @@ namespace ares::MasterSystem {
#include "port.cpp"
#include "gamepad/gamepad.cpp"
#include "light-phaser/light-phaser.cpp"
#include "paddle/paddle.cpp"
#include "sports-pad/sports-pad.cpp"
#include "md-control-pad/md-control-pad.cpp"
#include "md-fighting-pad/md-fighting-pad.cpp"
#include "mega-mouse/mega-mouse.cpp"
}
+11 -1
View File
@@ -20,9 +20,19 @@ struct Controller {
virtual ~Controller() = default;
virtual auto read() -> n7 { return 0x7f; }
virtual auto write(n8 data) -> void {}
// 0: trDirection
// 1: thDirection
// 2: trLevel
// 3: thLevel
virtual auto write(n4 data) -> void {}
};
#include "port.hpp"
#include "gamepad/gamepad.hpp"
#include "light-phaser/light-phaser.hpp"
#include "paddle/paddle.hpp"
#include "sports-pad/sports-pad.hpp"
#include "md-control-pad/md-control-pad.hpp"
#include "md-fighting-pad/md-fighting-pad.hpp"
#include "mega-mouse/mega-mouse.hpp"
@@ -0,0 +1,61 @@
MdControlPad::MdControlPad(Node::Port parent) {
node = parent->append<Node::Peripheral>("MD Control Pad");
up = node->append<Node::Input::Button>("Up");
down = node->append<Node::Input::Button>("Down");
left = node->append<Node::Input::Button>("Left");
right = node->append<Node::Input::Button>("Right");
a = node->append<Node::Input::Button>("A");
b = node->append<Node::Input::Button>("B");
c = node->append<Node::Input::Button>("C");
start = node->append<Node::Input::Button>("Start");
}
auto MdControlPad::read() -> n7 {
platform->input(up);
platform->input(down);
platform->input(left);
platform->input(right);
platform->input(a);
platform->input(b);
platform->input(c);
platform->input(start);
if(!(up->value() & down->value())) {
yHold = 0, upLatch = up->value(), downLatch = down->value();
} else if(!yHold) {
yHold = 1, swap(upLatch, downLatch);
}
if(!(left->value() & right->value())) {
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
} else if(!xHold) {
xHold = 1, swap(leftLatch, rightLatch);
}
n7 data;
if (th == 0) {
data.bit(0) = upLatch;
data.bit(1) = downLatch;
data.bit(2,3) = ~0;
data.bit(4) = a->value();
data.bit(5) = start->value();
} else {
data.bit(0) = upLatch;
data.bit(1) = downLatch;
data.bit(2) = leftLatch;
data.bit(3) = rightLatch;
data.bit(4) = b->value();
data.bit(5) = c->value();
}
data = ~data;
data.bit(6) = th;
return data;
}
auto MdControlPad::write(n4 data) -> void {
th = data.bit(3);
}
@@ -0,0 +1,24 @@
struct MdControlPad : Controller {
Node::Input::Button up;
Node::Input::Button down;
Node::Input::Button left;
Node::Input::Button right;
Node::Input::Button a;
Node::Input::Button b;
Node::Input::Button c;
Node::Input::Button start;
MdControlPad(Node::Port);
auto read() -> n7 override;
auto write(n4 data) -> void override;
private:
n1 yHold;
n1 upLatch;
n1 downLatch;
n1 xHold;
n1 leftLatch;
n1 rightLatch;
n1 th;
};
@@ -0,0 +1,110 @@
MdFightingPad::MdFightingPad(Node::Port parent) {
node = parent->append<Node::Peripheral>("MD Fighting Pad");
up = node->append<Node::Input::Button>("Up");
down = node->append<Node::Input::Button>("Down");
left = node->append<Node::Input::Button>("Left");
right = node->append<Node::Input::Button>("Right");
a = node->append<Node::Input::Button>("A");
b = node->append<Node::Input::Button>("B");
c = node->append<Node::Input::Button>("C");
x = node->append<Node::Input::Button>("X");
y = node->append<Node::Input::Button>("Y");
z = node->append<Node::Input::Button>("Z");
mode = node->append<Node::Input::Button>("Mode");
start = node->append<Node::Input::Button>("Start");
Thread::create(1'000'000, {&MdFightingPad::main, this});
}
MdFightingPad::~MdFightingPad() {
Thread::destroy();
}
auto MdFightingPad::main() -> void {
if(timeout) {
timeout--;
} else {
counter = 0;
}
Thread::step(1);
Thread::synchronize(cpu);
}
auto MdFightingPad::read() -> n7 {
platform->input(up);
platform->input(down);
platform->input(left);
platform->input(right);
platform->input(a);
platform->input(b);
platform->input(c);
platform->input(x);
platform->input(y);
platform->input(z);
platform->input(mode);
platform->input(start);
if(!(up->value() & down->value())) {
yHold = 0, upLatch = up->value(), downLatch = down->value();
} else if(!yHold) {
yHold = 1, swap(upLatch, downLatch);
}
if(!(left->value() & right->value())) {
xHold = 0, leftLatch = left->value(), rightLatch = right->value();
} else if(!xHold) {
xHold = 1, swap(leftLatch, rightLatch);
}
n7 data;
if(select == 0) {
if(counter == 0 || counter == 1 || counter == 4) {
data.bit(0) = upLatch;
data.bit(1) = downLatch;
data.bit(2,3) = ~0;
}
if(counter == 2) {
data.bit(0,3) = ~0; //controller type detection
}
if(counter == 3) {
data.bit(0,3) = 0;
}
data.bit(4) = a->value();
data.bit(5) = start->value();
} else {
if(counter == 0 || counter == 1 || counter == 2 || counter == 4) {
data.bit(0) = upLatch;
data.bit(1) = downLatch;
data.bit(2) = leftLatch;
data.bit(3) = rightLatch;
data.bit(4) = b->value();
data.bit(5) = c->value();
}
if(counter == 3) {
data.bit(0) = z->value();
data.bit(1) = y->value();
data.bit(2) = x->value();
data.bit(3) = mode->value();
data.bit(4,5) = 0;
}
}
data = ~data;
data.bit(6) = select;
return data;
}
auto MdFightingPad::write(n4 data) -> void {
if(!select && data.bit(3)) { //0->1 transition
if(++counter == 5) counter = 0;
}
select = data.bit(3);
timeout = 1600; //~1.6ms
}
Loaded 100 of 599 files, more files were not shown because too many files have changed in this diff. Show more