├── .github ├── FUNDING.yml └── workflows │ ├── macos.yml │ ├── ubuntu.yml │ └── windows.yml ├── .gitignore ├── Info.plist ├── LICENSE ├── Makefile ├── README.md ├── build-clean.ps1 ├── build-deps.ps1 ├── build-win32.ps1 ├── build-win64.ps1 ├── build.sh ├── compat.txt ├── frontend ├── argparse.c ├── argparse.h ├── common.h ├── config.c ├── config.h ├── main.c ├── screen.c ├── screen.h ├── toml.c └── toml.h └── psx ├── bus.c ├── bus.h ├── bus_init.h ├── config.c ├── config.h ├── cpu.c ├── cpu.h ├── cpu_debug.h ├── dev ├── bios.c ├── bios.h ├── cdrom │ ├── audio.c │ ├── cdrom.c │ ├── cdrom.h │ ├── cue.c │ ├── cue.h │ ├── disc.c │ ├── disc.h │ ├── impl.c │ ├── list.c │ ├── list.h │ ├── queue.c │ └── queue.h ├── dma.c ├── dma.h ├── exp1.c ├── exp1.h ├── exp2.c ├── exp2.h ├── gpu.c ├── gpu.h ├── ic.c ├── ic.h ├── input.c ├── input.h ├── mc1.c ├── mc1.h ├── mc2.c ├── mc2.h ├── mc3.c ├── mc3.h ├── mcd.c ├── mcd.h ├── mdec.c ├── mdec.h ├── pad.c ├── pad.h ├── ram.c ├── ram.h ├── scratchpad.c ├── scratchpad.h ├── spu.c ├── spu.h ├── timer.c ├── timer.h ├── xa.c └── xa.h ├── exe.c ├── exe.h ├── input ├── guncon.c ├── guncon.h ├── sda.c └── sda.h ├── log.c ├── log.h ├── psx.c └── psx.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: allkern 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | runs-on: macos-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | - name: Install SDL2 and dylibbundler 16 | run: | 17 | brew install sdl2 18 | brew install dylibbundler 19 | - name: Build and pack PSXE 20 | run: | 21 | git fetch --all --tags 22 | ./build.sh 23 | tar -czf psxe-macos-latest.tar.gz psxe.app 24 | - uses: actions/upload-artifact@v4 25 | with: 26 | name: psxe-macos-latest 27 | path: ./psxe-macos-latest.tar.gz 28 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | - name: Install SDL2 16 | run: | 17 | sudo apt update 18 | sudo apt install libsdl2-dev 19 | - name: Build PSXE 20 | run: | 21 | git fetch --all --tags 22 | make 23 | - name: Pack executable 24 | run: | 25 | chmod +x ./bin/psxe 26 | mv ./bin/psxe ./ 27 | tar -czf psxe-ubuntu-latest.tar.gz ./psxe 28 | - uses: actions/upload-artifact@v4 29 | with: 30 | name: psxe-ubuntu-latest 31 | path: ./psxe-ubuntu-latest.tar.gz 32 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | jobs: 8 | build: 9 | runs-on: windows-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | - name: Run build-deps 16 | run: ./build-deps.ps1 17 | - name: Run build-win64 18 | run: | 19 | ./build-win64.ps1 20 | New-Item -Path "psxe" -ItemType Directory 21 | Copy-Item -Recurse "bin" -Destination "psxe" 22 | - uses: actions/upload-artifact@v4 23 | with: 24 | name: psxe-win64-latest 25 | path: psxe/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | bin/ 3 | build-win32/ 4 | imgui/ 5 | res/ 6 | sdl2/ 7 | SDL2-2.30.3/ 8 | sdl2-win32/ 9 | sdl2-win64/ 10 | system573/ 11 | psyq/ 12 | test/ 13 | bios/ 14 | roms/ 15 | snap/ 16 | *.BIN 17 | *.bin 18 | *.dll 19 | *.exe 20 | *.out 21 | *.toml 22 | *.zip 23 | *.cue 24 | *.iso 25 | *.mcd 26 | *.rom 27 | *.o -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | psxe 9 | CFBundleIdentifier 10 | psxe.app.0 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | psxe 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 0.1-alpha 19 | CFBundleSignature 20 | psxe 21 | CFBundleVersion 22 | 0.1-alpha 23 | LSApplicationCategoryType 24 | public.app-category.games 25 | LSMinimumSystemVersion 26 | 10.6 27 | NSHighResolutionCapable 28 | 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Allkern (Lisandro Alarcón) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .ONESHELL: 2 | 3 | CFLAGS := -g -DLOG_USE_COLOR `sdl2-config --cflags --libs` 4 | CFLAGS += -Ofast -Wno-overflow -Wall -pedantic -Wno-address-of-packed-member -flto 5 | 6 | PLATFORM := $(shell uname -s) 7 | 8 | ifeq ($(PLATFORM),Darwin) 9 | CFLAGS += -mmacosx-version-min=10.9 -Wno-newline-eof 10 | endif 11 | 12 | VERSION_TAG := $(shell git describe --always --tags --abbrev=0) 13 | COMMIT_HASH := $(shell git rev-parse --short HEAD) 14 | OS_INFO := $(shell uname -rmo) 15 | 16 | SOURCES := $(wildcard psx/*.c) 17 | SOURCES += $(wildcard psx/dev/*.c) 18 | SOURCES += $(wildcard psx/dev/cdrom/*.c) 19 | SOURCES += $(wildcard psx/input/*.c) 20 | SOURCES += $(wildcard psx/disc/*.c) 21 | SOURCES += $(wildcard frontend/*.c) 22 | 23 | bin/psxe frontend/main.c: 24 | mkdir -p bin 25 | 26 | gcc $(SOURCES) -o bin/psxe \ 27 | -I"." \ 28 | -I"psx" \ 29 | -DOS_INFO="$(OS_INFO)" \ 30 | -DREP_VERSION="$(VERSION_TAG)" \ 31 | -DREP_COMMIT_HASH="$(COMMIT_HASH)" \ 32 | $(CFLAGS) 33 | 34 | clean: 35 | rm -rf "bin" 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # psxe 2 | A simple and portable Sony PlayStation emulator and emulation library written in C 3 | 4 | ## Screenshots 5 | | Windows | Ubuntu | macOS | 6 | | ------------- | ------------- | ------------- 7 | | ![Mega Man X6 (USA)](https://github.com/allkern/psxe/assets/15825466/34dde8f9-eedb-4b44-a08d-c17026df2ff2) | ![Bloody Roar 2 - Bringer of the New Age (Europe)](https://github.com/allkern/psxe/assets/15825466/41a6dc67-b0ba-442f-bed6-7b207c0db4dd) | ![Parodius (Europe)](https://github.com/allkern/psxe/assets/15825466/9ab291d9-ec47-4997-92d3-23e38982ae45) | 8 | | ![Spyro 2 - Ripto's Rage (USA)](https://github.com/allkern/psxe/assets/15825466/e161ab66-af57-4327-9a94-8b2591a0012a) | ![Namco Museum Vol. 1 (USA)](https://github.com/allkern/psxe/assets/15825466/67ea61e4-5f30-470c-a978-23e0755850b6) | ![Darius Gaiden (Japan)](https://github.com/allkern/psxe/assets/15825466/0c55118c-ab42-40e5-b34a-7594528080bf) | 9 | 10 | ### CI status 11 | ![Windows](https://github.com/allkern/psx/actions/workflows/windows.yml/badge.svg) 12 | ![macOS](https://github.com/allkern/psx/actions/workflows/macos.yml/badge.svg) 13 | ![Ubuntu](https://github.com/allkern/psx/actions/workflows/ubuntu.yml/badge.svg) 14 | 15 | ## Running 16 | You can download the latest automated build for your platform on Releases. If your system isn't supported, you can easily build the emulator from source, instructions on "Building" below. 17 | 18 | In order to run the emulator, you will need a BIOS file. You can either get one from the internet or [dump it from your own console](https://www.youtube.com/watch?v=u8eHp0COcBo). 19 | 20 | Most BIOS versions are confirmed to work. 21 | 22 | Use the `-b` or `--bios` setting to configure the BIOS file. 23 | 24 | ## Progress 25 | All components have been implemented, Memory card support is temporarily disabled. 26 | 27 | CPU
28 | DMA
29 | GPU
30 | SPU
31 | MDEC
32 | GTE
33 | Timers
34 | CDROM
35 | Memory cards
36 | 37 | ## Building 38 | Building the emulator should be easy, just use the scripts provided in this repo. 39 | 40 | On Windows, the `build-deps.ps1` script downloads SDL2 and unzips it. If you want to run the emulator standalone, you will have to move the SDL2 DLL to the same folder where the executable is located. 41 | 42 | **If you already have SDL2 on your system**, you can skip running `build-deps.ps1`. Though you will have to edit `build-win.ps1` to point the `SDL2_DIR` variable to your installation path. 43 | 44 | On Ubuntu, you will also need to install `libsdl2-dev`, you can get it from `apt` like so: 45 | ``` 46 | sudo apt update 47 | sudo apt upgrade 48 | sudo apt install libsdl2-dev 49 | ``` 50 | 51 | Building on macOS requires installing SDL2 and dylibbundler, this can be done using `brew`: 52 | ``` 53 | brew install sdl2 54 | brew install dylibbundler 55 | ``` 56 | 57 | Assuming you did everything described above, you should be able to build the emulator by using the following commands. 58 | 59 | ### Windows 60 | ``` 61 | git clone https://github.com/allkern/psxe 62 | cd psxe 63 | ./build-deps 64 | ./build-win64.ps1 65 | ``` 66 | On rare cases these scripts might not work (PowerShell/Windows bugs). If so, please open an issue on the issues tab with information about your system so we can make sure we cover the maximum amount of systems. 67 | 68 | ### Ubuntu 69 | ``` 70 | git clone https://github.com/allkern/psxe 71 | cd psxe 72 | make clean && make 73 | ``` 74 | 75 | ### macOS 76 | ``` 77 | git clone https://github.com/allkern/psxe 78 | cd psxe 79 | ./build.sh 80 | ``` 81 | 82 | ## Acknowledgements 83 | This project uses external open source code that can be found on the following GitHub repos: 84 | - argparse.c: https://github.com/cofyc/argparse 85 | - log.c (slightly modified): https://github.com/rxi/log.c 86 | - tomlc99: https://github.com/cktan/tomlc99 87 | 88 | Their original licenses are respected and apply to the code in this project. 89 | 90 | As always, thanks to all original developers for their amazing work. 91 | -------------------------------------------------------------------------------- /build-clean.ps1: -------------------------------------------------------------------------------- 1 | Remove-Item -Recurse bin -------------------------------------------------------------------------------- /build-deps.ps1: -------------------------------------------------------------------------------- 1 | if (Test-Path "SDL2-2.30.3") { 2 | Remove-Item -Recurse "SDL2-2.30.3" 3 | } 4 | 5 | $SDL2_URL = "https://github.com/libsdl-org/SDL/releases/download/release-2.30.3/SDL2-devel-2.30.3-mingw.zip" 6 | 7 | Invoke-WebRequest -URI $SDL2_URL -OutFile "sdl2.zip" 8 | Expand-Archive "sdl2.zip" -DestinationPath "." -Force 9 | 10 | Remove-Item "sdl2.zip" -------------------------------------------------------------------------------- /build-win32.ps1: -------------------------------------------------------------------------------- 1 | git fetch --all --tags 2 | 3 | $VERSION_TAG = git describe --always --tags --abbrev=0 4 | $COMMIT_HASH = git rev-parse --short HEAD 5 | $OS_INFO = (Get-WMIObject win32_operatingsystem).caption + " " + ` 6 | (Get-WMIObject win32_operatingsystem).version + " " + ` 7 | (Get-WMIObject win32_operatingsystem).OSArchitecture 8 | 9 | $SDL2_DIR = "SDL2-2.30.3\i686-w64-mingw32" 10 | $PSX_DIR = "." 11 | 12 | mkdir -Force -Path bin > $null 13 | 14 | gcc -I"$($PSX_DIR)" ` 15 | -I"$($PSX_DIR)\psx" ` 16 | -I"$($SDL2_DIR)\include\SDL2" ` 17 | "psx\*.c" ` 18 | "psx\dev\*.c" ` 19 | "psx\input\*.c" ` 20 | "psx\disc\*.c" ` 21 | "frontend\*.c" ` 22 | -o "bin\psxe.exe" ` 23 | -DREP_VERSION="`"$($VERSION_TAG)`"" ` 24 | -DREP_COMMIT_HASH="`"$($COMMIT_HASH)`"" ` 25 | -DOS_INFO="`"$($OS_INFO)`"" ` 26 | -L"$($SDL2_DIR)\lib" ` 27 | -lSDL2main -lSDL2 -Wno-overflow ` 28 | -Wall -pedantic -DLOG_USE_COLOR ` 29 | -Wno-address-of-packed-member ` 30 | -ffast-math -Ofast -g -flto 31 | 32 | Copy-Item -Path "$($SDL2_DIR)\bin\SDL2.dll" -Destination "bin" -------------------------------------------------------------------------------- /build-win64.ps1: -------------------------------------------------------------------------------- 1 | git fetch --all --tags 2 | 3 | $VERSION_TAG = git describe --always --tags --abbrev=0 4 | $COMMIT_HASH = git rev-parse --short HEAD 5 | $OS_INFO = (Get-WMIObject win32_operatingsystem).caption + " " + ` 6 | (Get-WMIObject win32_operatingsystem).version + " " + ` 7 | (Get-WMIObject win32_operatingsystem).OSArchitecture 8 | 9 | $SDL2_DIR = "SDL2-2.30.3\x86_64-w64-mingw32" 10 | $PSX_DIR = "." 11 | 12 | mkdir -Force -Path bin > $null 13 | 14 | gcc -I"$($PSX_DIR)" ` 15 | -I"$($PSX_DIR)\psx" ` 16 | -I"$($SDL2_DIR)\include\SDL2" ` 17 | "psx\*.c" ` 18 | "psx\dev\*.c" ` 19 | "psx\dev\cdrom\*.c" ` 20 | "psx\input\*.c" ` 21 | "frontend\*.c" ` 22 | -o "bin\psxe.exe" ` 23 | -DREP_VERSION="`"$($VERSION_TAG)`"" ` 24 | -DREP_COMMIT_HASH="`"$($COMMIT_HASH)`"" ` 25 | -DOS_INFO="`"$($OS_INFO)`"" ` 26 | -L"$($SDL2_DIR)\lib" ` 27 | -m64 -lSDL2main -lSDL2 -Wno-overflow ` 28 | -Wall -pedantic -DLOG_USE_COLOR ` 29 | -Wno-address-of-packed-member ` 30 | -ffast-math -Ofast -g -flto -Werror 31 | 32 | Copy-Item -Path "$($SDL2_DIR)\bin\SDL2.dll" -Destination "bin" -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Build emulator 4 | make clean && make 5 | 6 | # Create bundle filesystem 7 | mkdir -p psxe.app/Contents/MacOS/Libraries 8 | 9 | # Move executable to folder 10 | mv bin/psxe psxe.app/Contents/MacOS 11 | 12 | # Make executable 13 | chmod 777 psxe.app/Contents/MacOS/psxe 14 | 15 | # Bundle required dylibs 16 | dylibbundler -b -x ./psxe.app/Contents/MacOS/psxe -d ./psxe.app/Contents/Libraries/ -p @executable_path/../Libraries/ -cd 17 | 18 | # Move plist to Contents folder 19 | mv Info.plist psxe.app/Contents/Info.plist -------------------------------------------------------------------------------- /compat.txt: -------------------------------------------------------------------------------- 1 | Not working: 2 | Fixed: Aconcagua (Japan) (Disc 1) *6 3 | Fixed: Animetic Story Game 1 - Card Captor Sakura (Japan) (Disc 1) *1 (2989bb6) 4 | Fixed: Ape Escape (USA) *21 (0=10000h CDROM DMA BS) 5 | Fixed: Blade (USA) *3 6 | Fixed: CTR - Crash Team Racing (USA) *16 (regression) (f7e41cf) 7 | Fixed: Crash Bash (USA) *1 (2989bb6) 8 | Fixed: Crime Crackers (Japan) *20 (e145a4e) 9 | Fixed: Disney's Hercules Action Game (USA) *22 10 | Fixed: Disney's The Emperor's New Groove (USA).cue *22 11 | Fixed: Doom (All) *4 (e145a4e) 12 | Fixed: Elder Gate (Japan) *1 (2989bb6) 13 | Fixed: Fear Effect (USA) (Disc 1) *3 14 | Fixed: Final Doom (All) *4 (e145a4e) 15 | Fixed: Final Fantasy IX (USA) (Disc 1) *22 16 | Fixed: Gran Turismo 2 - Music at the Speed of Sound - The Album (USA) (Bonus PlayStation Disc) *19 (regression) 17 | Fixed: Hello Kitty - Cube Frenzy (USA) *22 (FMVs are very slow though) 18 | Fixed: Initial D (Japan) *22 19 | Fixed: Konami 80's Arcade Gallery (Japan) *1 (2989bb6) 20 | Nagano Winter Olympics '98 (USA) *18 21 | Need for Speed II (USA) *17 22 | Over Drivin' - Skyline Memorial (Japan) + Nissan Presents - Over Drivin' - Skyline Memorial (Japan) *7 23 | PS One Winter 03 Kiosk Version 1.21 (USA) *13 24 | PaRappa the Rapper (Japan) (USA version works) *16 25 | Fixed: Paro Wars (Japan) *12 (e145a4e) 26 | Fixed: Pet in TV - With My Dear Dog (Japan) *1 (2989bb6) 27 | Fixed: Philosoma (USA) *15 28 | Fixed: PoPoRoGue (Japan) *14 (2989bb6) 29 | Fixed: RayStorm (USA) + RayCrisis (Japan) + RayCrisis - Series Termination (USA) *12 30 | Fixed: Rockman (Japan) *1 (2989bb6) 31 | S.C.A.R.S (USA) *11 32 | SimCity 2000 (USA) *3 33 | Sister Princess 2 - Premium Fan Disc (Japan) (Disc A) *2 (regression) 34 | Star Wars - Rebel Assault II - The Hidden Empire (USA) (Disc 1) *9 35 | Fixed: Strider Hiryuu 1 & 2 (Japan) (Disc 1) (Strider Hiryuu) *6 (2989bb6) 36 | Tama - Adventurous Ball in Giddy Labyrinth (Japan) *5 37 | Fixed: Time Gal & Ninja Hayate (Japan) (En,Ja) (Disc 1) (Time Gal) *9 38 | Fixed: Time Gal & Ninja Hayate (Japan) (En,Ja) (Disc 2) (Ninja Hayate) *9 39 | Fixed: Tokimeki Memorial 2 (Japan) *1 (2989bb6) 40 | Fixed: Tomb Raider (USA) *8 (e145a4e) 41 | Fixed: Tony Hawk's Pro Skater (USA) *22 42 | Fixed: Vib-Ribbon (Japan) *8 (f7e41cf) 43 | WipeOut (USA) *7 44 | Fixed: Yu-Gi-Oh! Forbidden Memories (USA) *1 (2989bb6) 45 | 46 | Notes: 47 | *1 Hangs after CdlGetTN 48 | *2 Hangs while loading (possibly timer related) 49 | *3 Hangs after XA ReadS (MDEC playback (flags?) related, fixable) 50 | *4 Hangs after CdlPlay (timer? CDROM?) 51 | *5 Hangs after CdlSetSession (timeout) 52 | *6 Hangs on a retry deadloop, non-v0 command used (CdlReadTOC) 53 | *7 Hangs after SIO1 accesses 54 | *8 CdlGetlocP deadlock 55 | *9 Hangs on MDEC playback (tries, but dies) 56 | *10 Sector read errors (can't recover) 57 | *11 Input not working 58 | *12 Hang on a retry deadloop, ReadN XA playback? 59 | *13 Requires special hardware 60 | *14 Hangs after CdlTest 61 | *15 Hangs after CdlSetloc (early) 62 | *16 Hangs on XA playback 63 | *17 Hangs on sector error retry deadloop 64 | *18 Hangs on MDEC playback (Getstat deadloop) 65 | *19 Hangs on CdlSetloc deadloop 66 | *20 GetlocP loop hangs 67 | *21 Hangs after 0-sized CDROM DMA 68 | *22 MDEC corruption (fixed, XA resample buffer overflow) 69 | 70 | Graphical issues: 71 | Most graphical issues (black models, 1-pixel seams, polygon explosions, etc.) have been fixed! 72 | 73 | Silent Hill requires mask bit emulation, otherwise, semi-transparent white boxes appear on top of 74 | models/sprites 75 | 76 | Sound/music issues: 77 | Some games might play incorrect CDDA tracks, this is due to a bugfix that introduced slight track LBA deviations in our CUE loading code. 78 | -------------------------------------------------------------------------------- /frontend/argparse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #ifndef ARGPARSE_H 9 | #define ARGPARSE_H 10 | 11 | /* For c++ compatibility */ 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | 18 | struct argparse; 19 | struct argparse_option; 20 | 21 | typedef int argparse_callback (struct argparse *self, 22 | const struct argparse_option *option); 23 | 24 | enum argparse_flag { 25 | ARGPARSE_STOP_AT_NON_OPTION = 1 << 0, 26 | ARGPARSE_IGNORE_UNKNOWN_ARGS = 1 << 1, 27 | }; 28 | 29 | enum argparse_option_type { 30 | /* special */ 31 | ARGPARSE_OPT_END, 32 | ARGPARSE_OPT_GROUP, 33 | /* options with no arguments */ 34 | ARGPARSE_OPT_BOOLEAN, 35 | ARGPARSE_OPT_BIT, 36 | /* options with arguments (optional or required) */ 37 | ARGPARSE_OPT_INTEGER, 38 | ARGPARSE_OPT_FLOAT, 39 | ARGPARSE_OPT_STRING, 40 | }; 41 | 42 | enum argparse_option_flags { 43 | OPT_NONEG = 1, /* disable negation */ 44 | }; 45 | 46 | /** 47 | * argparse option 48 | * 49 | * `type`: 50 | * holds the type of the option, you must have an ARGPARSE_OPT_END last in your 51 | * array. 52 | * 53 | * `short_name`: 54 | * the character to use as a short option name, '\0' if none. 55 | * 56 | * `long_name`: 57 | * the long option name, without the leading dash, NULL if none. 58 | * 59 | * `value`: 60 | * stores pointer to the value to be filled. 61 | * 62 | * `help`: 63 | * the short help message associated to what the option does. 64 | * Must never be NULL (except for ARGPARSE_OPT_END). 65 | * 66 | * `callback`: 67 | * function is called when corresponding argument is parsed. 68 | * 69 | * `data`: 70 | * associated data. Callbacks can use it like they want. 71 | * 72 | * `flags`: 73 | * option flags. 74 | */ 75 | struct argparse_option { 76 | enum argparse_option_type type; 77 | const char short_name; 78 | const char *long_name; 79 | void *value; 80 | const char *help; 81 | argparse_callback *callback; 82 | intptr_t data; 83 | int flags; 84 | }; 85 | 86 | /** 87 | * argpparse 88 | */ 89 | struct argparse { 90 | // user supplied 91 | const struct argparse_option *options; 92 | const char *const *usages; 93 | int flags; 94 | const char *description; // a description after usage 95 | const char *epilog; // a description at the end 96 | // internal context 97 | int argc; 98 | const char **argv; 99 | const char **out; 100 | int cpidx; 101 | const char *optvalue; // current option value 102 | }; 103 | 104 | // built-in callbacks 105 | int argparse_help_cb(struct argparse *self, 106 | const struct argparse_option *option); 107 | int argparse_help_cb_no_exit(struct argparse *self, 108 | const struct argparse_option *option); 109 | 110 | // built-in option macros 111 | #define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } 112 | #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } 113 | #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } 114 | #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } 115 | #define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } 116 | #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } 117 | #define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } 118 | #define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ 119 | "show this help message and exit", \ 120 | argparse_help_cb, 0, OPT_NONEG) 121 | 122 | int argparse_init(struct argparse *self, struct argparse_option *options, 123 | const char *const *usages, int flags); 124 | void argparse_describe(struct argparse *self, const char *description, 125 | const char *epilog); 126 | int argparse_parse(struct argparse *self, int argc, const char **argv); 127 | void argparse_usage(struct argparse *self); 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /frontend/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #ifndef OS_INFO 5 | #define OS_INFO unknown 6 | #endif 7 | #ifndef REP_VERSION 8 | #define REP_VERSION latest 9 | #endif 10 | #ifndef REP_COMMIT_HASH 11 | #define REP_COMMIT_HASH latest 12 | #endif 13 | 14 | #define STR1(m) #m 15 | #define STR(m) STR1(m) 16 | 17 | #endif -------------------------------------------------------------------------------- /frontend/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "config.h" 5 | #include "common.h" 6 | 7 | #include "../psx/log.h" 8 | 9 | static const char* g_version_text = 10 | #ifdef _WIN32 11 | "psxe.exe (" STR(OS_INFO) ") " STR(REP_VERSION) "-" STR(REP_COMMIT_HASH) "\n" 12 | #elif __linux__ 13 | "psxe (" STR(OS_INFO) ") " STR(REP_VERSION) "-" STR(REP_COMMIT_HASH) "\n" 14 | #else 15 | "psxe (" STR(OS_INFO) ") " STR(REP_VERSION) "-" STR(REP_COMMIT_HASH) "\n" 16 | #endif 17 | "\nPSXE - A simple, fast and portable Sony PlayStation emulator.\n\n" 18 | "Copyright (C) 2023 Allkern (Lisandro Alarcon)\n" 19 | "This is free software; see the source for copying conditions. There is NO\n" 20 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; 21 | 22 | static const char g_default_settings[] = 23 | "# Settings file generated by PSXE\n" 24 | "\n" 25 | "# Don't change please, reserved for future use\n" 26 | "psxe_version = \"" STR(REP_VERSION) "\"\n" 27 | "\n" 28 | "# BIOS-related settings:\n" 29 | "[bios]\n" 30 | " search_path = \"bios\"\n" 31 | " preferred_model = \"SCPH-1001\"\n" 32 | " override_file = \"\"\n" 33 | "\n" 34 | "# Console settings\n" 35 | "[console]\n" 36 | " region = \"auto\"\n"; 37 | 38 | static const char* g_models_text = 39 | "Available console models:\n" 40 | "\"scph1000\" (SCPH-1000) [NTSC-J]\n" 41 | "\"scph1001\" (SCPH-1001) [NTSC-U/C]\n" 42 | "\"scph1002\" (SCPH-1002) [PAL]\n" 43 | "\"scph3000\" (SCPH-3000) [NTSC-J]\n" 44 | "\"scph3500\" (SCPH-3500) [NTSC-J]\n" 45 | "\"scph5000\" (SCPH-5000) [NTSC-U/C]\n" 46 | "\"scph5501\" (SCPH-5501) [NTSC-U/C]\n" 47 | "\"scph5500\" (SCPH-5500) [NTSC-J]\n" 48 | "\"scph5502\" (SCPH-5502) [PAL]\n" 49 | "\"scph5552\" (SCPH-5552) [PAL]\n" 50 | "\"scph7000\" (SCPH-7000) [NTSC-J]\n" 51 | "\"scph7001\" (SCPH-7001) [NTSC-U/C]\n" 52 | "\"scph7002\" (SCPH-7002) [PAL]\n" 53 | "\"scph7003\" (SCPH-7003) [NTSC-J]\n" 54 | "\"scph7501\" (SCPH-7501) [NTSC]\n" 55 | "\"scph7502\" (SCPH-7502) [PAL]\n" 56 | "\"scph9002\" (SCPH-9002) [PAL]\n" 57 | "\"scph100\" (SCPH-100) [NTSC-J]\n" 58 | "\"scph101\" (SCPH-101) [NTSC-U/C]\n" 59 | "\"scph102a\" (SCPH-102A) [PAL]\n" 60 | "\"scph102b\" (SCPH-102B) [PAL]\n" 61 | "\"scph102c\" (SCPH-102C) [?]\n"; 62 | 63 | static const char* g_regions_text = 64 | "Available region options: \"ntsc\", \"pal\", \"auto\"\n"; 65 | 66 | static const char* g_desc_text = 67 | "\nPlease report any bugs to \n"; 68 | 69 | psxe_config_t* psxe_cfg_create(void) { 70 | return (psxe_config_t*)malloc(sizeof(psxe_config_t)); 71 | } 72 | 73 | void psxe_cfg_init(psxe_config_t* cfg) { 74 | memset(cfg, 0, sizeof(psxe_config_t)); 75 | } 76 | 77 | void psxe_cfg_destroy(psxe_config_t* cfg) { 78 | free(cfg); 79 | } 80 | 81 | void psxe_cfg_load_defaults(psxe_config_t* cfg) { 82 | cfg->bios = "bios.bin"; 83 | cfg->bios_search = "bios"; 84 | cfg->exe = NULL; 85 | cfg->help_model = 0; 86 | cfg->help_region = 0; 87 | cfg->model = "scph1001"; 88 | cfg->scale = 3; 89 | cfg->psxe_version = STR(REP_VERSION); 90 | cfg->region = "ntsc"; 91 | cfg->settings_path = NULL; 92 | cfg->use_args = 0; 93 | cfg->version = 0; 94 | cfg->log_level = LOG_FATAL; 95 | cfg->quiet = 0; 96 | cfg->cd_path = NULL; 97 | cfg->exp_path = NULL; 98 | } 99 | 100 | void psxe_cfg_load(psxe_config_t* cfg, int argc, const char* argv[]) { 101 | log_set_level(LOG_INFO); 102 | 103 | int use_args = 0; 104 | int version = 0; 105 | int help_model = 0; 106 | int help_region = 0; 107 | int log_level = 0; 108 | int quiet = 0; 109 | int console_source = 0; 110 | int scale = 0; 111 | const char* settings_path = NULL; 112 | const char* bios = NULL; 113 | const char* bios_search = NULL; 114 | const char* model = NULL; 115 | const char* exe = NULL; 116 | const char* region = NULL; 117 | const char* psxe_version = NULL; 118 | const char* cd_path = NULL; 119 | const char* exp_path = NULL; 120 | 121 | static const char *const usages[] = { 122 | "psxe [options] path-to-cdrom", 123 | NULL, 124 | }; 125 | 126 | struct argparse_option options[] = { 127 | OPT_BOOLEAN ('h', "help" , NULL , "Display this information", argparse_help_cb, 0, 0), 128 | OPT_BOOLEAN (0 , "help-model" , &help_model , "Display available console models", NULL, 0, 0), 129 | OPT_BOOLEAN (0 , "help-region" , &help_region , "Display available region options", NULL, 0, 0), 130 | OPT_BOOLEAN ('v', "version" , &version , "Display version and build information", NULL, 0, 0), 131 | OPT_GROUP("Basic options"), 132 | OPT_BOOLEAN ('a', "use-args" , &use_args , "Ignore settings file, use CLI args instead", NULL, 0, 0), 133 | OPT_STRING ('b', "bios" , &bios , "Specify a BIOS file (ignores -B, -M)", NULL, 0, 0), 134 | OPT_BOOLEAN ('B', "bios-folder" , &bios_search , "Specify a BIOS search folder", NULL, 0, 0), 135 | OPT_STRING ('c', "console-source", &console_source, "Select console source (auto, null, kernel, atcons)"), 136 | OPT_STRING ('e', "exp-rom" , &exp_path , "Specify an expansion ROM file"), 137 | OPT_INTEGER ('L', "log-level" , &log_level , "Set log level"), 138 | OPT_STRING ('M', "model" , &model , "Specify console model (SPCH-XXXX)", NULL, 0, 0), 139 | OPT_STRING ('r', "region" , ®ion , "Specify console region"), 140 | OPT_INTEGER ('s', "scale" , &scale , "Display scaling factor", NULL, 0, 0), 141 | OPT_STRING ('S', "settings-file" , &settings_path , "Specify settings file path", NULL, 0, 0), 142 | OPT_BOOLEAN ('q', "quiet" , &quiet , "Silence all logs (ignores -L)"), 143 | OPT_STRING ('x', "exe" , &exe , "Launch a PS-X EXE file"), 144 | OPT_STRING (0 , "cdrom" , &cd_path , "Specify a CDROM image"), 145 | OPT_END() 146 | }; 147 | 148 | struct argparse argparse; 149 | 150 | argparse_init(&argparse, options, usages, 0); 151 | argparse_describe(&argparse, NULL, g_desc_text); 152 | 153 | argc = argparse_parse(&argparse, argc, argv); 154 | 155 | if (help_model) { 156 | printf("%s\n", g_models_text); 157 | 158 | exit(0); 159 | } 160 | 161 | if (help_region) { 162 | printf("%s\n", g_regions_text); 163 | 164 | exit(0); 165 | } 166 | 167 | if (version) { 168 | printf("%s\n", g_version_text); 169 | 170 | exit(0); 171 | } 172 | 173 | log_set_quiet(quiet); 174 | 175 | if (!use_args) { 176 | if (!settings_path) 177 | settings_path = "settings.toml"; 178 | 179 | FILE* settings = fopen(settings_path, "rb"); 180 | 181 | char error[0x100]; 182 | 183 | if (!settings) { 184 | settings = fopen("settings.toml", "w+b"); 185 | 186 | if (!settings) { 187 | log_error("Couldn't create settings file, loading default settings"); 188 | 189 | psxe_cfg_load_defaults(cfg); 190 | 191 | return; 192 | } 193 | 194 | fwrite(g_default_settings, 1, sizeof(g_default_settings) - 1, settings); 195 | 196 | fseek(settings, 0, 0); 197 | } 198 | 199 | log_info("Parsing settings file..."); 200 | 201 | toml_table_t* conf = toml_parse_file(settings, error, sizeof(error)); 202 | 203 | if (!conf) { 204 | log_error("Couldn't parse settings file"); 205 | 206 | exit(1); 207 | } 208 | 209 | toml_datum_t s_version = toml_string_in(conf, "psxe_version"); 210 | 211 | if (!s_version.ok) { 212 | log_error("Settings file lacking version number"); 213 | 214 | exit(1); 215 | } 216 | 217 | toml_table_t* s_bios_table = toml_table_in(conf, "bios"); 218 | 219 | if (s_bios_table) { 220 | toml_datum_t s_bios_search_path = toml_string_in(s_bios_table, "search_path"); 221 | 222 | if (s_bios_search_path.ok) 223 | bios_search = s_bios_search_path.u.s; 224 | 225 | toml_datum_t s_bios_preferred_model = toml_string_in(s_bios_table, "preferred_model"); 226 | 227 | if (s_bios_preferred_model.ok) 228 | model = s_bios_preferred_model.u.s; 229 | 230 | toml_datum_t s_bios_override_file = toml_string_in(s_bios_table, "override_file"); 231 | 232 | if (s_bios_override_file.ok) 233 | bios = s_bios_override_file.u.s; 234 | } 235 | 236 | toml_table_t* s_console_table = toml_table_in(conf, "console"); 237 | 238 | if (s_console_table) { 239 | toml_datum_t s_console_region = toml_string_in(s_console_table, "region"); 240 | 241 | if (s_console_region.ok) 242 | region = s_console_region.u.s; 243 | } 244 | 245 | psxe_version = s_version.u.s; 246 | 247 | log_info("Settings file parsed. PSXE version: %s", psxe_version); 248 | 249 | fclose(settings); 250 | } 251 | 252 | if (argc) { 253 | if (argc > 1) { 254 | log_error("Unrecognized parameter \'%s\'", argv[1]); 255 | 256 | exit(1); 257 | } 258 | 259 | cd_path = argv[0]; 260 | } 261 | 262 | if (cd_path) 263 | cfg->cd_path = cd_path; 264 | 265 | if (log_level) 266 | cfg->log_level = log_level - 1; 267 | 268 | if (bios) 269 | cfg->bios = bios; 270 | 271 | if (bios_search) 272 | cfg->bios_search = bios_search; 273 | 274 | if (model) 275 | cfg->model = model; 276 | 277 | if (exe) 278 | cfg->exe = exe; 279 | 280 | if (region) 281 | cfg->region = region; 282 | 283 | if (psxe_version) 284 | cfg->psxe_version = psxe_version; 285 | 286 | if (exp_path) 287 | cfg->exp_path = exp_path; 288 | 289 | if (scale) 290 | cfg->scale = scale; 291 | } 292 | 293 | // To-do: Implement BIOS searching 294 | char* psxe_cfg_get_bios_path(psxe_config_t* cfg) { 295 | return NULL; 296 | } 297 | 298 | #undef STR1 299 | #undef STR -------------------------------------------------------------------------------- /frontend/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #include 5 | 6 | #include "argparse.h" 7 | #include "toml.h" 8 | 9 | typedef struct { 10 | int use_args; 11 | int version; 12 | int help_model; 13 | int help_region; 14 | int log_level; 15 | int quiet; 16 | int console_source; 17 | int scale; 18 | const char* snap_path; 19 | const char* settings_path; 20 | const char* bios; 21 | const char* bios_search; 22 | const char* model; 23 | const char* exe; 24 | const char* region; 25 | const char* psxe_version; 26 | const char* cd_path; 27 | const char* exp_path; 28 | } psxe_config_t; 29 | 30 | psxe_config_t* psxe_cfg_create(void); 31 | void psxe_cfg_init(psxe_config_t*); 32 | void psxe_cfg_load_defaults(psxe_config_t*); 33 | void psxe_cfg_load(psxe_config_t*, int, const char**); 34 | char* psxe_cfg_get_bios_path(psxe_config_t*); 35 | void psxe_cfg_destroy(psxe_config_t*); 36 | 37 | #endif -------------------------------------------------------------------------------- /frontend/main.c: -------------------------------------------------------------------------------- 1 | #include "../psx/psx.h" 2 | #include "../psx/input/sda.h" 3 | #include "../psx/input/guncon.h" 4 | #include "../psx/dev/cdrom/cdrom.h" 5 | 6 | #include "screen.h" 7 | #include "config.h" 8 | 9 | #undef main 10 | 11 | void audio_update(void* ud, uint8_t* buf, int size) { 12 | psx_cdrom_t* cdrom = ((psx_t*)ud)->cdrom; 13 | psx_spu_t* spu = ((psx_t*)ud)->spu; 14 | 15 | memset(buf, 0, size); 16 | 17 | psx_cdrom_get_audio_samples(cdrom, buf, size); 18 | psx_spu_update_cdda_buffer(spu, cdrom->cdda_buf); 19 | 20 | for (int i = 0; i < (size >> 2); i++) { 21 | uint32_t sample = psx_spu_get_sample(spu); 22 | 23 | int16_t left = (int16_t)(sample & 0xffff); 24 | int16_t right = (int16_t)(sample >> 16); 25 | 26 | *(int16_t*)(&buf[(i << 2) + 0]) += left; 27 | *(int16_t*)(&buf[(i << 2) + 2]) += right; 28 | } 29 | } 30 | 31 | int main(int argc, const char* argv[]) { 32 | psxe_config_t* cfg = psxe_cfg_create(); 33 | 34 | psxe_cfg_init(cfg); 35 | psxe_cfg_load_defaults(cfg); 36 | psxe_cfg_load(cfg, argc, argv); 37 | 38 | log_set_level(cfg->log_level); 39 | 40 | psx_t* psx = psx_create(); 41 | psx_init(psx, cfg->bios, cfg->exp_path); 42 | 43 | psx_cdrom_t* cdrom = psx_get_cdrom(psx); 44 | 45 | // To-do: Set CDROM firmware version and region based 46 | // on CLI options 47 | 48 | if (cfg->cd_path) 49 | psx_cdrom_open(cdrom, cfg->cd_path); 50 | 51 | psxe_screen_t* screen = psxe_screen_create(); 52 | psxe_screen_init(screen, psx); 53 | psxe_screen_set_scale(screen, cfg->scale); 54 | psxe_screen_reload(screen); 55 | 56 | SDL_Init(SDL_INIT_AUDIO); 57 | 58 | SDL_AudioDeviceID dev; 59 | SDL_AudioSpec obtained, desired; 60 | 61 | desired.freq = 44100; 62 | desired.format = AUDIO_S16SYS; 63 | desired.channels = 2; 64 | desired.samples = CD_SECTOR_SIZE >> 2; 65 | desired.callback = &audio_update; 66 | desired.userdata = psx; 67 | 68 | dev = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0); 69 | 70 | if (dev) 71 | SDL_PauseAudioDevice(dev, 0); 72 | 73 | psx_gpu_t* gpu = psx_get_gpu(psx); 74 | psx_gpu_set_event_callback(gpu, GPU_EVENT_DMODE, psxe_gpu_dmode_event_cb); 75 | psx_gpu_set_event_callback(gpu, GPU_EVENT_VBLANK, psxe_gpu_vblank_event_cb); 76 | psx_gpu_set_event_callback(gpu, GPU_EVENT_HBLANK, psxe_gpu_hblank_event_cb); 77 | psx_gpu_set_event_callback(gpu, GPU_EVENT_VBLANK_END, psxe_gpu_vblank_end_event_cb); 78 | psx_gpu_set_event_callback(gpu, GPU_EVENT_HBLANK_END, psxe_gpu_hblank_end_event_cb); 79 | psx_gpu_set_udata(gpu, 0, screen); 80 | psx_gpu_set_udata(gpu, 1, psx->timer); 81 | 82 | psx_input_t* input = psx_input_create(); 83 | psx_input_init(input); 84 | 85 | // psxi_guncon_t* controller = psxi_guncon_create(); 86 | // psxi_guncon_init(controller); 87 | // psxi_guncon_init_input(controller, input); 88 | psxi_sda_t* controller = psxi_sda_create(); 89 | psxi_sda_init(controller, SDA_MODEL_DIGITAL); 90 | psxi_sda_init_input(controller, input); 91 | 92 | psx_pad_attach_joy(psx->pad, 0, input); 93 | psx_pad_attach_mcd(psx->pad, 0, "slot1.mcd"); 94 | psx_pad_attach_mcd(psx->pad, 1, "slot2.mcd"); 95 | 96 | if (cfg->exe) { 97 | while (psx->cpu->pc != 0x80030000) 98 | psx_update(psx); 99 | 100 | psx_load_exe(psx, cfg->exe); 101 | } 102 | 103 | psxe_cfg_destroy(cfg); 104 | 105 | while (psxe_screen_is_open(screen)) 106 | psx_update(psx); 107 | 108 | SDL_PauseAudioDevice(dev, 1); 109 | 110 | psx_cpu_t* cpu = psx_get_cpu(psx); 111 | 112 | log_set_quiet(0); 113 | 114 | log_fatal("r0=%08x at=%08x v0=%08x v1=%08x", cpu->r[0] , cpu->r[1] , cpu->r[2] , cpu->r[3] ); 115 | log_fatal("a0=%08x a1=%08x a2=%08x a3=%08x", cpu->r[4] , cpu->r[5] , cpu->r[6] , cpu->r[7] ); 116 | log_fatal("t0=%08x t1=%08x t2=%08x t3=%08x", cpu->r[8] , cpu->r[9] , cpu->r[10], cpu->r[11]); 117 | log_fatal("t4=%08x t5=%08x t6=%08x t7=%08x", cpu->r[12], cpu->r[13], cpu->r[14], cpu->r[15]); 118 | log_fatal("s0=%08x s1=%08x s2=%08x s3=%08x", cpu->r[16], cpu->r[17], cpu->r[18], cpu->r[19]); 119 | log_fatal("s4=%08x s5=%08x s6=%08x s7=%08x", cpu->r[20], cpu->r[21], cpu->r[22], cpu->r[23]); 120 | log_fatal("t8=%08x t9=%08x k0=%08x k1=%08x", cpu->r[24], cpu->r[25], cpu->r[26], cpu->r[27]); 121 | log_fatal("gp=%08x sp=%08x fp=%08x ra=%08x", cpu->r[28], cpu->r[29], cpu->r[30], cpu->r[31]); 122 | log_fatal("pc=%08x hi=%08x lo=%08x ep=%08x", cpu->pc, cpu->hi, cpu->lo, cpu->cop0_r[COP0_EPC]); 123 | 124 | psx_pad_detach_joy(psx->pad, 0); 125 | psx_destroy(psx); 126 | psxe_screen_destroy(screen); 127 | 128 | return 0; 129 | } -------------------------------------------------------------------------------- /frontend/screen.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_H 2 | #define SCREEN_H 3 | 4 | #include "../psx/psx.h" 5 | #include "common.h" 6 | 7 | #include 8 | 9 | #include "SDL.h" 10 | #include "SDL_version.h" 11 | #include "SDL_gamecontroller.h" 12 | 13 | typedef struct { 14 | SDL_Window* window; 15 | SDL_Renderer* renderer; 16 | SDL_Texture* texture; 17 | 18 | psx_t* psx; 19 | psx_pad_t* pad; 20 | 21 | unsigned int saved_scale; 22 | unsigned int width, height, scale; 23 | unsigned int image_width, image_height; 24 | unsigned int image_xoff, image_yoff; 25 | unsigned int format; 26 | unsigned int texture_width, texture_height; 27 | 28 | int bilinear; 29 | int fullscreen; 30 | int vertical_mode; 31 | int debug_mode; 32 | int open; 33 | 34 | SDL_GameController* controller; 35 | } psxe_screen_t; 36 | 37 | psxe_screen_t* psxe_screen_create(void); 38 | void psxe_screen_init(psxe_screen_t*, psx_t*); 39 | void psxe_screen_reload(psxe_screen_t*); 40 | int psxe_screen_is_open(psxe_screen_t*); 41 | void psxe_screen_update(psxe_screen_t*); 42 | void psxe_screen_destroy(psxe_screen_t*); 43 | void psxe_screen_set_scale(psxe_screen_t*, unsigned int); 44 | void psxe_screen_toggle_debug_mode(psxe_screen_t*); 45 | 46 | // GPU event handlers 47 | void psxe_gpu_dmode_event_cb(psx_gpu_t*); 48 | void psxe_gpu_vblank_event_cb(psx_gpu_t*); 49 | 50 | #endif -------------------------------------------------------------------------------- /frontend/toml.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) CK Tan 5 | https://github.com/cktan/tomlc99 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | #ifndef TOML_H 26 | #define TOML_H 27 | 28 | #ifdef _MSC_VER 29 | #pragma warning(disable: 4996) 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | #ifdef __cplusplus 36 | #define TOML_EXTERN extern "C" 37 | #else 38 | #define TOML_EXTERN extern 39 | #endif 40 | 41 | typedef struct toml_timestamp_t toml_timestamp_t; 42 | typedef struct toml_table_t toml_table_t; 43 | typedef struct toml_array_t toml_array_t; 44 | typedef struct toml_datum_t toml_datum_t; 45 | 46 | /* Parse a file. Return a table on success, or 0 otherwise. 47 | * Caller must toml_free(the-return-value) after use. 48 | */ 49 | TOML_EXTERN toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz); 50 | 51 | /* Parse a string containing the full config. 52 | * Return a table on success, or 0 otherwise. 53 | * Caller must toml_free(the-return-value) after use. 54 | */ 55 | TOML_EXTERN toml_table_t *toml_parse(char *conf, /* NUL terminated, please. */ 56 | char *errbuf, int errbufsz); 57 | 58 | /* Free the table returned by toml_parse() or toml_parse_file(). Once 59 | * this function is called, any handles accessed through this tab 60 | * directly or indirectly are no longer valid. 61 | */ 62 | TOML_EXTERN void toml_free(toml_table_t *tab); 63 | 64 | /* Timestamp types. The year, month, day, hour, minute, second, z 65 | * fields may be NULL if they are not relevant. e.g. In a DATE 66 | * type, the hour, minute, second and z fields will be NULLs. 67 | */ 68 | struct toml_timestamp_t { 69 | struct { /* internal. do not use. */ 70 | int year, month, day; 71 | int hour, minute, second, millisec; 72 | char z[10]; 73 | } __buffer; 74 | int *year, *month, *day; 75 | int *hour, *minute, *second, *millisec; 76 | char *z; 77 | }; 78 | 79 | /*----------------------------------------------------------------- 80 | * Enhanced access methods 81 | */ 82 | struct toml_datum_t { 83 | int ok; 84 | union { 85 | toml_timestamp_t *ts; /* ts must be freed after use */ 86 | char *s; /* string value. s must be freed after use */ 87 | int b; /* bool value */ 88 | int64_t i; /* int value */ 89 | double d; /* double value */ 90 | } u; 91 | }; 92 | 93 | /* on arrays: */ 94 | /* ... retrieve size of array. */ 95 | TOML_EXTERN int toml_array_nelem(const toml_array_t *arr); 96 | /* ... retrieve values using index. */ 97 | TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t *arr, int idx); 98 | TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t *arr, int idx); 99 | TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t *arr, int idx); 100 | TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t *arr, int idx); 101 | TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx); 102 | /* ... retrieve array or table using index. */ 103 | TOML_EXTERN toml_array_t *toml_array_at(const toml_array_t *arr, int idx); 104 | TOML_EXTERN toml_table_t *toml_table_at(const toml_array_t *arr, int idx); 105 | 106 | /* on tables: */ 107 | /* ... retrieve the key in table at keyidx. Return 0 if out of range. */ 108 | TOML_EXTERN const char *toml_key_in(const toml_table_t *tab, int keyidx); 109 | /* ... returns 1 if key exists in tab, 0 otherwise */ 110 | TOML_EXTERN int toml_key_exists(const toml_table_t *tab, const char *key); 111 | /* ... retrieve values using key. */ 112 | TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t *arr, 113 | const char *key); 114 | TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key); 115 | TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t *arr, const char *key); 116 | TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t *arr, 117 | const char *key); 118 | TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t *arr, 119 | const char *key); 120 | /* .. retrieve array or table using key. */ 121 | TOML_EXTERN toml_array_t *toml_array_in(const toml_table_t *tab, 122 | const char *key); 123 | TOML_EXTERN toml_table_t *toml_table_in(const toml_table_t *tab, 124 | const char *key); 125 | 126 | /*----------------------------------------------------------------- 127 | * lesser used 128 | */ 129 | /* Return the array kind: 't'able, 'a'rray, 'v'alue, 'm'ixed */ 130 | TOML_EXTERN char toml_array_kind(const toml_array_t *arr); 131 | 132 | /* For array kind 'v'alue, return the type of values 133 | i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp, 'm'ixed 134 | 0 if unknown 135 | */ 136 | TOML_EXTERN char toml_array_type(const toml_array_t *arr); 137 | 138 | /* Return the key of an array */ 139 | TOML_EXTERN const char *toml_array_key(const toml_array_t *arr); 140 | 141 | /* Return the number of key-values in a table */ 142 | TOML_EXTERN int toml_table_nkval(const toml_table_t *tab); 143 | 144 | /* Return the number of arrays in a table */ 145 | TOML_EXTERN int toml_table_narr(const toml_table_t *tab); 146 | 147 | /* Return the number of sub-tables in a table */ 148 | TOML_EXTERN int toml_table_ntab(const toml_table_t *tab); 149 | 150 | /* Return the key of a table*/ 151 | TOML_EXTERN const char *toml_table_key(const toml_table_t *tab); 152 | 153 | /*-------------------------------------------------------------- 154 | * misc 155 | */ 156 | TOML_EXTERN int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret); 157 | TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]); 158 | TOML_EXTERN void toml_set_memutil(void *(*xxmalloc)(size_t), 159 | void (*xxfree)(void *)); 160 | 161 | /*-------------------------------------------------------------- 162 | * deprecated 163 | */ 164 | /* A raw value, must be processed by toml_rto* before using. */ 165 | typedef const char *toml_raw_t; 166 | TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key); 167 | TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t *arr, int idx); 168 | TOML_EXTERN int toml_rtos(toml_raw_t s, char **ret); 169 | TOML_EXTERN int toml_rtob(toml_raw_t s, int *ret); 170 | TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t *ret); 171 | TOML_EXTERN int toml_rtod(toml_raw_t s, double *ret); 172 | TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double *ret, char *buf, int buflen); 173 | TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t *ret); 174 | 175 | #endif /* TOML_H */ 176 | -------------------------------------------------------------------------------- /psx/bus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "bus.h" 5 | #include "bus_init.h" 6 | #include "log.h" 7 | 8 | #define RANGE(v, s, e) ((v >= s) && (v < e)) 9 | 10 | const uint32_t g_psx_bus_region_mask_table[] = { 11 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 12 | 0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff 13 | }; 14 | 15 | psx_bus_t* psx_bus_create(void) { 16 | return (psx_bus_t*)malloc(sizeof(psx_bus_t)); 17 | } 18 | 19 | // Does nothing for now 20 | void psx_bus_init(psx_bus_t* bus) {} 21 | 22 | void psx_bus_destroy(psx_bus_t* bus) { 23 | free(bus); 24 | } 25 | 26 | #define HANDLE_READ(dev, bits) \ 27 | if (RANGE(addr, bus->dev->io_base, (bus->dev->io_base + bus->dev->io_size))) { \ 28 | bus->access_cycles = bus->dev->bus_delay; \ 29 | return psx_ ## dev ## _read ## bits (bus->dev, addr - bus->dev->io_base); \ 30 | } 31 | #define HANDLE_WRITE(dev, bits) \ 32 | if (RANGE(addr, bus->dev->io_base, (bus->dev->io_base + bus->dev->io_size))) { \ 33 | bus->access_cycles = bus->dev->bus_delay; \ 34 | psx_ ## dev ## _write ## bits (bus->dev, addr - bus->dev->io_base, value); \ 35 | return; \ 36 | } 37 | 38 | uint32_t psx_bus_read32(psx_bus_t* bus, uint32_t addr) { 39 | uint32_t vaddr = addr; 40 | 41 | addr &= g_psx_bus_region_mask_table[addr >> 29]; 42 | 43 | if (addr & 0x3) { 44 | log_fatal("Unaligned 32-bit read from %08x:%08x", vaddr, addr); 45 | } 46 | 47 | HANDLE_READ(bios, 32); 48 | HANDLE_READ(ram, 32); 49 | HANDLE_READ(dma, 32); 50 | HANDLE_READ(exp1, 32); 51 | HANDLE_READ(exp2, 32); 52 | HANDLE_READ(mc1, 32); 53 | HANDLE_READ(mc2, 32); 54 | HANDLE_READ(mc3, 32); 55 | HANDLE_READ(ic, 32); 56 | HANDLE_READ(scratchpad, 32); 57 | HANDLE_READ(gpu, 32); 58 | HANDLE_READ(spu, 32); 59 | HANDLE_READ(timer, 32); 60 | HANDLE_READ(cdrom, 32); 61 | HANDLE_READ(pad, 32); 62 | HANDLE_READ(mdec, 32); 63 | 64 | log_fatal("Unhandled 32-bit read from %08x:%08x", vaddr, addr); 65 | 66 | //exit(1); 67 | 68 | return 0x00000000; 69 | } 70 | 71 | static uint16_t sio_ctrl; 72 | 73 | uint16_t psx_bus_read16(psx_bus_t* bus, uint32_t addr) { 74 | bus->access_cycles = 2; 75 | 76 | uint32_t vaddr = addr; 77 | 78 | addr &= g_psx_bus_region_mask_table[addr >> 29]; 79 | 80 | if (addr & 0x1) { 81 | log_fatal("Unaligned 16-bit read from %08x:%08x", vaddr, addr); 82 | } 83 | 84 | HANDLE_READ(bios, 16); 85 | HANDLE_READ(ram, 16); 86 | HANDLE_READ(dma, 16); 87 | HANDLE_READ(exp1, 16); 88 | HANDLE_READ(exp2, 16); 89 | HANDLE_READ(mc1, 16); 90 | HANDLE_READ(mc2, 16); 91 | HANDLE_READ(mc3, 16); 92 | HANDLE_READ(ic, 16); 93 | HANDLE_READ(scratchpad, 16); 94 | HANDLE_READ(gpu, 16); 95 | HANDLE_READ(spu, 16); 96 | HANDLE_READ(timer, 16); 97 | HANDLE_READ(cdrom, 16); 98 | HANDLE_READ(pad, 16); 99 | HANDLE_READ(mdec, 16); 100 | 101 | if (addr == 0x1f80105a) 102 | return sio_ctrl; 103 | 104 | if (addr == 0x1f801054) 105 | return 0x05; 106 | 107 | if (addr == 0x1f400004) 108 | return 0xc8; 109 | 110 | if (addr == 0x1f400006) 111 | return 0x1fe0; 112 | 113 | printf("Unhandled 16-bit read from %08x:%08x\n", vaddr, addr); 114 | 115 | // exit(1); 116 | 117 | return 0x0000; 118 | } 119 | 120 | uint8_t psx_bus_read8(psx_bus_t* bus, uint32_t addr) { 121 | bus->access_cycles = 2; 122 | 123 | // uint32_t vaddr = addr; 124 | 125 | addr &= g_psx_bus_region_mask_table[addr >> 29]; 126 | 127 | HANDLE_READ(bios, 8); 128 | HANDLE_READ(ram, 8); 129 | HANDLE_READ(dma, 8); 130 | HANDLE_READ(exp1, 8); 131 | HANDLE_READ(exp2, 8); 132 | HANDLE_READ(mc1, 8); 133 | HANDLE_READ(mc2, 8); 134 | HANDLE_READ(mc3, 8); 135 | HANDLE_READ(ic, 8); 136 | HANDLE_READ(scratchpad, 8); 137 | HANDLE_READ(gpu, 8); 138 | HANDLE_READ(spu, 8); 139 | HANDLE_READ(timer, 8); 140 | HANDLE_READ(cdrom, 8); 141 | HANDLE_READ(pad, 8); 142 | HANDLE_READ(mdec, 8); 143 | 144 | // printf("Unhandled 8-bit read from %08x:%08x\n", vaddr, addr); 145 | 146 | //exit(1); 147 | 148 | return 0x00; 149 | } 150 | 151 | void psx_bus_write32(psx_bus_t* bus, uint32_t addr, uint32_t value) { 152 | bus->access_cycles = 0; 153 | 154 | uint32_t vaddr = addr; 155 | 156 | addr &= g_psx_bus_region_mask_table[addr >> 29]; 157 | 158 | if (addr & 0x3) { 159 | log_fatal("Unaligned 32-bit write to %08x:%08x (%08x)", vaddr, addr, value); 160 | } 161 | 162 | HANDLE_WRITE(bios, 32); 163 | HANDLE_WRITE(ram, 32); 164 | HANDLE_WRITE(dma, 32); 165 | HANDLE_WRITE(exp1, 32); 166 | HANDLE_WRITE(exp2, 32); 167 | HANDLE_WRITE(mc1, 32); 168 | HANDLE_WRITE(mc2, 32); 169 | HANDLE_WRITE(mc3, 32); 170 | HANDLE_WRITE(ic, 32); 171 | HANDLE_WRITE(scratchpad, 32); 172 | HANDLE_WRITE(gpu, 32); 173 | HANDLE_WRITE(spu, 32); 174 | HANDLE_WRITE(timer, 32); 175 | HANDLE_WRITE(cdrom, 32); 176 | HANDLE_WRITE(pad, 32); 177 | HANDLE_WRITE(mdec, 32); 178 | 179 | printf("Unhandled 32-bit write to %08x:%08x (%08x)\n", vaddr, addr, value); 180 | 181 | //exit(1); 182 | } 183 | 184 | 185 | void psx_bus_write16(psx_bus_t* bus, uint32_t addr, uint32_t value) { 186 | bus->access_cycles = 0; 187 | 188 | uint32_t vaddr = addr; 189 | 190 | addr &= g_psx_bus_region_mask_table[addr >> 29]; 191 | 192 | if (addr & 0x1) { 193 | log_fatal("Unaligned 16-bit write to %08x:%08x (%04x)", vaddr, addr, value); 194 | } 195 | 196 | HANDLE_WRITE(bios, 16); 197 | HANDLE_WRITE(ram, 16); 198 | HANDLE_WRITE(dma, 16); 199 | HANDLE_WRITE(exp1, 16); 200 | HANDLE_WRITE(exp2, 16); 201 | HANDLE_WRITE(mc1, 16); 202 | HANDLE_WRITE(mc2, 16); 203 | HANDLE_WRITE(mc3, 16); 204 | HANDLE_WRITE(ic, 16); 205 | HANDLE_WRITE(scratchpad, 16); 206 | HANDLE_WRITE(gpu, 16); 207 | HANDLE_WRITE(spu, 16); 208 | HANDLE_WRITE(timer, 16); 209 | HANDLE_WRITE(cdrom, 16); 210 | HANDLE_WRITE(pad, 16); 211 | HANDLE_WRITE(mdec, 16); 212 | 213 | // if (addr == 0x1f80105a) { sio_ctrl = value; return; } 214 | 215 | printf("Unhandled 16-bit write to %08x:%08x (%04x)\n", vaddr, addr, value); 216 | 217 | //exit(1); 218 | } 219 | 220 | void psx_bus_write8(psx_bus_t* bus, uint32_t addr, uint32_t value) { 221 | bus->access_cycles = 0; 222 | 223 | uint32_t vaddr = addr; 224 | 225 | addr &= g_psx_bus_region_mask_table[addr >> 29]; 226 | 227 | HANDLE_WRITE(bios, 8); 228 | HANDLE_WRITE(ram, 8); 229 | HANDLE_WRITE(dma, 8); 230 | HANDLE_WRITE(exp1, 8); 231 | HANDLE_WRITE(exp2, 8); 232 | HANDLE_WRITE(mc1, 8); 233 | HANDLE_WRITE(mc2, 8); 234 | HANDLE_WRITE(mc3, 8); 235 | HANDLE_WRITE(ic, 8); 236 | HANDLE_WRITE(scratchpad, 8); 237 | HANDLE_WRITE(gpu, 8); 238 | HANDLE_WRITE(spu, 8); 239 | HANDLE_WRITE(timer, 8); 240 | HANDLE_WRITE(cdrom, 8); 241 | HANDLE_WRITE(pad, 8); 242 | HANDLE_WRITE(mdec, 8); 243 | 244 | printf("Unhandled 8-bit write to %08x:%08x (%02x)\n", vaddr, addr, value); 245 | 246 | //exit(1); 247 | } 248 | 249 | void psx_bus_init_bios(psx_bus_t* bus, psx_bios_t* bios) { 250 | bus->bios = bios; 251 | } 252 | 253 | void psx_bus_init_ram(psx_bus_t* bus, psx_ram_t* ram) { 254 | bus->ram = ram; 255 | } 256 | 257 | void psx_bus_init_dma(psx_bus_t* bus, psx_dma_t* dma) { 258 | bus->dma = dma; 259 | } 260 | 261 | void psx_bus_init_exp1(psx_bus_t* bus, psx_exp1_t* exp1) { 262 | bus->exp1 = exp1; 263 | } 264 | 265 | void psx_bus_init_exp2(psx_bus_t* bus, psx_exp2_t* exp2) { 266 | bus->exp2 = exp2; 267 | } 268 | 269 | void psx_bus_init_mc1(psx_bus_t* bus, psx_mc1_t* mc1) { 270 | bus->mc1 = mc1; 271 | } 272 | 273 | void psx_bus_init_mc2(psx_bus_t* bus, psx_mc2_t* mc2) { 274 | bus->mc2 = mc2; 275 | } 276 | 277 | void psx_bus_init_mc3(psx_bus_t* bus, psx_mc3_t* mc3) { 278 | bus->mc3 = mc3; 279 | } 280 | 281 | void psx_bus_init_ic(psx_bus_t* bus, psx_ic_t* ic) { 282 | bus->ic = ic; 283 | } 284 | 285 | void psx_bus_init_scratchpad(psx_bus_t* bus, psx_scratchpad_t* scratchpad) { 286 | bus->scratchpad = scratchpad; 287 | } 288 | 289 | void psx_bus_init_gpu(psx_bus_t* bus, psx_gpu_t* gpu) { 290 | bus->gpu = gpu; 291 | } 292 | 293 | void psx_bus_init_spu(psx_bus_t* bus, psx_spu_t* spu) { 294 | bus->spu = spu; 295 | } 296 | 297 | void psx_bus_init_timer(psx_bus_t* bus, psx_timer_t* timer) { 298 | bus->timer = timer; 299 | } 300 | 301 | void psx_bus_init_cdrom(psx_bus_t* bus, psx_cdrom_t* cdrom) { 302 | bus->cdrom = cdrom; 303 | } 304 | 305 | void psx_bus_init_pad(psx_bus_t* bus, psx_pad_t* pad) { 306 | bus->pad = pad; 307 | } 308 | 309 | void psx_bus_init_mdec(psx_bus_t* bus, psx_mdec_t* mdec) { 310 | bus->mdec = mdec; 311 | } 312 | 313 | uint32_t psx_bus_get_access_cycles(psx_bus_t* bus) { 314 | uint32_t cycles = bus->access_cycles; 315 | 316 | bus->access_cycles = 0; 317 | 318 | return cycles; 319 | } 320 | 321 | #undef HANDLE_READ 322 | #undef HANDLE_WRITE -------------------------------------------------------------------------------- /psx/bus.h: -------------------------------------------------------------------------------- 1 | #ifndef BUS_H 2 | #define BUS_H 3 | 4 | #include 5 | 6 | struct psx_bus_t; 7 | 8 | typedef struct psx_bus_t psx_bus_t; 9 | 10 | psx_bus_t* psx_bus_create(void); 11 | void psx_bus_init(psx_bus_t*); 12 | uint32_t psx_bus_read32(psx_bus_t*, uint32_t); 13 | uint16_t psx_bus_read16(psx_bus_t*, uint32_t); 14 | uint8_t psx_bus_read8(psx_bus_t*, uint32_t); 15 | void psx_bus_write32(psx_bus_t*, uint32_t, uint32_t); 16 | void psx_bus_write16(psx_bus_t*, uint32_t, uint32_t); 17 | void psx_bus_write8(psx_bus_t*, uint32_t, uint32_t); 18 | uint32_t psx_bus_get_access_cycles(psx_bus_t*); 19 | void psx_bus_destroy(psx_bus_t*); 20 | 21 | #endif -------------------------------------------------------------------------------- /psx/bus_init.h: -------------------------------------------------------------------------------- 1 | #ifndef BUS_INIT_H 2 | #define BUS_INIT_H 3 | 4 | #include "dev/cdrom/cdrom.h" 5 | #include "dev/bios.h" 6 | #include "dev/ram.h" 7 | #include "dev/dma.h" 8 | #include "dev/exp1.h" 9 | #include "dev/exp2.h" 10 | #include "dev/mc1.h" 11 | #include "dev/mc2.h" 12 | #include "dev/mc3.h" 13 | #include "dev/ic.h" 14 | #include "dev/scratchpad.h" 15 | #include "dev/gpu.h" 16 | #include "dev/spu.h" 17 | #include "dev/timer.h" 18 | #include "dev/pad.h" 19 | #include "dev/mdec.h" 20 | 21 | struct psx_bus_t { 22 | psx_bios_t* bios; 23 | psx_ram_t* ram; 24 | psx_dma_t* dma; 25 | psx_exp1_t* exp1; 26 | psx_exp2_t* exp2; 27 | psx_mc1_t* mc1; 28 | psx_mc2_t* mc2; 29 | psx_mc3_t* mc3; 30 | psx_ic_t* ic; 31 | psx_scratchpad_t* scratchpad; 32 | psx_gpu_t* gpu; 33 | psx_spu_t* spu; 34 | psx_timer_t* timer; 35 | psx_cdrom_t* cdrom; 36 | psx_pad_t* pad; 37 | psx_mdec_t* mdec; 38 | 39 | uint32_t access_cycles; 40 | }; 41 | 42 | void psx_bus_init_bios(psx_bus_t*, psx_bios_t*); 43 | void psx_bus_init_ram(psx_bus_t*, psx_ram_t*); 44 | void psx_bus_init_dma(psx_bus_t*, psx_dma_t*); 45 | void psx_bus_init_exp1(psx_bus_t*, psx_exp1_t*); 46 | void psx_bus_init_exp2(psx_bus_t*, psx_exp2_t*); 47 | void psx_bus_init_mc1(psx_bus_t*, psx_mc1_t*); 48 | void psx_bus_init_mc2(psx_bus_t*, psx_mc2_t*); 49 | void psx_bus_init_mc3(psx_bus_t*, psx_mc3_t*); 50 | void psx_bus_init_ic(psx_bus_t*, psx_ic_t*); 51 | void psx_bus_init_scratchpad(psx_bus_t*, psx_scratchpad_t*); 52 | void psx_bus_init_gpu(psx_bus_t*, psx_gpu_t*); 53 | void psx_bus_init_spu(psx_bus_t*, psx_spu_t*); 54 | void psx_bus_init_timer(psx_bus_t*, psx_timer_t*); 55 | void psx_bus_init_cdrom(psx_bus_t*, psx_cdrom_t*); 56 | void psx_bus_init_pad(psx_bus_t*, psx_pad_t*); 57 | void psx_bus_init_mdec(psx_bus_t*, psx_mdec_t*); 58 | 59 | #endif -------------------------------------------------------------------------------- /psx/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file should contain definitions for our 3 | global configuration variables and implementations 4 | for accessor methods 5 | */ 6 | 7 | // Suppress "empty translation unit" warning until 8 | // we implement core configuration 9 | int dummy; -------------------------------------------------------------------------------- /psx/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | /* 5 | This file should contain accessor method definitions 6 | */ 7 | 8 | #endif -------------------------------------------------------------------------------- /psx/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef CPU_H 2 | #define CPU_H 3 | 4 | #include 5 | #include 6 | 7 | #include "bus.h" 8 | 9 | #define PSX_CPU_CPS 33868800 // 33868800 Clocks/s 10 | #define PSX_CPU_FREQ 33.868800f // 33.868800 MHz 11 | 12 | struct psx_cpu_t; 13 | 14 | typedef struct psx_cpu_t psx_cpu_t; 15 | 16 | typedef void (*psx_cpu_kcall_hook_t)(psx_cpu_t*); 17 | 18 | /* 19 | cop0r0 - N/A 20 | cop0r1 - N/A 21 | cop0r2 - N/A 22 | cop0r3 - BPC - Breakpoint on execute (R/W) 23 | cop0r4 - N/A 24 | cop0r5 - BDA - Breakpoint on data access (R/W) 25 | cop0r6 - JUMPDEST - Randomly memorized jump address (R) 26 | cop0r7 - DCIC - Breakpoint control (R/W) 27 | cop0r8 - BadVaddr - Bad Virtual Address (R) 28 | cop0r9 - BDAM - Data Access breakpoint mask (R/W) 29 | cop0r10 - N/A 30 | cop0r11 - BPCM - Execute breakpoint mask (R/W) 31 | cop0r12 - SR - System status register (R/W) 32 | cop0r13 - CAUSE - Describes the most recently recognised exception (R) 33 | cop0r14 - EPC - Return Address from Trap (R) 34 | cop0r15 - PRID - Processor ID (R) 35 | */ 36 | 37 | #define COP0_BPC 3 38 | #define COP0_BDA 5 39 | #define COP0_JUMPDEST 6 40 | #define COP0_DCIC 7 41 | #define COP0_BADVADDR 8 42 | #define COP0_BDAM 9 43 | #define COP0_BPCM 11 44 | #define COP0_SR 12 45 | #define COP0_CAUSE 13 46 | #define COP0_EPC 14 47 | #define COP0_PRID 15 48 | 49 | /* 50 | Name Alias Common Usage 51 | R0 zero Constant (always 0) 52 | R1 at Assembler temporary (destroyed by some assembler pseudoinstructions!) 53 | R2-R3 v0-v1 Subroutine return values, may be changed by subroutines 54 | R4-R7 a0-a3 Subroutine arguments, may be changed by subroutines 55 | R8-R15 t0-t7 Temporaries, may be changed by subroutines 56 | R16-R23 s0-s7 Static variables, must be saved by subs 57 | R24-R25 t8-t9 Temporaries, may be changed by subroutines 58 | R26-R27 k0-k1 Reserved for kernel (destroyed by some IRQ handlers!) 59 | R28 gp Global pointer (rarely used) 60 | R29 sp Stack pointer 61 | R30 fp(s8) Frame Pointer, or 9th Static variable, must be saved 62 | R31 ra Return address (used so by JAL,BLTZAL,BGEZAL opcodes) 63 | - pc Program counter 64 | - hi,lo Multiply/divide results, may be changed by subroutines 65 | */ 66 | 67 | typedef struct __attribute__((__packed__)) { 68 | union { 69 | uint32_t xy; 70 | int16_t p[2]; 71 | }; 72 | 73 | int16_t z; 74 | } gte_vertex_t; 75 | 76 | typedef struct __attribute__((__packed__)) { 77 | union { 78 | uint32_t xy; 79 | int16_t p[2]; 80 | }; 81 | } gte_vec2_t; 82 | 83 | typedef struct __attribute__((__packed__)) { 84 | int32_t x, y, z; 85 | } gte_vec3_t; 86 | 87 | typedef struct __attribute__((__packed__)) { 88 | union { 89 | uint32_t rgbc; 90 | uint8_t c[4]; 91 | }; 92 | } gte_color_t; 93 | 94 | typedef struct __attribute__((__packed__)) { 95 | union { 96 | uint32_t u32; 97 | int16_t c[2]; 98 | } m[4]; 99 | 100 | int16_t m33; 101 | } gte_matrix_t; 102 | 103 | struct __attribute__((__packed__)) psx_cpu_t { 104 | uint32_t r[32]; 105 | uint32_t opcode; 106 | uint32_t pc, next_pc, saved_pc; 107 | uint32_t hi, lo; 108 | uint32_t load_d, load_v; 109 | uint32_t last_cycles; 110 | uint32_t total_cycles; 111 | int branch, delay_slot, branch_taken; 112 | 113 | uint32_t cop0_r[16]; 114 | 115 | struct { 116 | gte_vertex_t v[3]; 117 | gte_color_t rgbc; 118 | uint16_t otz; 119 | int16_t ir[4]; 120 | gte_vec2_t sxy[4]; 121 | uint16_t sz[4]; 122 | gte_color_t rgb[3]; 123 | uint32_t res1; 124 | int32_t mac[4]; 125 | uint16_t irgb, orgb; 126 | int32_t lzcs, lzcr; 127 | } cop2_dr; 128 | 129 | struct { 130 | gte_matrix_t rt; 131 | gte_vec3_t tr; 132 | gte_matrix_t l; 133 | gte_vec3_t bk; 134 | gte_matrix_t lr; 135 | gte_vec3_t fc; 136 | uint32_t ofx, ofy; 137 | uint32_t h; 138 | int16_t dqa; 139 | int32_t dqb; 140 | int16_t zsf3, zsf4; 141 | uint32_t flag; 142 | } cop2_cr; 143 | 144 | int gte_lm; 145 | int gte_sf; 146 | int gte_mx; 147 | int gte_v; 148 | int gte_cv; 149 | int64_t s_mac0; 150 | int64_t s_mac3; 151 | 152 | psx_bus_t* bus; 153 | 154 | psx_cpu_kcall_hook_t a_function_hook; 155 | psx_cpu_kcall_hook_t b_function_hook; 156 | }; 157 | 158 | /* 159 | 0 IEc Current Interrupt Enable (0=Disable, 1=Enable) ;rfe pops IUp here 160 | 1 KUc Current Kernel/User Mode (0=Kernel, 1=User) ;rfe pops KUp here 161 | 2 IEp Previous Interrupt Disable ;rfe pops IUo here 162 | 3 KUp Previous Kernel/User Mode ;rfe pops KUo here 163 | 4 IEo Old Interrupt Disable ;left unchanged by rfe 164 | 5 KUo Old Kernel/User Mode ;left unchanged by rfe 165 | 6-7 - Not used (zero) 166 | 8-15 Im 8 bit interrupt mask fields. When set the corresponding 167 | interrupts are allowed to cause an exception. 168 | 16 Isc Isolate Cache (0=No, 1=Isolate) 169 | When isolated, all load and store operations are targetted 170 | to the Data cache, and never the main memory. 171 | (Used by PSX Kernel, in combination with Port FFFE0130h) 172 | 17 Swc Swapped cache mode (0=Normal, 1=Swapped) 173 | Instruction cache will act as Data cache and vice versa. 174 | Use only with Isc to access & invalidate Instr. cache entries. 175 | (Not used by PSX Kernel) 176 | 18 PZ When set cache parity bits are written as 0. 177 | 19 CM Shows the result of the last load operation with the D-cache 178 | isolated. It gets set if the cache really contained data 179 | for the addressed memory location. 180 | 20 PE Cache parity error (Does not cause exception) 181 | 21 TS TLB shutdown. Gets set if a programm address simultaneously 182 | matches 2 TLB entries. 183 | (initial value on reset allows to detect extended CPU version?) 184 | 22 BEV Boot exception vectors in RAM/ROM (0=RAM/KSEG0, 1=ROM/KSEG1) 185 | 23-24 - Not used (zero) 186 | 25 RE Reverse endianness (0=Normal endianness, 1=Reverse endianness) 187 | Reverses the byte order in which data is stored in 188 | memory. (lo-hi -> hi-lo) 189 | (Affects only user mode, not kernel mode) (?) 190 | (The bit doesn't exist in PSX ?) 191 | 26-27 - Not used (zero) 192 | 28 CU0 COP0 Enable (0=Enable only in Kernel Mode, 1=Kernel and User Mode) 193 | 29 CU1 COP1 Enable (0=Disable, 1=Enable) (none in PSX) 194 | 30 CU2 COP2 Enable (0=Disable, 1=Enable) (GTE in PSX) 195 | 31 CU3 COP3 Enable (0=Disable, 1=Enable) (none in PSX) 196 | */ 197 | 198 | #define SR_IEC 0x00000001 199 | #define SR_KUC 0x00000002 200 | #define SR_IEP 0x00000004 201 | #define SR_KUP 0x00000008 202 | #define SR_IEO 0x00000010 203 | #define SR_KUO 0x00000020 204 | #define SR_IM 0x0000ff00 205 | #define SR_IM0 0x00000100 206 | #define SR_IM1 0x00000200 207 | #define SR_IM2 0x00000400 208 | #define SR_IM3 0x00000800 209 | #define SR_IM4 0x00001000 210 | #define SR_IM5 0x00002000 211 | #define SR_IM6 0x00004000 212 | #define SR_IM7 0x00008000 213 | #define SR_ISC 0x00010000 214 | #define SR_SWC 0x00020000 215 | #define SR_PZ 0x00040000 216 | #define SR_CM 0x00080000 217 | #define SR_PE 0x00100000 218 | #define SR_TS 0x00200000 219 | #define SR_BEV 0x00400000 220 | #define SR_RE 0x02000000 221 | #define SR_CU0 0x10000000 222 | #define SR_CU1 0x20000000 223 | #define SR_CU2 0x40000000 224 | #define SR_CU3 0x80000000 225 | 226 | psx_cpu_t* psx_cpu_create(void); 227 | void psx_cpu_init(psx_cpu_t*, psx_bus_t*); 228 | void psx_cpu_destroy(psx_cpu_t*); 229 | void psx_cpu_cycle(psx_cpu_t*); 230 | void psx_cpu_set_irq_pending(psx_cpu_t*); 231 | void psx_cpu_load_state(psx_cpu_t*, FILE*); 232 | void psx_cpu_save_state(psx_cpu_t*, FILE*); 233 | void psx_cpu_fetch(psx_cpu_t*); 234 | void psx_cpu_set_a_kcall_hook(psx_cpu_t*, psx_cpu_kcall_hook_t); 235 | void psx_cpu_set_b_kcall_hook(psx_cpu_t*, psx_cpu_kcall_hook_t); 236 | int psx_cpu_execute(psx_cpu_t*); 237 | 238 | /* 239 | 00h INT Interrupt 240 | 01h MOD TLB modification (none such in PSX) 241 | 02h TLBL TLB load (none such in PSX) 242 | 03h TLBS TLB store (none such in PSX) 243 | 04h AdEL Address error, Data load or Instruction fetch 244 | 05h AdES Address error, Data store 245 | The address errors occur when attempting to read 246 | outside of KUseg in user mode and when the address 247 | is misaligned. (See also: BadVaddr register) 248 | 06h IBE Bus error on Instruction fetch 249 | 07h DBE Bus error on Data load/store 250 | 08h Syscall Generated unconditionally by syscall instruction 251 | 09h BP Breakpoint - break instruction 252 | 0Ah RI Reserved instruction 253 | 0Bh CpU Coprocessor unusable 254 | 0Ch Ov Arithmetic overflow 255 | */ 256 | 257 | #define CAUSE_INT (0x00 << 2) 258 | #define CAUSE_MOD (0x01 << 2) 259 | #define CAUSE_TLBL (0x02 << 2) 260 | #define CAUSE_TLBS (0x03 << 2) 261 | #define CAUSE_ADEL (0x04 << 2) 262 | #define CAUSE_ADES (0x05 << 2) 263 | #define CAUSE_IBE (0x06 << 2) 264 | #define CAUSE_DBE (0x07 << 2) 265 | #define CAUSE_SYSCALL (0x08 << 2) 266 | #define CAUSE_BP (0x09 << 2) 267 | #define CAUSE_RI (0x0a << 2) 268 | #define CAUSE_CPU (0x0b << 2) 269 | #define CAUSE_OV (0x0c << 2) 270 | 271 | /* 272 | 31 Error Flag (Bit30..23, and 18..13 ORed together) (Read only) 273 | 30 MAC1 Result positive 44bit overflow (max +7FFFFFFFFFFh) ;\triggered 274 | 29 MAC2 Result positive 44bit overflow (max +7FFFFFFFFFFh) ; during 275 | 28 MAC3 Result positive 44bit overflow (max +7FFFFFFFFFFh) ; calculations 276 | 27 MAC1 Result negative 44bit overflow (min -80000000000h) ; 277 | 26 MAC2 Result negative 44bit overflow (min -80000000000h) ; 278 | 25 MAC3 Result negative 44bit overflow (min -80000000000h) ;/ 279 | 24 IR1 saturated to +0000h..+7FFFh (lm=1) or to -8000h..+7FFFh (lm=0) 280 | 23 IR2 saturated to +0000h..+7FFFh (lm=1) or to -8000h..+7FFFh (lm=0) 281 | 22 IR3 saturated to +0000h..+7FFFh (lm=1) or to -8000h..+7FFFh (lm=0) 282 | 21 Color-FIFO-R saturated to +00h..+FFh 283 | 20 Color-FIFO-G saturated to +00h..+FFh 284 | 19 Color-FIFO-B saturated to +00h..+FFh 285 | 18 SZ3 or OTZ saturated to +0000h..+FFFFh 286 | 17 Divide overflow. RTPS/RTPT division result saturated to max=1FFFFh 287 | 16 MAC0 Result positive 32bit overflow (max +7FFFFFFFh) ;\triggered on 288 | 15 MAC0 Result negative 32bit overflow (min -80000000h) ;/final result 289 | 14 SX2 saturated to -0400h..+03FFh 290 | 13 SY2 saturated to -0400h..+03FFh 291 | */ 292 | 293 | #define GTEF_SY2SAT 0x00002000 294 | #define GTEF_SX2SAT 0x00004000 295 | #define GTEF_M0POVF 0x00008000 296 | #define GTEF_M0NOVF 0x00010000 297 | #define GTEF_DIVOVF 0x00020000 298 | #define GTEF_SZ3SAT 0x00040000 299 | #define GTEF_CFRSAT 0x00080000 300 | #define GTEF_CFGSAT 0x00100000 301 | #define GTEF_CFBSAT 0x00200000 302 | #define GTEF_IR3SAT 0x00400000 303 | #define GTEF_IR2SAT 0x00800000 304 | #define GTEF_IR1SAT 0x01000000 305 | #define GTEF_M3NOVF 0x02000000 306 | #define GTEF_M2NOVF 0x04000000 307 | #define GTEF_M1NOVF 0x08000000 308 | #define GTEF_M3POVF 0x10000000 309 | #define GTEF_M2POVF 0x20000000 310 | #define GTEF_M1POVF 0x40000000 311 | #define GTEF_ERRORF 0x80000000 312 | 313 | #endif -------------------------------------------------------------------------------- /psx/dev/bios.c: -------------------------------------------------------------------------------- 1 | #include "bios.h" 2 | #include "../log.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | psx_bios_t* psx_bios_create(void) { 9 | return (psx_bios_t*)malloc(sizeof(psx_bios_t)); 10 | } 11 | 12 | void psx_bios_init(psx_bios_t* bios) { 13 | memset(bios, 0, sizeof(psx_bios_t)); 14 | 15 | bios->io_base = PSX_BIOS_BEGIN; 16 | bios->io_size = PSX_BIOS_SIZE; 17 | bios->bus_delay = 18; 18 | } 19 | 20 | int psx_bios_load(psx_bios_t* bios, const char* path) { 21 | if (!path) 22 | return 0; 23 | 24 | FILE* file = fopen(path, "rb"); 25 | 26 | if (!file) 27 | return 1; 28 | 29 | // Almost all PS1 BIOS ROMs are 512 KiB in size. 30 | // There's (at least) one exception, and that is SCPH-5903. 31 | // This is a special asian model PS1 that had built-in support 32 | // for Video CD (VCD) playback. Its BIOS is double the normal 33 | // size 34 | fseek(file, 0, SEEK_END); 35 | 36 | size_t size = ftell(file); 37 | 38 | fseek(file, 0, SEEK_SET); 39 | 40 | bios->buf = malloc(size); 41 | bios->io_size = size; 42 | 43 | if (!fread(bios->buf, 1, size, file)) 44 | return 2; 45 | 46 | fclose(file); 47 | 48 | return 0; 49 | } 50 | 51 | uint32_t psx_bios_read32(psx_bios_t* bios, uint32_t offset) { 52 | return *((uint32_t*)(bios->buf + offset)); 53 | } 54 | 55 | uint16_t psx_bios_read16(psx_bios_t* bios, uint32_t offset) { 56 | return *((uint16_t*)(bios->buf + offset)); 57 | } 58 | 59 | uint8_t psx_bios_read8(psx_bios_t* bios, uint32_t offset) { 60 | return bios->buf[offset]; 61 | } 62 | 63 | void psx_bios_write32(psx_bios_t* bios, uint32_t offset, uint32_t value) { 64 | log_warn("Unhandled 32-bit BIOS write at offset %08x (%08x)", offset, value); 65 | } 66 | 67 | void psx_bios_write16(psx_bios_t* bios, uint32_t offset, uint16_t value) { 68 | log_warn("Unhandled 16-bit BIOS write at offset %08x (%04x)", offset, value); 69 | } 70 | 71 | void psx_bios_write8(psx_bios_t* bios, uint32_t offset, uint8_t value) { 72 | log_warn("Unhandled 8-bit BIOS write at offset %08x (%02x)", offset, value); 73 | } 74 | 75 | void psx_bios_destroy(psx_bios_t* bios) { 76 | free(bios->buf); 77 | free(bios); 78 | } -------------------------------------------------------------------------------- /psx/dev/bios.h: -------------------------------------------------------------------------------- 1 | #ifndef BIOS_H 2 | #define BIOS_H 3 | 4 | #include 5 | 6 | #include "../log.h" 7 | 8 | #define PSX_BIOS_SIZE 0x80000 9 | #define PSX_BIOS_BEGIN 0x1fc00000 10 | #define PSX_BIOS_END 0x1fc7ffff 11 | 12 | typedef struct { 13 | uint32_t bus_delay; 14 | uint32_t io_base, io_size; 15 | 16 | uint8_t* buf; 17 | } psx_bios_t; 18 | 19 | psx_bios_t* psx_bios_create(void); 20 | void psx_bios_init(psx_bios_t*); 21 | int psx_bios_load(psx_bios_t*, const char*); 22 | uint32_t psx_bios_read32(psx_bios_t*, uint32_t); 23 | uint16_t psx_bios_read16(psx_bios_t*, uint32_t); 24 | uint8_t psx_bios_read8(psx_bios_t*, uint32_t); 25 | void psx_bios_write32(psx_bios_t*, uint32_t, uint32_t); 26 | void psx_bios_write16(psx_bios_t*, uint32_t, uint16_t); 27 | void psx_bios_write8(psx_bios_t*, uint32_t, uint8_t); 28 | void psx_bios_destroy(psx_bios_t*); 29 | 30 | #endif -------------------------------------------------------------------------------- /psx/dev/cdrom/cue.h: -------------------------------------------------------------------------------- 1 | #ifndef CUE_H 2 | #define CUE_H 3 | 4 | #include "list.h" 5 | #include "disc.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | enum { 12 | CUE_OK = 0, 13 | CUE_FILE_NOT_FOUND, 14 | CUE_TRACK_FILE_NOT_FOUND, 15 | CUE_TRACK_READ_ERROR 16 | }; 17 | 18 | enum { 19 | CUE_4CH = 0, 20 | CUE_AIFF, 21 | CUE_AUDIO, 22 | CUE_BINARY, 23 | CUE_CATALOG, 24 | CUE_CDG, 25 | CUE_CDI_2336, 26 | CUE_CDI_2352, 27 | CUE_CDTEXTFILE, 28 | CUE_DCP, 29 | CUE_FILE, 30 | CUE_FLAGS, 31 | CUE_INDEX, 32 | CUE_ISRC, 33 | CUE_MODE1_2048, 34 | CUE_MODE1_2352, 35 | CUE_MODE2_2336, 36 | CUE_MODE2_2352, 37 | CUE_MOTOROLA, 38 | CUE_MP3, 39 | CUE_PERFORMER, 40 | CUE_POSTGAP, 41 | CUE_PRE, 42 | CUE_PREGAP, 43 | CUE_REM, 44 | CUE_SCMS, 45 | CUE_SONGWRITER, 46 | CUE_TITLE, 47 | CUE_TRACK, 48 | CUE_WAVE, 49 | CUE_NONE = 255 50 | }; 51 | 52 | enum { 53 | LD_BUFFERED, 54 | LD_FILE 55 | }; 56 | 57 | typedef struct { 58 | char* name; 59 | int buf_mode; 60 | void* buf; 61 | size_t size; 62 | uint32_t start; 63 | list_t* tracks; 64 | } cue_file_t; 65 | 66 | typedef struct { 67 | int number; 68 | int mode; 69 | 70 | int32_t index[2]; 71 | uint32_t pregap; 72 | uint32_t start; 73 | uint32_t end; 74 | 75 | cue_file_t* file; 76 | } cue_track_t; 77 | 78 | typedef struct { 79 | list_t* files; 80 | list_t* tracks; 81 | 82 | char c; 83 | FILE* file; 84 | } cue_t; 85 | 86 | cue_t* cue_create(void); 87 | void cue_init(cue_t* cue); 88 | int cue_parse(cue_t* cue, const char* path); 89 | int cue_load(cue_t* cue, int mode); 90 | 91 | // Disc interface 92 | int cue_read(cue_t* cue, uint32_t lba, void* buf); 93 | int cue_query(cue_t* cue, uint32_t lba); 94 | int cue_get_track_number(cue_t* cue, uint32_t lba); 95 | int cue_get_track_count(cue_t* cue); 96 | int cue_get_track_lba(cue_t* cue, int track); 97 | void cue_init_disc(cue_t* cue, psx_disc_t* disc); 98 | void cue_destroy(cue_t* cue); 99 | 100 | #endif -------------------------------------------------------------------------------- /psx/dev/cdrom/disc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "disc.h" 5 | #include "cue.h" 6 | 7 | #define MSF_TO_LBA(m, s, f) ((m * 4500) + (s * 75) + f) 8 | 9 | const char* disc_cd_extensions[] = { 10 | "cue", 11 | "bin", 12 | "iso", 13 | 0 14 | }; 15 | 16 | psx_disc_t* psx_disc_create(void) { 17 | return malloc(sizeof(psx_disc_t)); 18 | } 19 | 20 | int disc_get_extension(const char* path) { 21 | const char* ptr = &path[strlen(path) - 1]; 22 | int i = 0; 23 | 24 | while ((*ptr != '.') && (ptr != path)) 25 | --ptr; 26 | 27 | if (ptr == path) 28 | return CD_EXT_UNSUPPORTED; 29 | 30 | while (disc_cd_extensions[i]) { 31 | if (!strcmp(ptr + 1, disc_cd_extensions[i])) 32 | return i; 33 | 34 | ++i; 35 | } 36 | 37 | return CD_EXT_UNSUPPORTED; 38 | } 39 | 40 | int disc_get_cd_type(psx_disc_t* disc) { 41 | char buf[CD_SECTOR_SIZE]; 42 | 43 | // If the disc is smaller than 16 sectors 44 | // then it can't be a PlayStation game. 45 | // Audio discs should also have ISO volume 46 | // descriptors, so it's probably something else 47 | // entirely. 48 | if (!psx_disc_read(disc, MSF_TO_LBA(0, 2, 16), buf)) 49 | return CDT_UNKNOWN; 50 | 51 | // Check for the "PLAYSTATION" string at PVD offset 20h 52 | // Patch 20 byte so comparison is done correctly 53 | buf[0x2b] = 0; 54 | 55 | if (strncmp(&buf[0x20], "PLAYSTATION", 12)) 56 | return CDT_AUDIO; 57 | 58 | return CDT_LICENSED; 59 | } 60 | 61 | int psx_disc_open(psx_disc_t* disc, const char* path) { 62 | if (!path) 63 | return CDT_ERROR; 64 | 65 | int ext = disc_get_extension(path); 66 | 67 | return psx_disc_open_as(disc, path, ext); 68 | } 69 | 70 | int psx_disc_open_as(psx_disc_t* disc, const char* path, int type) { 71 | switch (type) { 72 | case CD_EXT_CUE: { 73 | cue_t* cue = cue_create(); 74 | 75 | cue_init(cue); 76 | cue_init_disc(cue, disc); 77 | 78 | if (cue_parse(cue, path)) 79 | return CDT_ERROR; 80 | 81 | if (cue_load(cue, LD_FILE)) 82 | return CDT_ERROR; 83 | } break; 84 | } 85 | 86 | return disc_get_cd_type(disc); 87 | } 88 | 89 | int psx_disc_read(psx_disc_t* disc, uint32_t lba, void* buf) { 90 | return disc->read_sector(disc->udata, lba, buf); 91 | } 92 | 93 | int psx_disc_query(psx_disc_t* disc, uint32_t lba) { 94 | return disc->query_sector(disc->udata, lba); 95 | } 96 | 97 | int psx_disc_get_track_number(psx_disc_t* disc, uint32_t lba) { 98 | return disc->get_track_number(disc->udata, lba); 99 | } 100 | 101 | int psx_disc_get_track_count(psx_disc_t* disc) { 102 | return disc->get_track_count(disc->udata); 103 | } 104 | 105 | int psx_disc_get_track_lba(psx_disc_t* disc, int track) { 106 | return disc->get_track_lba(disc->udata, track); 107 | } 108 | 109 | void psx_disc_destroy(psx_disc_t* disc) { 110 | disc->destroy(disc->udata); 111 | 112 | free(disc); 113 | } -------------------------------------------------------------------------------- /psx/dev/cdrom/disc.h: -------------------------------------------------------------------------------- 1 | #ifndef DISC_H 2 | #define DISC_H 3 | 4 | #include 5 | 6 | /* 7 | PSX disc reader API version 2 specification: 8 | 9 | Mandatory formats: 10 | - BIN/CUE (Multi track) 11 | - BIN (Single track) 12 | 13 | Optional (but encouraged) formats: 14 | - ISO (Raw ISO9660 images) 15 | - CHD (Compressed MAME "Hunks of Data") 16 | 17 | Optional formats: 18 | - MDS/MDF (Alcohol 120% images) 19 | */ 20 | 21 | enum { 22 | TS_FAR = 0, 23 | TS_DATA, 24 | TS_AUDIO, 25 | TS_PREGAP 26 | }; 27 | 28 | enum { 29 | CD_EXT_CUE = 0, 30 | CD_EXT_BIN, 31 | CD_EXT_ISO, 32 | CD_EXT_RAW, 33 | CD_EXT_UNSUPPORTED 34 | }; 35 | 36 | enum { 37 | CDT_ERROR = 0, 38 | CDT_LICENSED, 39 | CDT_AUDIO, 40 | CDT_UNKNOWN 41 | }; 42 | 43 | #define CD_SECTOR_SIZE 2352 44 | 45 | typedef int (*read_sector_func)(void*, uint32_t, void*); 46 | typedef int (*query_sector_func)(void*, uint32_t); 47 | typedef int (*get_track_number_func)(void*, uint32_t); 48 | typedef int (*get_track_count_func)(void*); 49 | typedef uint32_t (*get_track_lba_func)(void*, int); 50 | typedef void (*destroy_func)(void*); 51 | 52 | typedef struct { 53 | void* udata; 54 | read_sector_func read_sector; 55 | query_sector_func query_sector; 56 | get_track_number_func get_track_number; 57 | get_track_count_func get_track_count; 58 | get_track_lba_func get_track_lba; 59 | destroy_func destroy; 60 | } psx_disc_t; 61 | 62 | psx_disc_t* psx_disc_create(void); 63 | int psx_disc_open(psx_disc_t* disc, const char* path); 64 | int psx_disc_open_as(psx_disc_t* disc, const char* path, int type); 65 | int psx_disc_read(psx_disc_t* disc, uint32_t lba, void* buf); 66 | int psx_disc_query(psx_disc_t* disc, uint32_t lba); 67 | int psx_disc_get_track_number(psx_disc_t* disc, uint32_t lba); 68 | int psx_disc_get_track_count(psx_disc_t* disc); 69 | int psx_disc_get_track_lba(psx_disc_t* disc, int track); 70 | void psx_disc_close(psx_disc_t* disc); 71 | void psx_disc_destroy(psx_disc_t* disc); 72 | 73 | #endif -------------------------------------------------------------------------------- /psx/dev/cdrom/list.c: -------------------------------------------------------------------------------- 1 | #include "list.h" 2 | 3 | #include 4 | #include 5 | 6 | list_t* list_create(void) { 7 | list_t* list = malloc(sizeof(list_t)); 8 | 9 | list_init(list); 10 | 11 | return list; 12 | } 13 | 14 | void list_init(list_t* list) { 15 | list->first = NULL; 16 | list->last = NULL; 17 | list->size = 0; 18 | } 19 | 20 | void list_push_front(list_t* list, void* data) { 21 | node_t* node = malloc(sizeof(node_t)); 22 | 23 | node->data = data; 24 | node->next = list->first; 25 | 26 | list->first = node; 27 | 28 | if (!list->last) 29 | list->last = list->first; 30 | 31 | ++list->size; 32 | } 33 | 34 | void list_push_back(list_t* list, void* data) { 35 | node_t* node = malloc(sizeof(node_t)); 36 | 37 | node->data = data; 38 | node->next = NULL; 39 | 40 | if (!list->last) { 41 | list->first = node; 42 | list->last = node; 43 | } else { 44 | list->last->next = node; 45 | list->last = node; 46 | } 47 | 48 | ++list->size; 49 | } 50 | 51 | void list_pop_front(list_t* list) { 52 | if (!list->first) 53 | return; 54 | 55 | node_t* next = list->first->next; 56 | 57 | free(list->first); 58 | 59 | list->first = next; 60 | 61 | --list->size; 62 | } 63 | 64 | void list_pop_back(list_t* list) { 65 | if (!list->last) 66 | return; 67 | 68 | node_t* node = list->first; 69 | 70 | while (node->next != list->last) 71 | node = node->next; 72 | 73 | free(node->next); 74 | 75 | node->next = NULL; 76 | 77 | list->last = node; 78 | } 79 | 80 | node_t* list_front(list_t* list) { 81 | return list->first; 82 | } 83 | 84 | node_t* list_back(list_t* list) { 85 | return list->last; 86 | } 87 | 88 | node_t* list_at(list_t* list, int index) { 89 | if (index > list->size) 90 | return NULL; 91 | 92 | node_t* node = list->first; 93 | 94 | for (int i = 0; i < index; i++) 95 | node = node->next; 96 | 97 | return node; 98 | } 99 | 100 | void list_iterate(list_t* list, void (*func)(void*)) { 101 | node_t* node = list->first; 102 | 103 | while (node) { 104 | func(node->data); 105 | 106 | node = node->next; 107 | } 108 | } 109 | 110 | void list_destroy(list_t* list) { 111 | node_t* node = list->first; 112 | 113 | while (node) { 114 | node_t* next = node->next; 115 | 116 | free(node); 117 | 118 | node = next; 119 | } 120 | 121 | free(list); 122 | } -------------------------------------------------------------------------------- /psx/dev/cdrom/list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | 4 | #include 5 | 6 | typedef struct node_t node_t; 7 | 8 | typedef struct node_t { 9 | node_t* next; 10 | void* data; 11 | } node_t; 12 | 13 | typedef struct { 14 | node_t* first; 15 | node_t* last; 16 | size_t size; 17 | } list_t; 18 | 19 | list_t* list_create(void); 20 | void list_init(list_t* list); 21 | void list_push_front(list_t* list, void* data); 22 | void list_push_back(list_t* list, void* data); 23 | void list_pop_front(list_t* list); 24 | void list_pop_back(list_t* list); 25 | node_t* list_front(list_t* list); 26 | node_t* list_back(list_t* list); 27 | node_t* list_at(list_t* list, int index); 28 | void list_iterate(list_t* list, void (*func)(void*)); 29 | void list_destroy(list_t* list); 30 | 31 | #endif -------------------------------------------------------------------------------- /psx/dev/cdrom/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "queue.h" 6 | 7 | queue_t* queue_create(void) { 8 | return malloc(sizeof(queue_t)); 9 | } 10 | 11 | void queue_init(queue_t* queue, size_t size) { 12 | queue->buf = malloc(size); 13 | queue->read_index = 0; 14 | queue->write_index = 0; 15 | queue->size = size; 16 | } 17 | 18 | void queue_push(queue_t* queue, uint8_t value) { 19 | if (queue_is_full(queue)) 20 | return; 21 | 22 | queue->buf[queue->write_index++] = value; 23 | } 24 | 25 | uint8_t queue_pop(queue_t* queue) { 26 | if (queue_is_empty(queue)) 27 | return 0; 28 | 29 | uint8_t data = queue->buf[queue->read_index++]; 30 | 31 | if (queue_is_empty(queue)) 32 | queue_reset(queue); 33 | 34 | return data; 35 | } 36 | 37 | uint8_t queue_peek(queue_t* queue) { 38 | if (queue_is_empty(queue)) 39 | return 0; 40 | 41 | return queue->buf[queue->read_index]; 42 | } 43 | 44 | int queue_is_empty(queue_t* queue) { 45 | return queue->read_index == queue->write_index; 46 | } 47 | 48 | int queue_is_full(queue_t* queue) { 49 | return queue->write_index == queue->size; 50 | } 51 | 52 | void queue_reset(queue_t* queue) { 53 | queue->write_index = 0; 54 | queue->read_index = 0; 55 | } 56 | 57 | void queue_clear(queue_t* queue) { 58 | for (int i = 0; i < queue->write_index; i++) 59 | queue->buf[i] = 0; 60 | 61 | queue_reset(queue); 62 | } 63 | 64 | int queue_size(queue_t* queue) { 65 | return queue->write_index - queue->read_index; 66 | } 67 | 68 | int queue_max_size(queue_t* queue) { 69 | return queue->size; 70 | } 71 | 72 | void queue_destroy(queue_t* queue) { 73 | free(queue->buf); 74 | free(queue); 75 | } -------------------------------------------------------------------------------- /psx/dev/cdrom/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef QUEUE_H 2 | #define QUEUE_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | uint8_t* buf; 9 | size_t read_index; 10 | size_t write_index; 11 | size_t size; 12 | } queue_t; 13 | 14 | queue_t* queue_create(void); 15 | void queue_init(queue_t* queue, size_t size); 16 | void queue_push(queue_t* queue, uint8_t value); 17 | uint8_t queue_pop(queue_t* queue); 18 | uint8_t queue_peek(queue_t* queue); 19 | int queue_is_empty(queue_t* queue); 20 | int queue_is_full(queue_t* queue); 21 | void queue_reset(queue_t* queue); 22 | void queue_clear(queue_t* queue); 23 | int queue_size(queue_t* queue); 24 | int queue_max_size(queue_t* queue); 25 | void queue_destroy(queue_t* queue); 26 | 27 | #endif -------------------------------------------------------------------------------- /psx/dev/dma.h: -------------------------------------------------------------------------------- 1 | #ifndef DMA_H 2 | #define DMA_H 3 | 4 | #include 5 | 6 | #define PSX_DMAR_BEGIN 0x1f801080 7 | #define PSX_DMAR_SIZE 0x80 8 | #define PSX_DMAR_END 0x1f8010ff 9 | 10 | #include "../bus.h" 11 | #include "ic.h" 12 | 13 | typedef struct { 14 | uint32_t madr; 15 | uint32_t bcr; 16 | uint32_t chcr; 17 | } dma_channel_t; 18 | 19 | typedef struct { 20 | uint32_t bus_delay; 21 | uint32_t io_base, io_size; 22 | 23 | psx_bus_t* bus; 24 | psx_ic_t* ic; 25 | 26 | dma_channel_t mdec_in; 27 | dma_channel_t mdec_out; 28 | dma_channel_t gpu; 29 | dma_channel_t cdrom; 30 | dma_channel_t spu; 31 | dma_channel_t pio; 32 | dma_channel_t otc; 33 | 34 | int mdec_in_irq_delay; 35 | int mdec_out_irq_delay; 36 | int cdrom_irq_delay; 37 | int spu_irq_delay; 38 | int gpu_irq_delay; 39 | int otc_irq_delay; 40 | 41 | uint32_t dpcr; 42 | uint32_t dicr; 43 | } psx_dma_t; 44 | 45 | psx_dma_t* psx_dma_create(void); 46 | void psx_dma_init(psx_dma_t*, psx_bus_t*, psx_ic_t*); 47 | void psx_dma_do_mdec_in(psx_dma_t*); 48 | void psx_dma_do_mdec_out(psx_dma_t*); 49 | void psx_dma_do_gpu(psx_dma_t*); 50 | void psx_dma_do_cdrom(psx_dma_t*); 51 | void psx_dma_do_spu(psx_dma_t*); 52 | void psx_dma_do_pio(psx_dma_t*); 53 | void psx_dma_do_otc(psx_dma_t*); 54 | void psx_dma_perform(psx_dma_t*, int); 55 | uint32_t psx_dma_read32(psx_dma_t*, uint32_t); 56 | uint16_t psx_dma_read16(psx_dma_t*, uint32_t); 57 | uint8_t psx_dma_read8(psx_dma_t*, uint32_t); 58 | void psx_dma_write32(psx_dma_t*, uint32_t, uint32_t); 59 | void psx_dma_write16(psx_dma_t*, uint32_t, uint16_t); 60 | void psx_dma_write8(psx_dma_t*, uint32_t, uint8_t); 61 | void psx_dma_destroy(psx_dma_t*); 62 | void psx_dma_update(psx_dma_t*, int); 63 | 64 | typedef void (*psx_dma_do_fn_t)(psx_dma_t*); 65 | 66 | /* 67 | 0-2 DMA0, MDECin Priority (0..7; 0=Highest, 7=Lowest) 68 | 3 DMA0, MDECin Master Enable (0=Disable, 1=Enable) 69 | 4-6 DMA1, MDECout Priority (0..7; 0=Highest, 7=Lowest) 70 | 7 DMA1, MDECout Master Enable (0=Disable, 1=Enable) 71 | 8-10 DMA2, GPU Priority (0..7; 0=Highest, 7=Lowest) 72 | 11 DMA2, GPU Master Enable (0=Disable, 1=Enable) 73 | 12-14 DMA3, CDROM Priority (0..7; 0=Highest, 7=Lowest) 74 | 15 DMA3, CDROM Master Enable (0=Disable, 1=Enable) 75 | 16-18 DMA4, SPU Priority (0..7; 0=Highest, 7=Lowest) 76 | 19 DMA4, SPU Master Enable (0=Disable, 1=Enable) 77 | 20-22 DMA5, PIO Priority (0..7; 0=Highest, 7=Lowest) 78 | 23 DMA5, PIO Master Enable (0=Disable, 1=Enable) 79 | 24-26 DMA6, OTC Priority (0..7; 0=Highest, 7=Lowest) 80 | 27 DMA6, OTC Master Enable (0=Disable, 1=Enable) 81 | 28-30 Unknown, Priority Offset or so? (R/W) 82 | 31 Unknown, no effect? (R/W) 83 | */ 84 | 85 | #define DPCR_DMA0EN 0x00000008 86 | #define DPCR_DMA1EN 0x00000080 87 | #define DPCR_DMA2EN 0x00000800 88 | #define DPCR_DMA3EN 0x00008000 89 | #define DPCR_DMA4EN 0x00080000 90 | #define DPCR_DMA5EN 0x00800000 91 | #define DPCR_DMA6EN 0x08000000 92 | 93 | /* 94 | 0 Transfer Direction (0=To Main RAM, 1=From Main RAM) 95 | 1 Memory Address Step (0=Forward;+4, 1=Backward;-4) 96 | 2-7 Not used (always zero) 97 | 8 Chopping Enable (0=Normal, 1=Chopping; run CPU during DMA gaps) 98 | 9-10 SyncMode, Transfer Synchronisation/Mode (0-3): 99 | 0 Start immediately and transfer all at once (used for CDROM, OTC) 100 | 1 Sync blocks to DMA requests (used for MDEC, SPU, and GPU-data) 101 | 2 Linked-List mode (used for GPU-command-lists) 102 | 3 Reserved (not used) 103 | 11-15 Not used (always zero) 104 | 16-18 Chopping DMA Window Size (1 SHL N words) 105 | 19 Not used (always zero) 106 | 20-22 Chopping CPU Window Size (1 SHL N clks) 107 | 23 Not used (always zero) 108 | 24 Start/Busy (0=Stopped/Completed, 1=Start/Enable/Busy) 109 | 25-27 Not used (always zero) 110 | 28 Start/Trigger (0=Normal, 1=Manual Start; use for SyncMode=0) 111 | 29 Unknown (R/W) Pause? (0=No, 1=Pause?) (For SyncMode=0 only?) 112 | 30 Unknown (R/W) 113 | 31 Not used (always zero) 114 | */ 115 | 116 | #define CHCR_TDIR_MASK 0x00000001 117 | #define CHCR_STEP_MASK 0x00000002 118 | #define CHCR_CPEN_MASK 0x00000100 119 | #define CHCR_SYNC_MASK 0x00000600 120 | #define CHCR_CDWS_MASK 0x00070000 121 | #define CHCR_CCWS_MASK 0x00380000 122 | #define CHCR_BUSY_MASK 0x01000000 123 | #define CHCR_TRIG_MASK 0x10000000 124 | 125 | #define SYNC_SHIF 9 126 | #define CDWS_SHIF 16 127 | #define CCWS_SHIF 19 128 | 129 | #define CHCR_TDIR(c) (dma->c.chcr & CHCR_TDIR_MASK) 130 | #define CHCR_STEP(c) (dma->c.chcr & CHCR_STEP_MASK) 131 | #define CHCR_CPEN(c) (dma->c.chcr & CHCR_CPEN_MASK) 132 | #define CHCR_SYNC(c) ((dma->c.chcr & CHCR_SYNC_MASK) >> SYNC_SHIF) 133 | #define CHCR_CDWS(c) ((dma->c.chcr & CHCR_CDWS_MASK) >> CDWS_SHIF) 134 | #define CHCR_CCWS(c) ((dma->c.chcr & CHCR_CCWS_MASK) >> CCWS_SHIF) 135 | #define CHCR_BUSY(c) (dma->c.chcr & CHCR_BUSY_MASK) 136 | #define CHCR_TRIG(c) (dma->c.chcr & CHCR_TRIG_MASK) 137 | 138 | #define BCR_SIZE(c) (dma->c.bcr & 0xffff) 139 | #define BCR_BCNT(c) ((dma->c.bcr >> 16) & 0xffff) 140 | 141 | /* 142 | 0-5 Unknown (read/write-able) 143 | 6-14 Not used (always zero) 144 | 15 Force IRQ (sets bit31) (0=None, 1=Force Bit31=1) 145 | 16-22 IRQ Enable setting bit24-30 upon DMA0..DMA6 (0=None, 1=Enable) 146 | 23 IRQ Enable setting bit31 when bit24-30=nonzero (0=None, 1=Enable) 147 | 24-30 IRQ Flags for DMA0..DMA6 (Write 1 to reset) (0=None, 1=IRQ) 148 | 31 IRQ Signal (0-to-1 triggers 1F801070h.bit3) (0=None, 1=IRQ) (R) 149 | */ 150 | 151 | #define DICR_FORCE 0x00008000 152 | #define DICR_FLGEN 0x007f0000 153 | #define DICR_IRQEN 0x00800000 154 | #define DICR_FLAGS 0x7f000000 155 | #define DICR_IRQSI 0x80000000 156 | #define DICR_DMA0EN 0x00010000 157 | #define DICR_DMA1EN 0x00020000 158 | #define DICR_DMA2EN 0x00040000 159 | #define DICR_DMA3EN 0x00080000 160 | #define DICR_DMA4EN 0x00100000 161 | #define DICR_DMA5EN 0x00200000 162 | #define DICR_DMA6EN 0x00400000 163 | #define DICR_DMA0FL 0x01000000 164 | #define DICR_DMA1FL 0x02000000 165 | #define DICR_DMA2FL 0x04000000 166 | #define DICR_DMA3FL 0x08000000 167 | #define DICR_DMA4FL 0x10000000 168 | #define DICR_DMA5FL 0x20000000 169 | #define DICR_DMA6FL 0x40000000 170 | 171 | #endif -------------------------------------------------------------------------------- /psx/dev/exp1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../log.h" 6 | #include "exp1.h" 7 | 8 | psx_exp1_t* psx_exp1_create(void) { 9 | return (psx_exp1_t*)malloc(sizeof(psx_exp1_t)); 10 | } 11 | 12 | int psx_exp1_init(psx_exp1_t* exp1, psx_mc1_t* mc1, const char* path) { 13 | memset(exp1, 0, sizeof(psx_exp1_t)); 14 | 15 | exp1->io_base = PSX_EXP1_BEGIN; 16 | exp1->io_size = PSX_EXP1_SIZE; 17 | 18 | exp1->mc1 = mc1; 19 | exp1->rom = (uint8_t*)malloc(PSX_EXP1_SIZE); 20 | 21 | memset(exp1->rom, 0xff, PSX_EXP1_SIZE); 22 | 23 | if (path) 24 | return psx_exp1_load(exp1, path); 25 | 26 | return 0; 27 | } 28 | 29 | int psx_exp1_load(psx_exp1_t* exp1, const char* path) { 30 | if (!path) 31 | return 0; 32 | 33 | FILE* file = fopen(path, "rb"); 34 | 35 | if (!file) 36 | return 1; 37 | 38 | if (!fread(exp1->rom, 1, PSX_EXP1_SIZE, file)) 39 | return 2; 40 | 41 | fclose(file); 42 | 43 | return 0; 44 | } 45 | 46 | uint32_t psx_exp1_read32(psx_exp1_t* exp1, uint32_t offset) { 47 | return *((uint32_t*)(exp1->rom + offset)); 48 | } 49 | 50 | uint16_t psx_exp1_read16(psx_exp1_t* exp1, uint32_t offset) { 51 | return *((uint16_t*)(exp1->rom + offset)); 52 | } 53 | 54 | uint8_t psx_exp1_read8(psx_exp1_t* exp1, uint32_t offset) { 55 | return exp1->rom[offset]; 56 | } 57 | 58 | void psx_exp1_write32(psx_exp1_t* exp1, uint32_t offset, uint32_t value) { 59 | log_warn("Unhandled 32-bit EXP1 write at offset %08x (%08x)", offset, value); 60 | } 61 | 62 | void psx_exp1_write16(psx_exp1_t* exp1, uint32_t offset, uint16_t value) { 63 | log_warn("Unhandled 16-bit EXP1 write at offset %08x (%04x)", offset, value); 64 | } 65 | 66 | void psx_exp1_write8(psx_exp1_t* exp1, uint32_t offset, uint8_t value) { 67 | log_warn("Unhandled 8-bit EXP1 write at offset %08x (%02x)", offset, value); 68 | } 69 | 70 | void psx_exp1_destroy(psx_exp1_t* exp1) { 71 | free(exp1->rom); 72 | free(exp1); 73 | } -------------------------------------------------------------------------------- /psx/dev/exp1.h: -------------------------------------------------------------------------------- 1 | #ifndef EXP1_H 2 | #define EXP1_H 3 | 4 | #include 5 | 6 | #include "mc1.h" 7 | 8 | #define PSX_EXP1_BEGIN 0x1f000000 9 | #define PSX_EXP1_SIZE 0x80000 10 | #define PSX_EXP1_END 0x1f080000 11 | 12 | typedef struct { 13 | uint32_t bus_delay; 14 | uint32_t io_base, io_size; 15 | 16 | psx_mc1_t* mc1; 17 | uint8_t* rom; 18 | } psx_exp1_t; 19 | 20 | psx_exp1_t* psx_exp1_create(void); 21 | int psx_exp1_init(psx_exp1_t*, psx_mc1_t*, const char*); 22 | int psx_exp1_load(psx_exp1_t*, const char*); 23 | uint32_t psx_exp1_read32(psx_exp1_t*, uint32_t); 24 | uint16_t psx_exp1_read16(psx_exp1_t*, uint32_t); 25 | uint8_t psx_exp1_read8(psx_exp1_t*, uint32_t); 26 | void psx_exp1_write32(psx_exp1_t*, uint32_t, uint32_t); 27 | void psx_exp1_write16(psx_exp1_t*, uint32_t, uint16_t); 28 | void psx_exp1_write8(psx_exp1_t*, uint32_t, uint8_t); 29 | void psx_exp1_destroy(psx_exp1_t*); 30 | 31 | #endif -------------------------------------------------------------------------------- /psx/dev/exp2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../log.h" 6 | #include "exp2.h" 7 | 8 | psx_exp2_t* psx_exp2_create(void) { 9 | return (psx_exp2_t*)malloc(sizeof(psx_exp2_t)); 10 | } 11 | 12 | void psx_exp2_init(psx_exp2_t* exp2, exp2_tty_tx atcons_tx, exp2_tty_tx duart_tx) { 13 | memset(exp2, 0, sizeof(psx_exp2_t)); 14 | 15 | exp2->io_base = PSX_EXP2_BEGIN; 16 | exp2->io_size = PSX_EXP2_SIZE; 17 | exp2->atcons_tx = atcons_tx; 18 | exp2->duart_tx = duart_tx; 19 | } 20 | 21 | void psx_exp2_atcons_put(psx_exp2_t* exp2, char c) { 22 | exp2->atc_stat |= 0x10; 23 | exp2->atc_rx = c; 24 | } 25 | 26 | void psx_exp2_duart_put(psx_exp2_t* exp2, char c) { 27 | /* To-do */ 28 | } 29 | 30 | uint32_t psx_exp2_read32(psx_exp2_t* exp2, uint32_t offset) { 31 | return 0; 32 | } 33 | 34 | uint16_t psx_exp2_read16(psx_exp2_t* exp2, uint32_t offset) { 35 | return 0; 36 | } 37 | 38 | uint8_t psx_exp2_read8(psx_exp2_t* exp2, uint32_t offset) { 39 | switch (offset) { 40 | case EXP2_DTL_ATC_STAT: 41 | return exp2->atc_stat | 8; 42 | 43 | case EXP2_DTL_ATC_DATA: 44 | exp2->atc_stat &= 0xef; 45 | return exp2->atc_rx; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | void psx_exp2_write32(psx_exp2_t* exp2, uint32_t offset, uint32_t value) { 52 | log_warn("Unhandled 32-bit EXP2 write at offset %08x (%08x)", offset, value); 53 | } 54 | 55 | void psx_exp2_write16(psx_exp2_t* exp2, uint32_t offset, uint16_t value) { 56 | log_warn("Unhandled 16-bit EXP2 write at offset %08x (%04x)", offset, value); 57 | } 58 | 59 | void psx_exp2_write8(psx_exp2_t* exp2, uint32_t offset, uint8_t value) { 60 | switch (offset) { 61 | case EXP2_DTL_ATC_DATA: 62 | if (exp2->atcons_tx) 63 | exp2->atcons_tx(exp2->atcons_udata, value); 64 | return; 65 | break; 66 | 67 | case EXP2_LED: 68 | case EXP2_POST: 69 | case EXP2_POST2: 70 | // To-do: Do something with this data 71 | return; 72 | break; 73 | } 74 | 75 | log_warn("Unhandled 8-bit EXP2 write at offset %08x (%02x)", offset, value); 76 | } 77 | 78 | void psx_exp2_destroy(psx_exp2_t* exp2) { 79 | free(exp2); 80 | } -------------------------------------------------------------------------------- /psx/dev/exp2.h: -------------------------------------------------------------------------------- 1 | #ifndef EXP2_H 2 | #define EXP2_H 3 | 4 | #include 5 | 6 | #define PSX_EXP2_BEGIN 0x1f802000 7 | #define PSX_EXP2_SIZE 0x1fe000 8 | #define PSX_EXP2_END 0x1f9fffff 9 | 10 | #define EXP2_DTL_ATC_STAT 0x00 // 1f802000 11 | #define EXP2_DTL_ATC_DATA 0x02 // 1f802002 12 | #define EXP2_DTL_HDATA 0x04 // 1f802004 13 | #define EXP2_DTL_SEC_IRQ10 0x30 // 1f802030 14 | #define EXP2_DTL_IRQ_CTRL 0x32 // 1f802032 15 | #define EXP2_DTL_BOOT_DIP 0x40 // 1f802040 16 | #define EXP2_POST 0x41 // 1f802041 17 | #define EXP2_LED 0x42 // 1f802042 18 | #define EXP2_POST2 0x70 // 1f802070 19 | 20 | typedef void (*exp2_tty_tx)(void*, uint8_t); 21 | 22 | typedef struct { 23 | uint32_t bus_delay; 24 | uint32_t io_base, io_size; 25 | 26 | void* duart_udata; 27 | void* atcons_udata; 28 | 29 | exp2_tty_tx duart_tx; 30 | exp2_tty_tx atcons_tx; 31 | 32 | uint8_t atc_stat; 33 | uint8_t atc_rx; 34 | } psx_exp2_t; 35 | 36 | psx_exp2_t* psx_exp2_create(void); 37 | void psx_exp2_init(psx_exp2_t*, exp2_tty_tx atcons_tx, exp2_tty_tx duart_tx); 38 | void psx_exp2_atcons_put(psx_exp2_t*, char); 39 | void psx_exp2_duart_put(psx_exp2_t*, char); 40 | uint32_t psx_exp2_read32(psx_exp2_t*, uint32_t); 41 | uint16_t psx_exp2_read16(psx_exp2_t*, uint32_t); 42 | uint8_t psx_exp2_read8(psx_exp2_t*, uint32_t); 43 | void psx_exp2_write32(psx_exp2_t*, uint32_t, uint32_t); 44 | void psx_exp2_write16(psx_exp2_t*, uint32_t, uint16_t); 45 | void psx_exp2_write8(psx_exp2_t*, uint32_t, uint8_t); 46 | void psx_exp2_destroy(psx_exp2_t*); 47 | 48 | #endif -------------------------------------------------------------------------------- /psx/dev/gpu.h: -------------------------------------------------------------------------------- 1 | #ifndef GPU_H 2 | #define GPU_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ic.h" 9 | 10 | #define PSX_GPU_BEGIN 0x1f801810 11 | #define PSX_GPU_SIZE 0x8 12 | #define PSX_GPU_END 0x1f801814 13 | 14 | #define PSX_GPU_FB_WIDTH 1024 15 | #define PSX_GPU_FB_HEIGHT 512 16 | 17 | // Use this when updating your texture 18 | #define PSX_GPU_FB_STRIDE 2048 19 | 20 | // 0x100000 * 2 21 | #define PSX_GPU_VRAM_SIZE (0x200000) 22 | 23 | #define PSX_GPU_CLOCK_NTSC 53693175 // 53.693175 MHz 24 | #define PSX_GPU_CLOCK_FREQ_NTSC 53.693175f // 53.693175 MHz 25 | #define PSX_GPU_CLOCK_FREQ_PAL 53.203425f // 53.203425 MHz 26 | 27 | enum { 28 | GPU_EVENT_DMODE, 29 | GPU_EVENT_VBLANK, 30 | GPU_EVENT_VBLANK_END, 31 | GPU_EVENT_HBLANK, 32 | GPU_EVENT_HBLANK_END, 33 | GPU_EVENT_VBLANK_TIMER 34 | }; 35 | 36 | enum { 37 | GPU_STATE_RECV_CMD, 38 | GPU_STATE_RECV_ARGS, 39 | GPU_STATE_RECV_DATA 40 | }; 41 | 42 | struct psx_gpu_t; 43 | 44 | typedef struct psx_gpu_t psx_gpu_t; 45 | 46 | typedef void (*psx_gpu_cmd_t)(psx_gpu_t*); 47 | typedef void (*psx_gpu_event_callback_t)(psx_gpu_t*); 48 | 49 | enum { 50 | RS_VARIABLE, 51 | RS_1X1, 52 | RS_8X8, 53 | RS_16X16 54 | }; 55 | 56 | enum { 57 | RA_RAW = 0x01, 58 | RA_TRANSP = 0x02, 59 | RA_TEXTURED = 0x04 60 | }; 61 | 62 | enum { 63 | PA_RAW = 0x01, 64 | PA_TRANSP = 0x02, 65 | PA_TEXTURED = 0x04, 66 | PA_QUAD = 0x08, 67 | PA_SHADED = 0x10 68 | }; 69 | 70 | typedef struct { 71 | int16_t x, y; 72 | uint32_t c; 73 | uint8_t tx, ty; 74 | } vertex_t; 75 | 76 | typedef struct { 77 | uint8_t attrib; 78 | vertex_t v[4]; 79 | uint16_t clut, texp; 80 | } poly_data_t; 81 | 82 | typedef struct { 83 | uint8_t attrib; 84 | vertex_t v0; 85 | uint16_t clut; 86 | uint16_t width, height; 87 | } rect_data_t; 88 | 89 | struct psx_gpu_t { 90 | uint32_t bus_delay; 91 | uint32_t io_base, io_size; 92 | 93 | void* udata[4]; 94 | 95 | uint16_t* vram; 96 | uint16_t* empty; 97 | int display_enable; 98 | 99 | // State data 100 | uint32_t buf[16]; 101 | uint32_t recv_data; 102 | int buf_index; 103 | int cmd_args_remaining; 104 | int cmd_data_remaining; 105 | int line_done; 106 | vertex_t prev_line_vertex; 107 | 108 | // Command counters 109 | uint32_t color; 110 | uint32_t xpos, ypos; 111 | uint32_t xsiz, ysiz; 112 | uint32_t tsiz; 113 | uint32_t addr; 114 | uint32_t xcnt, ycnt; 115 | vertex_t v0, v1, v2, v3; 116 | uint32_t pal, texp; 117 | uint32_t c0_xcnt, c0_ycnt; 118 | uint32_t c0_addr; 119 | int c0_xsiz, c0_ysiz; 120 | int c0_tsiz; 121 | int gp1_10h_req; 122 | 123 | // GPU state 124 | uint32_t state; 125 | 126 | uint32_t display_mode; 127 | uint32_t gpuread; 128 | uint32_t gpustat; 129 | 130 | // Drawing area 131 | uint32_t draw_x1, draw_y1; 132 | uint32_t draw_x2, draw_y2; 133 | 134 | // Drawing offset 135 | int32_t off_x, off_y; 136 | 137 | // Texture Window 138 | uint32_t texw_mx, texw_my; 139 | uint32_t texw_ox, texw_oy; 140 | 141 | // CLUT offset 142 | uint32_t clut_x, clut_y; 143 | 144 | // Texture page 145 | uint32_t texp_x, texp_y; 146 | uint32_t texp_d; 147 | 148 | // Display area 149 | uint32_t disp_x, disp_y; 150 | uint32_t disp_x1, disp_x2; 151 | uint32_t disp_y1, disp_y2; 152 | 153 | // Timing and IRQs 154 | float cycles; 155 | int line; 156 | 157 | psx_ic_t* ic; 158 | 159 | psx_gpu_event_callback_t event_cb_table[8]; 160 | }; 161 | 162 | psx_gpu_t* psx_gpu_create(void); 163 | void psx_gpu_init(psx_gpu_t*, psx_ic_t*); 164 | uint32_t psx_gpu_read32(psx_gpu_t*, uint32_t); 165 | uint16_t psx_gpu_read16(psx_gpu_t*, uint32_t); 166 | uint8_t psx_gpu_read8(psx_gpu_t*, uint32_t); 167 | void psx_gpu_write32(psx_gpu_t*, uint32_t, uint32_t); 168 | void psx_gpu_write16(psx_gpu_t*, uint32_t, uint16_t); 169 | void psx_gpu_write8(psx_gpu_t*, uint32_t, uint8_t); 170 | void psx_gpu_destroy(psx_gpu_t*); 171 | void psx_gpu_set_udata(psx_gpu_t*, int, void*); 172 | void psx_gpu_set_event_callback(psx_gpu_t*, int, psx_gpu_event_callback_t); 173 | void* psx_gpu_get_display_buffer(psx_gpu_t*); 174 | void psx_gpu_update(psx_gpu_t*, int); 175 | 176 | #endif -------------------------------------------------------------------------------- /psx/dev/ic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "ic.h" 6 | 7 | #include "../log.h" 8 | 9 | psx_ic_t* psx_ic_create(void) { 10 | return (psx_ic_t*)malloc(sizeof(psx_ic_t)); 11 | } 12 | 13 | void psx_ic_init(psx_ic_t* ic, psx_cpu_t* cpu) { 14 | memset(ic, 0, sizeof(psx_ic_t)); 15 | 16 | ic->io_base = PSX_IC_BEGIN; 17 | ic->io_size = PSX_IC_SIZE; 18 | 19 | ic->stat = 0x00000000; 20 | ic->mask = 0x00000000; 21 | 22 | ic->cpu = cpu; 23 | } 24 | 25 | uint32_t psx_ic_read32(psx_ic_t* ic, uint32_t offset) { 26 | switch (offset) { 27 | case 0x00: return ic->stat; 28 | case 0x04: return ic->mask; 29 | } 30 | 31 | log_fatal("Unhandled 32-bit IC read at offset %08x", offset); 32 | 33 | return 0x0; 34 | } 35 | 36 | uint16_t psx_ic_read16(psx_ic_t* ic, uint32_t offset) { 37 | switch (offset) { 38 | case 0x00: return (ic->stat >> 0 ) & 0xffff; 39 | case 0x02: return (ic->stat >> 16) & 0xffff; 40 | case 0x04: return (ic->mask >> 0 ) & 0xffff; 41 | case 0x06: return (ic->mask >> 16) & 0xffff; 42 | } 43 | 44 | return 0x0; 45 | } 46 | 47 | uint8_t psx_ic_read8(psx_ic_t* ic, uint32_t offset) { 48 | switch (offset) { 49 | case 0x00: return (ic->stat >> 0 ) & 0xff; 50 | case 0x01: return (ic->stat >> 8 ) & 0xff; 51 | case 0x02: return (ic->stat >> 16) & 0xff; 52 | case 0x03: return (ic->stat >> 24) & 0xff; 53 | case 0x04: return (ic->mask >> 0 ) & 0xff; 54 | case 0x05: return (ic->mask >> 8 ) & 0xff; 55 | case 0x06: return (ic->mask >> 16) & 0xff; 56 | case 0x07: return (ic->mask >> 24) & 0xff; 57 | } 58 | 59 | return 0x0; 60 | } 61 | 62 | void psx_ic_write32(psx_ic_t* ic, uint32_t offset, uint32_t value) { 63 | switch (offset) { 64 | case 0x00: ic->stat &= value; break; 65 | case 0x04: ic->mask = value; break; 66 | 67 | default: { 68 | log_fatal("Unhandled 32-bit IC write at offset %08x (%08x)", offset, value); 69 | } break; 70 | } 71 | 72 | // Emulate acknowledge 73 | if (!(ic->stat & ic->mask)) { 74 | ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2; 75 | } else { 76 | psx_cpu_set_irq_pending(ic->cpu); 77 | } 78 | } 79 | 80 | void psx_ic_write16(psx_ic_t* ic, uint32_t offset, uint16_t value) { 81 | switch (offset) { 82 | case 0x00: ic->stat &= ((uint32_t)value) << 0 ; break; 83 | case 0x02: ic->stat &= ((uint32_t)value) << 16; break; 84 | case 0x04: ic->mask = ((uint32_t)value) << 0 ; break; 85 | case 0x06: ic->mask = ((uint32_t)value) << 16; break; 86 | } 87 | 88 | // Emulate acknowledge 89 | if (!(ic->stat & ic->mask)) { 90 | ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2; 91 | } else { 92 | psx_cpu_set_irq_pending(ic->cpu); 93 | } 94 | } 95 | 96 | void psx_ic_write8(psx_ic_t* ic, uint32_t offset, uint8_t value) { 97 | switch (offset) { 98 | case 0x00: ic->stat &= ((uint32_t)value) << 0 ; break; 99 | case 0x01: ic->stat &= ((uint32_t)value) << 8 ; break; 100 | case 0x02: ic->stat &= ((uint32_t)value) << 16; break; 101 | case 0x03: ic->stat &= ((uint32_t)value) << 24; break; 102 | case 0x04: ic->mask = ((uint32_t)value) << 0 ; break; 103 | case 0x05: ic->mask = ((uint32_t)value) << 8 ; break; 104 | case 0x06: ic->mask = ((uint32_t)value) << 16; break; 105 | case 0x07: ic->mask = ((uint32_t)value) << 24; break; 106 | } 107 | 108 | // Emulate acknowledge 109 | if (!(ic->stat & ic->mask)) { 110 | ic->cpu->cop0_r[COP0_CAUSE] &= ~SR_IM2; 111 | } else { 112 | psx_cpu_set_irq_pending(ic->cpu); 113 | } 114 | } 115 | 116 | void psx_ic_irq(psx_ic_t* ic, int id) { 117 | ic->stat |= id; 118 | 119 | if (ic->mask & ic->stat) 120 | psx_cpu_set_irq_pending(ic->cpu); 121 | } 122 | 123 | void psx_ic_destroy(psx_ic_t* ic) { 124 | free(ic); 125 | } -------------------------------------------------------------------------------- /psx/dev/ic.h: -------------------------------------------------------------------------------- 1 | #ifndef IC_H 2 | #define IC_H 3 | 4 | #include 5 | 6 | #include "../cpu.h" 7 | 8 | #define PSX_IC_BEGIN 0x1f801070 9 | #define PSX_IC_SIZE 0x8 10 | #define PSX_IC_END 0x1F801077 11 | 12 | /* 13 | 0 IRQ0 VBLANK (PAL=50Hz, NTSC=60Hz) 14 | 1 IRQ1 GPU Can be requested via GP0(1Fh) command (rarely used) 15 | 2 IRQ2 CDROM 16 | 3 IRQ3 DMA 17 | 4 IRQ4 TMR0 Timer 0 aka Root Counter 0 (Sysclk or Dotclk) 18 | 5 IRQ5 TMR1 Timer 1 aka Root Counter 1 (Sysclk or H-blank) 19 | 6 IRQ6 TMR2 Timer 2 aka Root Counter 2 (Sysclk or Sysclk/8) 20 | 7 IRQ7 Controller and Memory Card - Byte Received Interrupt 21 | 8 IRQ8 SIO 22 | 9 IRQ9 SPU 23 | 10 IRQ10 Controller - Lightpen Interrupt (reportedly also PIO...?) 24 | 11-15 Not used (always zero) 25 | 16-31 Garbage 26 | */ 27 | enum { 28 | IC_VBLANK = 0x001, 29 | IC_GPU = 0x002, 30 | IC_CDROM = 0x004, 31 | IC_DMA = 0x008, 32 | IC_TIMER0 = 0x010, 33 | IC_TIMER1 = 0x020, 34 | IC_TIMER2 = 0x040, 35 | IC_JOY = 0x080, 36 | IC_SIO = 0x100, 37 | IC_SPU = 0x200, 38 | IC_LP_PIO = 0x400 39 | }; 40 | 41 | /* 42 | 1F801070h 2 I_STAT - Interrupt status register 43 | 1F801074h 2 I_MASK - Interrupt mask register 44 | */ 45 | 46 | typedef struct { 47 | uint32_t bus_delay; 48 | uint32_t io_base, io_size; 49 | 50 | uint16_t stat; 51 | uint16_t mask; 52 | 53 | psx_cpu_t* cpu; 54 | } psx_ic_t; 55 | 56 | psx_ic_t* psx_ic_create(void); 57 | void psx_ic_init(psx_ic_t*, psx_cpu_t*); 58 | uint32_t psx_ic_read32(psx_ic_t*, uint32_t); 59 | uint16_t psx_ic_read16(psx_ic_t*, uint32_t); 60 | uint8_t psx_ic_read8(psx_ic_t*, uint32_t); 61 | void psx_ic_write32(psx_ic_t*, uint32_t, uint32_t); 62 | void psx_ic_write16(psx_ic_t*, uint32_t, uint16_t); 63 | void psx_ic_write8(psx_ic_t*, uint32_t, uint8_t); 64 | void psx_ic_irq(psx_ic_t*, int); 65 | void psx_ic_destroy(psx_ic_t*); 66 | 67 | #endif -------------------------------------------------------------------------------- /psx/dev/input.c: -------------------------------------------------------------------------------- 1 | #ifndef PAD_H 2 | #define PAD_H 3 | 4 | #include "input.h" 5 | 6 | #include 7 | #include 8 | 9 | psx_input_t* psx_input_create(void) { 10 | return (psx_input_t*)malloc(sizeof(psx_input_t)); 11 | } 12 | 13 | void psx_input_init(psx_input_t* input) { 14 | memset(input, 0, sizeof(psx_input_t)); 15 | } 16 | 17 | void psx_input_set_write_func(psx_input_t* input, psx_input_write_t write_func) { 18 | input->write_func = write_func; 19 | } 20 | 21 | void psx_input_set_read_func(psx_input_t* input, psx_input_read_t read_func) { 22 | input->read_func = read_func; 23 | } 24 | 25 | void psx_input_set_on_button_press_func(psx_input_t* input, psx_input_on_button_press_t on_button_press_func) { 26 | input->on_button_press_func = on_button_press_func; 27 | } 28 | 29 | void psx_input_set_on_button_release_func(psx_input_t* input, psx_input_on_button_release_t on_button_release_func) { 30 | input->on_button_release_func = on_button_release_func; 31 | } 32 | 33 | void psx_input_set_on_analog_change_func(psx_input_t* input, psx_input_on_analog_change_t on_analog_change_func) { 34 | input->on_analog_change_func = on_analog_change_func; 35 | } 36 | 37 | void psx_input_destroy(psx_input_t* input) { 38 | free(input->udata); 39 | free(input); 40 | } 41 | 42 | #endif -------------------------------------------------------------------------------- /psx/dev/input.h: -------------------------------------------------------------------------------- 1 | #ifndef INPUT_H 2 | #define INPUT_H 3 | 4 | #include 5 | 6 | struct psx_input_t; 7 | 8 | typedef struct psx_input_t psx_input_t; 9 | 10 | typedef void (*psx_input_write_t)(void*, uint16_t); 11 | typedef uint32_t (*psx_input_read_t)(void*); 12 | typedef void (*psx_input_on_button_press_t)(void*, uint32_t); 13 | typedef void (*psx_input_on_button_release_t)(void*, uint32_t); 14 | typedef void (*psx_input_on_analog_change_t)(void*, uint32_t, uint16_t); 15 | typedef int (*psx_input_query_fifo_t)(void*); 16 | 17 | struct psx_input_t { 18 | void* udata; 19 | 20 | psx_input_write_t write_func; 21 | psx_input_read_t read_func; 22 | psx_input_on_button_press_t on_button_press_func; 23 | psx_input_on_button_release_t on_button_release_func; 24 | psx_input_on_analog_change_t on_analog_change_func; 25 | psx_input_query_fifo_t query_fifo_func; 26 | }; 27 | 28 | psx_input_t* psx_input_create(void); 29 | void psx_input_init(psx_input_t*); 30 | void psx_input_set_write_func(psx_input_t*, psx_input_write_t); 31 | void psx_input_set_read_func(psx_input_t*, psx_input_read_t); 32 | void psx_input_set_on_button_press_func(psx_input_t*, psx_input_on_button_press_t); 33 | void psx_input_set_on_button_release_func(psx_input_t*, psx_input_on_button_release_t); 34 | void psx_input_set_on_analog_change_func(psx_input_t*, psx_input_on_analog_change_t); 35 | void psx_input_set_query_fifo_func(psx_input_t*, psx_input_query_fifo_t); 36 | void psx_input_destroy(psx_input_t*); 37 | 38 | #endif -------------------------------------------------------------------------------- /psx/dev/mc1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mc1.h" 6 | #include "../log.h" 7 | 8 | /* 9 | 0-3 Write Delay (00h..0Fh=01h..10h Cycles) 10 | 4-7 Read Delay (00h..0Fh=01h..10h Cycles) 11 | 8 Recovery Period (0=No, 1=Yes, uses COM0 timings) 12 | 9 Hold Period (0=No, 1=Yes, uses COM1 timings) 13 | 10 Floating Period (0=No, 1=Yes, uses COM2 timings) 14 | 11 Pre-strobe Period (0=No, 1=Yes, uses COM3 timings) 15 | 12 Data Bus-width (0=8bits, 1=16bits) 16 | 13 Auto Increment (0=No, 1=Yes) 17 | 14-15 Unknown (R/W) 18 | 16-20 Memory Window Size (1 SHL N bytes) (0..1Fh = 1 byte ... 2 gigabytes) 19 | 21-23 Unknown (always zero) 20 | 24-27 DMA timing override 21 | 28 Address error flag. Write 1 to it to clear it. 22 | 29 DMA timing select (0=use normal timings, 1=use bits 24-27) 23 | 30 Wide DMA (0=use bit 12, 1=override to full 32 bits) 24 | 31 Wait (1=wait on external device before being ready) 25 | */ 26 | 27 | #define DEFAULT_DLY 2 28 | 29 | // #define WRITE_DLY(dev) ((mc1-> ## dev ## _delay & 0xf) + 1) 30 | // #define READ_DLY(dev) (((mc1-> ## dev ## _delay >> 4) & 0xf) + 1) 31 | // #define USE_COM0(dev) (mc1-> ## dev ## _delay >> 8) 32 | // #define USE_COM1(dev) (mc1-> ## dev ## _delay >> 9) 33 | // #define USE_COM2(dev) (mc1-> ## dev ## _delay >> 10) 34 | // #define USE_COM3(dev) (mc1-> ## dev ## _delay >> 11) 35 | 36 | // #define COM0_DLY (mc1->com_delay ) 37 | 38 | // psx_access_delay_t mc1_get_com_delay(int uc0, int uc1, int uc2, int uc3) { 39 | // int fst = 0, seq = 0, min = 0; 40 | // } 41 | 42 | // #define COM_DLY(dev) 43 | 44 | psx_mc1_t* psx_mc1_create(void) { 45 | return (psx_mc1_t*)malloc(sizeof(psx_mc1_t)); 46 | } 47 | 48 | /* 49 | 1F801000h 4 Expansion 1 Base Address (usually 1F000000h) 50 | 1F801004h 4 Expansion 2 Base Address (usually 1F802000h) 51 | 1F801008h 4 Expansion 1 Delay/Size (usually 0013243Fh; 512Kbytes 8bit-bus) 52 | 1F80100Ch 4 Expansion 3 Delay/Size (usually 00003022h; 1 byte) 53 | 1F801010h 4 BIOS ROM Delay/Size (usually 0013243Fh; 512Kbytes 8bit-bus) 54 | 1F801014h 4 SPU_DELAY Delay/Size (usually 200931E1h) 55 | 1F801018h 4 CDROM_DELAY Delay/Size (usually 00020843h or 00020943h) 56 | 1F80101Ch 4 Expansion 2 Delay/Size (usually 00070777h; 128-bytes 8bit-bus) 57 | 1F801020h 4 COM_DELAY / COMMON_DELAY (00031125h or 0000132Ch or 00001325h) 58 | */ 59 | void psx_mc1_init(psx_mc1_t* mc1) { 60 | memset(mc1, 0, sizeof(psx_mc1_t)); 61 | 62 | mc1->io_base = PSX_MC1_BEGIN; 63 | mc1->io_size = PSX_MC1_SIZE; 64 | 65 | mc1->exp1_base = 0x1f000000; 66 | mc1->exp2_base = 0x1f802000; 67 | mc1->exp1_delay = 0x0013243f; 68 | mc1->exp3_delay = 0x00003022; 69 | mc1->bios_delay = 0x0013243f; 70 | mc1->spu_delay = 0x200931e1; 71 | mc1->cdrom_delay = 0x00020843; 72 | mc1->exp2_delay = 0x00070777; 73 | mc1->com_delay = 0x00031125; 74 | } 75 | 76 | uint32_t psx_mc1_read32(psx_mc1_t* mc1, uint32_t offset) { 77 | switch (offset) { 78 | case 0x00: return mc1->exp1_base; 79 | case 0x04: return mc1->exp2_base; 80 | case 0x08: return mc1->exp1_delay; 81 | case 0x0c: return mc1->exp3_delay; 82 | case 0x10: return mc1->bios_delay; 83 | case 0x14: return mc1->spu_delay; 84 | case 0x18: return mc1->cdrom_delay; 85 | case 0x1c: return mc1->exp2_delay; 86 | case 0x20: return mc1->com_delay; 87 | } 88 | 89 | log_warn("Unhandled 32-bit MC1 read at offset %08x", offset); 90 | 91 | return 0x0; 92 | } 93 | 94 | uint16_t psx_mc1_read16(psx_mc1_t* mc1, uint32_t offset) { 95 | log_warn("Unhandled 16-bit MC1 read at offset %08x", offset); 96 | 97 | return 0x0; 98 | } 99 | 100 | uint8_t psx_mc1_read8(psx_mc1_t* mc1, uint32_t offset) { 101 | log_warn("Unhandled 8-bit MC1 read at offset %08x", offset); 102 | 103 | return 0x0; 104 | } 105 | 106 | void psx_mc1_write32(psx_mc1_t* mc1, uint32_t offset, uint32_t value) { 107 | switch (offset) { 108 | case 0x00: mc1->exp1_base = value; break; 109 | case 0x04: mc1->exp2_base = value; break; 110 | case 0x08: mc1->exp1_delay = value; break; 111 | case 0x0c: mc1->exp3_delay = value; break; 112 | case 0x10: mc1->bios_delay = value; break; 113 | case 0x14: mc1->spu_delay = value; break; 114 | case 0x18: mc1->cdrom_delay = value; break; 115 | case 0x1c: mc1->exp2_delay = value; break; 116 | case 0x20: mc1->com_delay = value; break; 117 | 118 | default: { 119 | log_warn("Unhandled 32-bit MC1 write at offset %08x (%08x)", offset, value); 120 | } break; 121 | } 122 | } 123 | 124 | void psx_mc1_write16(psx_mc1_t* mc1, uint32_t offset, uint16_t value) { 125 | log_warn("Unhandled 16-bit MC1 write at offset %08x (%04x)", offset, value); 126 | } 127 | 128 | void psx_mc1_write8(psx_mc1_t* mc1, uint32_t offset, uint8_t value) { 129 | log_warn("Unhandled 8-bit MC1 write at offset %08x (%02x)", offset, value); 130 | } 131 | 132 | uint32_t psx_mc1_get_bios_read_delay(psx_mc1_t* mc1) { 133 | return DEFAULT_DLY; 134 | } 135 | 136 | uint32_t psx_mc1_get_ram_read_delay(psx_mc1_t* mc1) { 137 | return DEFAULT_DLY; 138 | } 139 | 140 | uint32_t psx_mc1_get_dma_read_delay(psx_mc1_t* mc1) { 141 | return DEFAULT_DLY; 142 | } 143 | 144 | uint32_t psx_mc1_get_exp1_read_delay(psx_mc1_t* mc1) { 145 | return DEFAULT_DLY; 146 | } 147 | 148 | uint32_t psx_mc1_get_mc1_read_delay(psx_mc1_t* mc1) { 149 | return DEFAULT_DLY; 150 | } 151 | 152 | uint32_t psx_mc1_get_mc2_read_delay(psx_mc1_t* mc1) { 153 | return DEFAULT_DLY; 154 | } 155 | 156 | uint32_t psx_mc1_get_mc3_read_delay(psx_mc1_t* mc1) { 157 | return DEFAULT_DLY; 158 | } 159 | 160 | uint32_t psx_mc1_get_ic_read_delay(psx_mc1_t* mc1) { 161 | return DEFAULT_DLY; 162 | } 163 | 164 | uint32_t psx_mc1_get_scratchpad_read_delay(psx_mc1_t* mc1) { 165 | return 1; 166 | } 167 | 168 | uint32_t psx_mc1_get_gpu_read_delay(psx_mc1_t* mc1) { 169 | return DEFAULT_DLY; 170 | } 171 | 172 | uint32_t psx_mc1_get_spu_read_delay(psx_mc1_t* mc1) { 173 | return DEFAULT_DLY; 174 | } 175 | 176 | uint32_t psx_mc1_get_timer_read_delay(psx_mc1_t* mc1) { 177 | return DEFAULT_DLY; 178 | } 179 | 180 | uint32_t psx_mc1_get_cdrom_read_delay(psx_mc1_t* mc1) { 181 | return DEFAULT_DLY; 182 | } 183 | 184 | uint32_t psx_mc1_get_pad_read_delay(psx_mc1_t* mc1) { 185 | return DEFAULT_DLY; 186 | } 187 | 188 | uint32_t psx_mc1_get_bios_write_delay(psx_mc1_t* mc1) { 189 | return DEFAULT_DLY; 190 | } 191 | 192 | uint32_t psx_mc1_get_ram_write_delay(psx_mc1_t* mc1) { 193 | return DEFAULT_DLY; 194 | } 195 | 196 | uint32_t psx_mc1_get_dma_write_delay(psx_mc1_t* mc1) { 197 | return DEFAULT_DLY; 198 | } 199 | 200 | uint32_t psx_mc1_get_exp1_write_delay(psx_mc1_t* mc1) { 201 | return DEFAULT_DLY; 202 | } 203 | 204 | uint32_t psx_mc1_get_mc1_write_delay(psx_mc1_t* mc1) { 205 | return DEFAULT_DLY; 206 | } 207 | 208 | uint32_t psx_mc1_get_mc2_write_delay(psx_mc1_t* mc1) { 209 | return DEFAULT_DLY; 210 | } 211 | 212 | uint32_t psx_mc1_get_mc3_write_delay(psx_mc1_t* mc1) { 213 | return DEFAULT_DLY; 214 | } 215 | 216 | uint32_t psx_mc1_get_ic_write_delay(psx_mc1_t* mc1) { 217 | return DEFAULT_DLY; 218 | } 219 | 220 | uint32_t psx_mc1_get_scratchpad_write_delay(psx_mc1_t* mc1) { 221 | return 1; 222 | } 223 | 224 | uint32_t psx_mc1_get_gpu_write_delay(psx_mc1_t* mc1) { 225 | return DEFAULT_DLY; 226 | } 227 | 228 | uint32_t psx_mc1_get_spu_write_delay(psx_mc1_t* mc1) { 229 | return DEFAULT_DLY; 230 | } 231 | 232 | uint32_t psx_mc1_get_timer_write_delay(psx_mc1_t* mc1) { 233 | return DEFAULT_DLY; 234 | } 235 | 236 | uint32_t psx_mc1_get_cdrom_write_delay(psx_mc1_t* mc1) { 237 | return DEFAULT_DLY; 238 | } 239 | 240 | uint32_t psx_mc1_get_pad_write_delay(psx_mc1_t* mc1) { 241 | return DEFAULT_DLY; 242 | } 243 | 244 | void psx_mc1_destroy(psx_mc1_t* mc1) { 245 | free(mc1); 246 | } -------------------------------------------------------------------------------- /psx/dev/mc1.h: -------------------------------------------------------------------------------- 1 | #ifndef MC1_H 2 | #define MC1_H 3 | 4 | #include 5 | 6 | #define PSX_MC1_BEGIN 0x1f801000 7 | #define PSX_MC1_SIZE 0x24 8 | #define PSX_MC1_END 0x1f801023 9 | 10 | typedef struct { 11 | uint32_t bus_delay; 12 | uint32_t io_base, io_size; 13 | 14 | uint32_t exp1_base; 15 | uint32_t exp2_base; 16 | uint32_t exp1_delay; 17 | uint32_t exp3_delay; 18 | uint32_t bios_delay; 19 | uint32_t spu_delay; 20 | uint32_t cdrom_delay; 21 | uint32_t exp2_delay; 22 | uint32_t com_delay; 23 | } psx_mc1_t; 24 | 25 | typedef struct { 26 | int fst; 27 | int seq; 28 | } psx_access_delay_t; 29 | 30 | psx_mc1_t* psx_mc1_create(void); 31 | void psx_mc1_init(psx_mc1_t*); 32 | uint32_t psx_mc1_read32(psx_mc1_t*, uint32_t); 33 | uint16_t psx_mc1_read16(psx_mc1_t*, uint32_t); 34 | uint8_t psx_mc1_read8(psx_mc1_t*, uint32_t); 35 | void psx_mc1_write32(psx_mc1_t*, uint32_t, uint32_t); 36 | void psx_mc1_write16(psx_mc1_t*, uint32_t, uint16_t); 37 | void psx_mc1_write8(psx_mc1_t*, uint32_t, uint8_t); 38 | void psx_mc1_destroy(psx_mc1_t*); 39 | uint32_t psx_mc1_get_bios_read_delay(psx_mc1_t*); 40 | uint32_t psx_mc1_get_ram_read_delay(psx_mc1_t*); 41 | uint32_t psx_mc1_get_dma_read_delay(psx_mc1_t*); 42 | uint32_t psx_mc1_get_exp1_read_delay(psx_mc1_t*); 43 | uint32_t psx_mc1_get_mc1_read_delay(psx_mc1_t*); 44 | uint32_t psx_mc1_get_mc2_read_delay(psx_mc1_t*); 45 | uint32_t psx_mc1_get_mc3_read_delay(psx_mc1_t*); 46 | uint32_t psx_mc1_get_ic_read_delay(psx_mc1_t*); 47 | uint32_t psx_mc1_get_scratchpad_read_delay(psx_mc1_t*); 48 | uint32_t psx_mc1_get_gpu_read_delay(psx_mc1_t*); 49 | uint32_t psx_mc1_get_spu_read_delay(psx_mc1_t*); 50 | uint32_t psx_mc1_get_timer_read_delay(psx_mc1_t*); 51 | uint32_t psx_mc1_get_cdrom_read_delay(psx_mc1_t*); 52 | uint32_t psx_mc1_get_pad_read_delay(psx_mc1_t*); 53 | 54 | uint32_t psx_mc1_get_bios_write_delay(psx_mc1_t*); 55 | uint32_t psx_mc1_get_ram_write_delay(psx_mc1_t*); 56 | uint32_t psx_mc1_get_dma_write_delay(psx_mc1_t*); 57 | uint32_t psx_mc1_get_exp1_write_delay(psx_mc1_t*); 58 | uint32_t psx_mc1_get_mc1_write_delay(psx_mc1_t*); 59 | uint32_t psx_mc1_get_mc2_write_delay(psx_mc1_t*); 60 | uint32_t psx_mc1_get_mc3_write_delay(psx_mc1_t*); 61 | uint32_t psx_mc1_get_ic_write_delay(psx_mc1_t*); 62 | uint32_t psx_mc1_get_scratchpad_write_delay(psx_mc1_t*); 63 | uint32_t psx_mc1_get_gpu_write_delay(psx_mc1_t*); 64 | uint32_t psx_mc1_get_spu_write_delay(psx_mc1_t*); 65 | uint32_t psx_mc1_get_timer_write_delay(psx_mc1_t*); 66 | uint32_t psx_mc1_get_cdrom_write_delay(psx_mc1_t*); 67 | uint32_t psx_mc1_get_pad_write_delay(psx_mc1_t*); 68 | 69 | #endif -------------------------------------------------------------------------------- /psx/dev/mc2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mc2.h" 6 | #include "../log.h" 7 | 8 | psx_mc2_t* psx_mc2_create(void) { 9 | return (psx_mc2_t*)malloc(sizeof(psx_mc2_t)); 10 | } 11 | 12 | /* 13 | 1F801060h 4/2 RAM_SIZE (usually 00000B88h; 2MB RAM mirrored in first 8MB) 14 | */ 15 | void psx_mc2_init(psx_mc2_t* mc2) { 16 | memset(mc2, 0, sizeof(psx_mc2_t)); 17 | 18 | mc2->io_base = PSX_MC2_BEGIN; 19 | mc2->io_size = PSX_MC2_SIZE; 20 | 21 | mc2->ram_size = 0x00000b88; 22 | } 23 | 24 | uint32_t psx_mc2_read32(psx_mc2_t* mc2, uint32_t offset) { 25 | switch (offset) { 26 | case 0x00: return mc2->ram_size; 27 | } 28 | 29 | log_warn("Unhandled 32-bit MC2 read at offset %08x", offset); 30 | 31 | return 0x0; 32 | } 33 | 34 | uint16_t psx_mc2_read16(psx_mc2_t* mc2, uint32_t offset) { 35 | log_warn("Unhandled 16-bit MC2 read at offset %08x", offset); 36 | 37 | return 0x0; 38 | } 39 | 40 | uint8_t psx_mc2_read8(psx_mc2_t* mc2, uint32_t offset) { 41 | log_warn("Unhandled 8-bit MC2 read at offset %08x", offset); 42 | 43 | return 0x0; 44 | } 45 | 46 | void psx_mc2_write32(psx_mc2_t* mc2, uint32_t offset, uint32_t value) { 47 | switch (offset) { 48 | case 0x00: printf("ram_size write %08x\n", value); mc2->ram_size = value; break; 49 | 50 | default: { 51 | log_warn("Unhandled 32-bit MC2 write at offset %08x (%08x)", offset, value); 52 | } break; 53 | } 54 | } 55 | 56 | void psx_mc2_write16(psx_mc2_t* mc2, uint32_t offset, uint16_t value) { 57 | log_warn("Unhandled 16-bit MC2 write at offset %08x (%04x)", offset, value); 58 | } 59 | 60 | void psx_mc2_write8(psx_mc2_t* mc2, uint32_t offset, uint8_t value) { 61 | log_warn("Unhandled 8-bit MC2 write at offset %08x (%02x)", offset, value); 62 | } 63 | 64 | void psx_mc2_destroy(psx_mc2_t* mc2) { 65 | free(mc2); 66 | } -------------------------------------------------------------------------------- /psx/dev/mc2.h: -------------------------------------------------------------------------------- 1 | #ifndef MC2_H 2 | #define MC2_H 3 | 4 | #include 5 | 6 | #define PSX_MC2_BEGIN 0x1f801060 7 | #define PSX_MC2_SIZE 0x4 8 | #define PSX_MC2_END 0x1F801063 9 | 10 | typedef struct { 11 | uint32_t bus_delay; 12 | uint32_t io_base, io_size; 13 | 14 | uint32_t ram_size; 15 | } psx_mc2_t; 16 | 17 | psx_mc2_t* psx_mc2_create(void); 18 | void psx_mc2_init(psx_mc2_t*); 19 | uint32_t psx_mc2_read32(psx_mc2_t*, uint32_t); 20 | uint16_t psx_mc2_read16(psx_mc2_t*, uint32_t); 21 | uint8_t psx_mc2_read8(psx_mc2_t*, uint32_t); 22 | void psx_mc2_write32(psx_mc2_t*, uint32_t, uint32_t); 23 | void psx_mc2_write16(psx_mc2_t*, uint32_t, uint16_t); 24 | void psx_mc2_write8(psx_mc2_t*, uint32_t, uint8_t); 25 | void psx_mc2_destroy(psx_mc2_t*); 26 | 27 | #endif -------------------------------------------------------------------------------- /psx/dev/mc3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mc3.h" 6 | #include "../log.h" 7 | 8 | psx_mc3_t* psx_mc3_create(void) { 9 | return (psx_mc3_t*)malloc(sizeof(psx_mc3_t)); 10 | } 11 | 12 | /* 13 | FFFE0130h 4 Cache Control 14 | */ 15 | void psx_mc3_init(psx_mc3_t* mc3) { 16 | memset(mc3, 0, sizeof(psx_mc3_t)); 17 | 18 | mc3->io_base = PSX_MC3_BEGIN; 19 | mc3->io_size = PSX_MC3_SIZE; 20 | } 21 | 22 | uint32_t psx_mc3_read32(psx_mc3_t* mc3, uint32_t offset) { 23 | switch (offset) { 24 | case 0x00: return mc3->cache_control; 25 | } 26 | 27 | log_warn("Unhandled 32-bit MC3 read at offset %08x", offset); 28 | 29 | return 0x0; 30 | } 31 | 32 | uint16_t psx_mc3_read16(psx_mc3_t* mc3, uint32_t offset) { 33 | log_warn("Unhandled 16-bit MC3 read at offset %08x", offset); 34 | 35 | return 0x0; 36 | } 37 | 38 | uint8_t psx_mc3_read8(psx_mc3_t* mc3, uint32_t offset) { 39 | log_warn("Unhandled 8-bit MC3 read at offset %08x", offset); 40 | 41 | return 0x0; 42 | } 43 | 44 | void psx_mc3_write32(psx_mc3_t* mc3, uint32_t offset, uint32_t value) { 45 | switch (offset) { 46 | case 0x00: mc3->cache_control = value; break; 47 | 48 | default: { 49 | log_warn("Unhandled 32-bit MC3 write at offset %08x (%08x)", offset, value); 50 | } break; 51 | } 52 | } 53 | 54 | void psx_mc3_write16(psx_mc3_t* mc3, uint32_t offset, uint16_t value) { 55 | log_warn("Unhandled 16-bit MC3 write at offset %08x (%04x)", offset, value); 56 | } 57 | 58 | void psx_mc3_write8(psx_mc3_t* mc3, uint32_t offset, uint8_t value) { 59 | log_warn("Unhandled 8-bit MC3 write at offset %08x (%02x)", offset, value); 60 | } 61 | 62 | void psx_mc3_destroy(psx_mc3_t* mc3) { 63 | free(mc3); 64 | } -------------------------------------------------------------------------------- /psx/dev/mc3.h: -------------------------------------------------------------------------------- 1 | #ifndef MC3_H 2 | #define MC3_H 3 | 4 | #include 5 | 6 | #define PSX_MC3_BEGIN 0xfffe0130 7 | #define PSX_MC3_SIZE 0x4 8 | #define PSX_MC3_END 0xfffe0133 9 | 10 | typedef struct { 11 | uint32_t bus_delay; 12 | uint32_t io_base, io_size; 13 | 14 | uint32_t cache_control; 15 | } psx_mc3_t; 16 | 17 | psx_mc3_t* psx_mc3_create(void); 18 | void psx_mc3_init(psx_mc3_t*); 19 | uint32_t psx_mc3_read32(psx_mc3_t*, uint32_t); 20 | uint16_t psx_mc3_read16(psx_mc3_t*, uint32_t); 21 | uint8_t psx_mc3_read8(psx_mc3_t*, uint32_t); 22 | void psx_mc3_write32(psx_mc3_t*, uint32_t, uint32_t); 23 | void psx_mc3_write16(psx_mc3_t*, uint32_t, uint16_t); 24 | void psx_mc3_write8(psx_mc3_t*, uint32_t, uint8_t); 25 | void psx_mc3_destroy(psx_mc3_t*); 26 | 27 | #endif -------------------------------------------------------------------------------- /psx/dev/mcd.c: -------------------------------------------------------------------------------- 1 | #include "mcd.h" 2 | #include "../log.h" 3 | 4 | psx_mcd_t* psx_mcd_create(void) { 5 | return (psx_mcd_t*)malloc(sizeof(psx_mcd_t)); 6 | } 7 | 8 | int psx_mcd_init(psx_mcd_t* mcd, const char* path) { 9 | memset(mcd, 0, sizeof(psx_mcd_t)); 10 | 11 | mcd->state = MCD_STATE_TX_HIZ; 12 | mcd->flag = 0x08; 13 | mcd->path = path; 14 | mcd->buf = malloc(MCD_MEMORY_SIZE); 15 | mcd->tx_data_ready = 0; 16 | 17 | memset(mcd->buf, 0, MCD_MEMORY_SIZE); 18 | 19 | if (!path) 20 | return 0; 21 | 22 | FILE* file = fopen(path, "rb"); 23 | 24 | if (!file) 25 | return 1; 26 | 27 | if (!fread(mcd->buf, 1, MCD_MEMORY_SIZE, file)) 28 | return 2; 29 | 30 | fclose(file); 31 | 32 | return 0; 33 | } 34 | 35 | uint8_t psx_mcd_read(psx_mcd_t* mcd) { 36 | switch (mcd->state) { 37 | case MCD_STATE_TX_HIZ: mcd->tx_data = 0xff; break; 38 | case MCD_STATE_TX_FLG: mcd->tx_data = mcd->flag; mcd->flag = 0x00; break; 39 | case MCD_STATE_TX_ID1: mcd->tx_data = 0x5a; break; 40 | case MCD_STATE_TX_ID2: { 41 | mcd->tx_data_ready = 1; 42 | mcd->tx_data = 0x5d; 43 | 44 | switch (mcd->mode) { 45 | case 'R': mcd->state = MCD_R_STATE_RX_MSB; break; 46 | case 'W': mcd->state = MCD_W_STATE_RX_MSB; break; 47 | case 'S': mcd->state = MCD_S_STATE_TX_ACK1; break; 48 | } 49 | 50 | // printf("mcd read %02x\n", mcd->tx_data); 51 | 52 | // log_set_quiet(0); 53 | // log_fatal("mcd read %02x", mcd->tx_data); 54 | // log_set_quiet(1); 55 | 56 | return mcd->tx_data; 57 | } break; 58 | 59 | // Read states 60 | case MCD_R_STATE_RX_MSB: mcd->tx_data = 0x00; break; 61 | case MCD_R_STATE_RX_LSB: mcd->tx_data = mcd->msb; break; 62 | case MCD_R_STATE_TX_ACK1: mcd->tx_data = 0x5c; break; 63 | case MCD_R_STATE_TX_ACK2: mcd->tx_data = 0x5d; break; 64 | case MCD_R_STATE_TX_MSB: mcd->tx_data = mcd->msb; mcd->checksum = mcd->msb; break; 65 | case MCD_R_STATE_TX_LSB: mcd->tx_data = mcd->lsb; mcd->checksum ^= mcd->lsb; 66 | mcd->pending_bytes = 128; break; 67 | case MCD_R_STATE_TX_DATA: { 68 | --mcd->pending_bytes; 69 | 70 | uint8_t data = mcd->buf[mcd->addr++]; 71 | 72 | mcd->checksum ^= data; 73 | 74 | if (!mcd->pending_bytes) { 75 | mcd->tx_data = data; 76 | 77 | break; 78 | } 79 | 80 | // printf("mcd read %02x\n", data); 81 | 82 | // log_set_quiet(0); 83 | // log_fatal("mcd read %02x", data); 84 | // log_set_quiet(1); 85 | 86 | return data; 87 | } break; 88 | case MCD_R_STATE_TX_CHK: mcd->tx_data = mcd->checksum; break; 89 | case MCD_R_STATE_TX_MEB: { 90 | mcd->tx_data_ready = 0; 91 | mcd->state = MCD_STATE_TX_HIZ; 92 | 93 | // log_set_quiet(0); 94 | // log_fatal("mcd read %02x", 'G'); 95 | // log_set_quiet(1); 96 | 97 | // printf("mcd read %02x\n", 'G'); 98 | 99 | return 'G'; 100 | } break; 101 | 102 | /* Write states */ 103 | case MCD_W_STATE_RX_MSB: mcd->tx_data = 0x00; break; 104 | case MCD_W_STATE_RX_LSB: mcd->tx_data = mcd->msb; 105 | mcd->pending_bytes = 127; break; 106 | case MCD_W_STATE_RX_DATA: { 107 | --mcd->pending_bytes; 108 | 109 | mcd->buf[mcd->addr++] = mcd->rx_data; 110 | 111 | if (!mcd->pending_bytes) { 112 | mcd->tx_data = mcd->rx_data; 113 | 114 | break; 115 | } 116 | 117 | // printf("mcd read %02x\n", mcd->rx_data); 118 | 119 | // log_set_quiet(0); 120 | // log_fatal("mcd read %02x", mcd->rx_data); 121 | // log_set_quiet(1); 122 | 123 | return mcd->rx_data; 124 | } break; 125 | case MCD_W_STATE_RX_CHK: mcd->tx_data = mcd->rx_data; break; 126 | case MCD_W_STATE_TX_ACK1: mcd->tx_data = 0x5c; break; 127 | case MCD_W_STATE_TX_ACK2: mcd->tx_data = 0x5d; break; 128 | case MCD_W_STATE_TX_MEB: { 129 | mcd->tx_data_ready = 0; 130 | mcd->state = MCD_STATE_TX_HIZ; 131 | 132 | // log_set_quiet(0); 133 | // log_fatal("mcd read %02x", 'G'); 134 | // log_set_quiet(1); 135 | 136 | // printf("mcd read %02x\n", 'G'); 137 | 138 | return 'G'; 139 | } break; 140 | } 141 | 142 | mcd->tx_data_ready = 1; 143 | mcd->state++; 144 | 145 | // log_set_quiet(0); 146 | // log_fatal("mcd read %02x", mcd->tx_data); 147 | // log_set_quiet(1); 148 | 149 | // printf("mcd read %02x\n", mcd->tx_data); 150 | 151 | return mcd->tx_data; 152 | } 153 | 154 | void psx_mcd_write(psx_mcd_t* mcd, uint8_t data) { 155 | // log_set_quiet(0); 156 | // log_fatal("mcd write %02x", data); 157 | // log_set_quiet(1); 158 | 159 | printf("mcd write %02x\n", data); 160 | 161 | switch (mcd->state) { 162 | case MCD_STATE_TX_FLG: mcd->mode = data; break; 163 | case MCD_R_STATE_RX_MSB: mcd->msb = data; break; 164 | case MCD_R_STATE_RX_LSB: { 165 | mcd->lsb = data; 166 | mcd->addr = ((mcd->msb << 8) | mcd->lsb) << 7; 167 | } break; 168 | case MCD_W_STATE_RX_MSB: mcd->msb = data; break; 169 | case MCD_W_STATE_RX_LSB: { 170 | mcd->lsb = data; 171 | mcd->addr = ((mcd->msb << 8) | mcd->lsb) << 7; 172 | } break; 173 | case MCD_W_STATE_RX_DATA: mcd->rx_data = data; break; 174 | case MCD_W_STATE_RX_CHK: /* Don't care */ break; 175 | } 176 | } 177 | 178 | int psx_mcd_query(psx_mcd_t* mcd) { 179 | return mcd->tx_data_ready; 180 | } 181 | 182 | void psx_mcd_reset(psx_mcd_t* mcd) { 183 | mcd->state = MCD_STATE_TX_HIZ; 184 | } 185 | 186 | void psx_mcd_destroy(psx_mcd_t* mcd) { 187 | FILE* file = fopen(mcd->path, "wb"); 188 | 189 | fwrite(mcd->buf, 1, MCD_MEMORY_SIZE, file); 190 | fclose(file); 191 | 192 | free(mcd->buf); 193 | free(mcd); 194 | } -------------------------------------------------------------------------------- /psx/dev/mcd.h: -------------------------------------------------------------------------------- 1 | #ifndef MCD_H 2 | #define MCD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define MCD_MEMORY_SIZE 0x20000 // 128 KB 10 | 11 | enum { 12 | MCD_STATE_TX_HIZ = 0, 13 | MCD_STATE_TX_FLG, 14 | MCD_STATE_TX_ID1, 15 | MCD_STATE_TX_ID2, 16 | MCD_R_STATE_RX_MSB, 17 | MCD_R_STATE_RX_LSB, 18 | MCD_R_STATE_TX_ACK1, 19 | MCD_R_STATE_TX_ACK2, 20 | MCD_R_STATE_TX_MSB, 21 | MCD_R_STATE_TX_LSB, 22 | MCD_R_STATE_TX_DATA, 23 | MCD_R_STATE_TX_CHK, 24 | MCD_R_STATE_TX_MEB, 25 | MCD_W_STATE_RX_MSB, 26 | MCD_W_STATE_RX_LSB, 27 | MCD_W_STATE_RX_DATA, 28 | MCD_W_STATE_RX_CHK, 29 | MCD_W_STATE_TX_ACK1, 30 | MCD_W_STATE_TX_ACK2, 31 | MCD_W_STATE_TX_MEB, 32 | MCD_S_STATE_TX_ACK1, 33 | MCD_S_STATE_TX_ACK2, 34 | MCD_S_STATE_TX_DAT0, 35 | MCD_S_STATE_TX_DAT1, 36 | MCD_S_STATE_TX_DAT2, 37 | MCD_S_STATE_TX_DAT3 38 | }; 39 | 40 | typedef struct { 41 | const char* path; 42 | uint8_t* buf; 43 | uint8_t flag; 44 | uint16_t msb; 45 | uint16_t lsb; 46 | uint16_t addr; 47 | uint8_t rx_data; 48 | int pending_bytes; 49 | char mode; 50 | int state; 51 | uint8_t tx_data; 52 | int tx_data_ready; 53 | uint8_t checksum; 54 | } psx_mcd_t; 55 | 56 | psx_mcd_t* psx_mcd_create(void); 57 | int psx_mcd_init(psx_mcd_t*, const char*); 58 | uint8_t psx_mcd_read(psx_mcd_t*); 59 | void psx_mcd_write(psx_mcd_t*, uint8_t); 60 | int psx_mcd_query(psx_mcd_t*); 61 | void psx_mcd_reset(psx_mcd_t*); 62 | void psx_mcd_destroy(psx_mcd_t*); 63 | 64 | #endif -------------------------------------------------------------------------------- /psx/dev/mdec.h: -------------------------------------------------------------------------------- 1 | #ifndef MDEC_H 2 | #define MDEC_H 3 | 4 | #include 5 | 6 | #include "../log.h" 7 | 8 | #define PSX_MDEC_SIZE 0x8 9 | #define PSX_MDEC_BEGIN 0x1f801820 10 | #define PSX_MDEC_END 0x1f801827 11 | 12 | #define MDEC_SCALE_TABLE_SIZE 64 13 | #define MDEC_QUANT_TABLE_SIZE 64 14 | 15 | enum { 16 | MDEC_RECV_CMD, 17 | MDEC_RECV_BLOCK, 18 | MDEC_RECV_QUANT, 19 | MDEC_RECV_QUANT_COLOR, 20 | MDEC_RECV_SCALE 21 | }; 22 | 23 | /* 24 | 31 Data-Out Fifo Empty (0=No, 1=Empty) 25 | 30 Data-In Fifo Full (0=No, 1=Full, or Last word received) 26 | 29 Command Busy (0=Ready, 1=Busy receiving or processing parameters) 27 | 28 Data-In Request (set when DMA0 enabled and ready to receive data) 28 | 27 Data-Out Request (set when DMA1 enabled and ready to send data) 29 | 26-25 Data Output Depth (0=4bit, 1=8bit, 2=24bit, 3=15bit) ;CMD.28-27 30 | 24 Data Output Signed (0=Unsigned, 1=Signed) ;CMD.26 31 | 23 Data Output Bit15 (0=Clear, 1=Set) (for 15bit depth only) ;CMD.25 32 | 22-19 Not used (seems to be always zero) 33 | 18-16 Current Block (0..3=Y1..Y4, 4=Cr, 5=Cb) (or for mono: always 4=Y) 34 | 15-0 Number of Parameter Words remaining minus 1 (FFFFh=None) ;CMD.Bit0-15 35 | */ 36 | 37 | enum { 38 | MDEC_CMD_NOP, 39 | MDEC_CMD_DECODE, 40 | MDEC_CMD_SET_QT, 41 | MDEC_CMD_SET_ST 42 | }; 43 | 44 | typedef struct { 45 | uint32_t bus_delay; 46 | uint32_t io_base, io_size; 47 | 48 | uint32_t cmd; 49 | 50 | int state; 51 | int data_remaining; 52 | int index; 53 | 54 | uint32_t* input; 55 | int input_index; 56 | size_t input_size; 57 | 58 | uint8_t* output; 59 | int output_index; 60 | uint32_t output_words_remaining; 61 | 62 | uint32_t words_remaining; 63 | int current_block; 64 | int output_bit15; 65 | int output_signed; 66 | int output_depth; 67 | int input_request; 68 | int output_request; 69 | int busy; 70 | int input_full; 71 | int output_empty; 72 | 73 | int enable_dma0; 74 | int enable_dma1; 75 | 76 | int recv_color; 77 | uint8_t uv_quant_table[MDEC_QUANT_TABLE_SIZE]; 78 | uint8_t y_quant_table[MDEC_QUANT_TABLE_SIZE]; 79 | int16_t scale_table[MDEC_SCALE_TABLE_SIZE]; 80 | 81 | int16_t yblk[64]; 82 | int16_t crblk[64]; 83 | int16_t cbblk[64]; 84 | 85 | uint32_t status; 86 | } psx_mdec_t; 87 | 88 | psx_mdec_t* psx_mdec_create(void); 89 | void psx_mdec_init(psx_mdec_t*); 90 | uint32_t psx_mdec_read32(psx_mdec_t*, uint32_t); 91 | uint16_t psx_mdec_read16(psx_mdec_t*, uint32_t); 92 | uint8_t psx_mdec_read8(psx_mdec_t*, uint32_t); 93 | void psx_mdec_write32(psx_mdec_t*, uint32_t, uint32_t); 94 | void psx_mdec_write16(psx_mdec_t*, uint32_t, uint16_t); 95 | void psx_mdec_write8(psx_mdec_t*, uint32_t, uint8_t); 96 | void psx_mdec_destroy(psx_mdec_t*); 97 | 98 | typedef void (*mdec_fn_t)(psx_mdec_t*); 99 | 100 | #endif -------------------------------------------------------------------------------- /psx/dev/pad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "pad.h" 6 | #include "../log.h" 7 | 8 | #define JOY_IRQ_DELAY 512 9 | 10 | uint32_t pad_read_rx(psx_pad_t* pad) { 11 | int slot = (pad->ctrl >> 13) & 1; 12 | 13 | psx_input_t* joy = pad->joy_slot[slot]; 14 | psx_mcd_t* mcd = pad->mcd_slot[slot]; 15 | 16 | if (!pad->dest[slot]) 17 | return 0xffffffff; 18 | 19 | if (!(pad->ctrl & CTRL_JOUT) && !(pad->ctrl & CTRL_RXEN)) 20 | return 0xffffffff; 21 | 22 | switch (pad->dest[slot]) { 23 | case DEST_JOY: { 24 | if (!joy) { 25 | pad->dest[slot] = 0; 26 | 27 | return 0xffffffff; 28 | } 29 | 30 | uint8_t data = joy->read_func(joy->udata); 31 | 32 | if (!joy->query_fifo_func(joy->udata)) 33 | pad->dest[slot] = 0; 34 | 35 | return data; 36 | } break; 37 | 38 | case DEST_MCD: { 39 | if (!mcd) { 40 | pad->dest[slot] = 0; 41 | 42 | return 0xffffffff; 43 | } 44 | 45 | uint8_t data = psx_mcd_read(mcd); 46 | 47 | if (!psx_mcd_query(mcd)) 48 | pad->dest[slot] = 0; 49 | 50 | return data; 51 | } break; 52 | } 53 | 54 | return 0xffffffff; 55 | } 56 | 57 | void pad_write_tx(psx_pad_t* pad, uint16_t data) { 58 | int slot = (pad->ctrl >> 13) & 1; 59 | 60 | psx_input_t* joy = pad->joy_slot[slot]; 61 | psx_mcd_t* mcd = pad->mcd_slot[slot]; 62 | 63 | if (!(pad->ctrl & CTRL_TXEN)) 64 | return; 65 | 66 | if (!pad->dest[slot]) { 67 | if ((data == DEST_JOY) || (data == DEST_MCD)) { 68 | pad->dest[slot] = data; 69 | 70 | if ((data == DEST_JOY) && !joy) 71 | return; 72 | 73 | if ((data == DEST_MCD) && !mcd) 74 | return; 75 | 76 | if (pad->ctrl & CTRL_ACIE) 77 | pad->cycles_until_irq = JOY_IRQ_DELAY; 78 | } 79 | } else { 80 | switch (pad->dest[slot]) { 81 | case DEST_JOY: { 82 | if (!joy) { 83 | pad->dest[slot] = 0; 84 | 85 | return; 86 | } 87 | 88 | joy->write_func(joy->udata, data); 89 | 90 | if (!joy->query_fifo_func(joy->udata)) 91 | pad->dest[slot] = 0; 92 | } break; 93 | 94 | case DEST_MCD: { 95 | if (!mcd) { 96 | pad->dest[slot] = 0; 97 | 98 | return; 99 | } 100 | 101 | psx_mcd_write(mcd, data); 102 | 103 | if (pad->ctrl & CTRL_ACIE) { 104 | pad->irq_bit = 1; 105 | pad->cycles_until_irq = 1024; 106 | 107 | return; 108 | } 109 | 110 | if (!psx_mcd_query(mcd)) 111 | pad->dest[slot] = 0; 112 | } break; 113 | } 114 | 115 | if (pad->ctrl & CTRL_ACIE) { 116 | pad->irq_bit = 1; 117 | pad->cycles_until_irq = (pad->dest[slot] == DEST_MCD) ? 2048 : JOY_IRQ_DELAY; 118 | } 119 | } 120 | } 121 | 122 | uint32_t pad_handle_stat_read(psx_pad_t* pad) { 123 | return pad->stat | 7; 124 | } 125 | 126 | void pad_handle_ctrl_write(psx_pad_t* pad, uint32_t value) { 127 | int slot = pad->ctrl & CTRL_SLOT; 128 | 129 | pad->ctrl = value; 130 | 131 | if (!(pad->ctrl & CTRL_JOUT)) { 132 | pad->ctrl &= ~CTRL_SLOT; 133 | 134 | if (pad->mcd_slot[slot >> 13]) 135 | psx_mcd_reset(pad->mcd_slot[slot >> 13]); 136 | } 137 | 138 | // Reset STAT bits 3, 4, 5, 9 139 | if (pad->ctrl & CTRL_ACKN) { 140 | pad->stat &= 0xfdc7; 141 | pad->ctrl &= ~CTRL_ACKN; 142 | } 143 | } 144 | 145 | psx_pad_t* psx_pad_create(void) { 146 | return (psx_pad_t*)malloc(sizeof(psx_pad_t)); 147 | } 148 | 149 | void psx_pad_init(psx_pad_t* pad, psx_ic_t* ic) { 150 | memset(pad, 0, sizeof(psx_pad_t)); 151 | 152 | pad->ic = ic; 153 | 154 | pad->io_base = PSX_PAD_BEGIN; 155 | pad->io_size = PSX_PAD_SIZE; 156 | 157 | pad->joy_slot[0] = NULL; 158 | pad->joy_slot[1] = NULL; 159 | pad->mcd_slot[0] = NULL; 160 | pad->mcd_slot[1] = NULL; 161 | } 162 | 163 | uint32_t psx_pad_read32(psx_pad_t* pad, uint32_t offset) { 164 | uint32_t v = 0; 165 | 166 | switch (offset) { 167 | case 0: v = pad_read_rx(pad); break; 168 | case 4: v = pad_handle_stat_read(pad); break; 169 | case 8: v = pad->mode; break; 170 | case 10: v = pad->ctrl; break; 171 | case 14: v = pad->baud; break; 172 | } 173 | 174 | return v; 175 | } 176 | 177 | uint16_t psx_pad_read16(psx_pad_t* pad, uint32_t offset) { 178 | uint32_t v = 0; 179 | 180 | switch (offset) { 181 | case 0: v = pad_read_rx(pad) & 0xffff; break; 182 | case 4: v = pad_handle_stat_read(pad) & 0xffff; break; 183 | case 8: v = pad->mode; break; 184 | case 10: v = pad->ctrl & 0xffff; break; 185 | case 14: v = pad->baud; break; 186 | } 187 | 188 | return v; 189 | 190 | printf("Unhandled 16-bit PAD read at offset %08x", offset); 191 | 192 | return 0x0; 193 | } 194 | 195 | uint8_t psx_pad_read8(psx_pad_t* pad, uint32_t offset) { 196 | uint32_t v = 0; 197 | 198 | switch (offset) { 199 | case 0: v = pad_read_rx(pad) & 0xff; break; 200 | case 4: v = pad_handle_stat_read(pad) & 0xff; break; 201 | case 8: v = pad->mode; break; 202 | case 10: v = pad->ctrl & 0xff; break; 203 | case 14: v = pad->baud; break; 204 | } 205 | 206 | // if (offset <= 0xa) 207 | // printf("slot=%d dest=%02x pad_read8(%02x) -> %02x\n", 208 | // (pad->ctrl & CTRL_SLOT) >> 13, 209 | // pad->dest[(pad->ctrl & CTRL_SLOT) >> 13], 210 | // offset, 211 | // v 212 | // ); 213 | 214 | return v; 215 | } 216 | 217 | void psx_pad_write32(psx_pad_t* pad, uint32_t offset, uint32_t value) { 218 | switch (offset) { 219 | case 0: pad_write_tx(pad, value); return; 220 | case 8: pad->mode = value & 0xffff; return; 221 | case 10: pad_handle_ctrl_write(pad, value); return; 222 | case 14: pad->baud = value & 0xffff; return; 223 | } 224 | 225 | printf("Unhandled 32-bit PAD write at offset %08x (%08x)", offset, value); 226 | } 227 | 228 | void psx_pad_write16(psx_pad_t* pad, uint32_t offset, uint16_t value) { 229 | switch (offset) { 230 | case 0: pad_write_tx(pad, value); return; 231 | case 8: pad->mode = value; return; 232 | case 10: pad_handle_ctrl_write(pad, value); return; 233 | case 14: pad->baud = value; return; 234 | } 235 | 236 | printf("Unhandled 16-bit PAD write at offset %08x (%04x)", offset, value); 237 | } 238 | 239 | void psx_pad_write8(psx_pad_t* pad, uint32_t offset, uint8_t value) { 240 | // if (offset <= 0xa) 241 | // printf("slot=%d dest=%02x pad_write8(%02x) -> %02x\n", 242 | // (pad->ctrl & CTRL_SLOT) >> 13, 243 | // pad->dest[(pad->ctrl & CTRL_SLOT) >> 13], 244 | // offset, 245 | // value 246 | // ); 247 | 248 | switch (offset) { 249 | case 0: pad_write_tx(pad, value); return; 250 | case 8: pad->mode = value; return; 251 | case 10: pad_handle_ctrl_write(pad, value); return; 252 | case 14: pad->baud = value; return; 253 | } 254 | 255 | printf("Unhandled 8-bit PAD write at offset %08x (%02x)", offset, value); 256 | } 257 | 258 | void psx_pad_button_press(psx_pad_t* pad, int slot, uint32_t data) { 259 | psx_input_t* selected_slot = pad->joy_slot[slot]; 260 | 261 | if (selected_slot) 262 | selected_slot->on_button_press_func(selected_slot->udata, data); 263 | } 264 | 265 | void psx_pad_button_release(psx_pad_t* pad, int slot, uint32_t data) { 266 | psx_input_t* selected_slot = pad->joy_slot[slot]; 267 | 268 | if (selected_slot) 269 | selected_slot->on_button_release_func(selected_slot->udata, data); 270 | } 271 | 272 | void psx_pad_analog_change(psx_pad_t* pad, int slot, uint32_t stick, uint16_t data) { 273 | psx_input_t* selected_slot = pad->joy_slot[slot]; 274 | 275 | if (selected_slot) 276 | selected_slot->on_analog_change_func(selected_slot->udata, stick, data); 277 | } 278 | 279 | void psx_pad_attach_joy(psx_pad_t* pad, int slot, psx_input_t* input) { 280 | if (pad->joy_slot[slot]) 281 | psx_pad_detach_joy(pad, slot); 282 | 283 | pad->joy_slot[slot] = input; 284 | } 285 | 286 | void psx_pad_detach_joy(psx_pad_t* pad, int slot) { 287 | if (!pad->joy_slot[slot]) 288 | return; 289 | 290 | psx_input_destroy(pad->joy_slot[slot]); 291 | 292 | pad->joy_slot[slot] = NULL; 293 | } 294 | 295 | int psx_pad_attach_mcd(psx_pad_t* pad, int slot, const char* path) { 296 | printf("Memory Card support is disabled\n"); 297 | 298 | return 0; 299 | 300 | if (pad->mcd_slot[slot]) 301 | psx_pad_detach_mcd(pad, slot); 302 | 303 | psx_mcd_t* mcd = psx_mcd_create(); 304 | 305 | int r = psx_mcd_init(mcd, path); 306 | 307 | if (r) { 308 | psx_pad_detach_mcd(pad, slot); 309 | 310 | return r; 311 | } 312 | 313 | pad->mcd_slot[slot] = mcd; 314 | 315 | return 0; 316 | } 317 | 318 | void psx_pad_detach_mcd(psx_pad_t* pad, int slot) { 319 | if (!pad->mcd_slot[slot]) 320 | return; 321 | 322 | psx_mcd_destroy(pad->mcd_slot[slot]); 323 | 324 | pad->mcd_slot[slot] = NULL; 325 | } 326 | 327 | void psx_pad_update(psx_pad_t* pad, int cyc) { 328 | if (pad->cycles_until_irq) { 329 | pad->cycles_until_irq -= cyc; 330 | 331 | if (pad->cycles_until_irq <= 0) { 332 | psx_ic_irq(pad->ic, IC_JOY); 333 | 334 | if (pad->irq_bit) { 335 | pad->stat |= STAT_IRQ7; 336 | pad->irq_bit = 0; 337 | } 338 | 339 | pad->cycles_until_irq = 0; 340 | } 341 | } 342 | } 343 | 344 | void psx_pad_destroy(psx_pad_t* pad) { 345 | psx_pad_detach_joy(pad, 0); 346 | psx_pad_detach_joy(pad, 1); 347 | psx_pad_detach_mcd(pad, 0); 348 | psx_pad_detach_mcd(pad, 1); 349 | 350 | free(pad); 351 | } -------------------------------------------------------------------------------- /psx/dev/pad.h: -------------------------------------------------------------------------------- 1 | #ifndef PAD_H 2 | #define PAD_H 3 | 4 | #include 5 | 6 | #include "ic.h" 7 | #include "input.h" 8 | #include "mcd.h" 9 | 10 | #include "../input/sda.h" 11 | 12 | #define PSX_PAD_BEGIN 0x1f801040 13 | #define PSX_PAD_SIZE 0x10 14 | #define PSX_PAD_END 0x1f80104f 15 | 16 | /* 17 | 0 TX Ready Flag 1 (1=Ready/Started) 18 | 1 RX FIFO Not Empty (0=Empty, 1=Not Empty) 19 | 2 TX Ready Flag 2 (1=Ready/Finished) 20 | 3 RX Parity Error (0=No, 1=Error; Wrong Parity, when enabled) (sticky) 21 | 4 Unknown (zero) (unlike SIO, this isn't RX FIFO Overrun flag) 22 | 5 Unknown (zero) (for SIO this would be RX Bad Stop Bit) 23 | 6 Unknown (zero) (for SIO this would be RX Input Level AFTER Stop bit) 24 | 7 /ACK Input Level (0=High, 1=Low) 25 | 8 Unknown (zero) (for SIO this would be CTS Input Level) 26 | 9 Interrupt Request (0=None, 1=IRQ7) (See JOY_CTRL.Bit4,10-12) (sticky) 27 | 10 Unknown (always zero) 28 | 11-31 Baudrate Timer (21bit timer, decrementing at 33MHz) 29 | */ 30 | 31 | #define STAT_TXR1 0x0001 32 | #define STAT_RXNE 0x0002 33 | #define STAT_TXR2 0x0004 34 | #define STAT_RXPE 0x0008 35 | #define STAT_UNK4 0x0010 36 | #define STAT_UNK5 0x0020 37 | #define STAT_UNK6 0x0040 38 | #define STAT_ACKL 0x0080 39 | #define STAT_UNK8 0x0100 40 | #define STAT_IRQ7 0x0200 41 | 42 | /* 43 | 0 TX Enable (TXEN) (0=Disable, 1=Enable) 44 | 1 /JOYn Output (0=High, 1=Low/Select) (/JOYn as defined in Bit13) 45 | 2 RX Enable (RXEN) (0=Normal, when /JOYn=Low, 1=Force Enable Once) 46 | 3 Unknown? (read/write-able) (for SIO, this would be TX Output Level) 47 | 4 Acknowledge (0=No change, 1=Reset JOY_STAT.Bits 3,9) (W) 48 | 5 Unknown? (read/write-able) (for SIO, this would be RTS Output Level) 49 | 6 Reset (0=No change, 1=Reset most JOY_registers to zero) (W) 50 | 7 Not used (always zero) (unlike SIO, no matter of FACTOR) 51 | 8-9 RX Interrupt Mode (0..3 = IRQ when RX FIFO contains 1,2,4,8 bytes) 52 | 10 TX Interrupt Enable (0=Disable, 1=Enable) ;when JOY_STAT.0-or-2 ;Ready 53 | 11 RX Interrupt Enable (0=Disable, 1=Enable) ;when N bytes in RX FIFO 54 | 12 ACK Interrupt Enable (0=Disable, 1=Enable) ;when JOY_STAT.7 ;/ACK=LOW 55 | 13 Desired Slot Number (0=/JOY1, 1=/JOY2) (set to LOW when Bit1=1) 56 | 14-15 Not used (always zero) 57 | */ 58 | 59 | #define CTRL_TXEN 0x0001 60 | #define CTRL_JOUT 0x0002 61 | #define CTRL_RXEN 0x0004 62 | #define CTRL_UNK3 0x0008 63 | #define CTRL_ACKN 0x0010 64 | #define CTRL_UNK5 0x0020 65 | #define CTRL_REST 0x0040 66 | #define CTRL_NUS7 0x0080 67 | #define CTRL_RXIM 0x0300 68 | #define CTRL_TXIE 0x0400 69 | #define CTRL_RXIE 0x0800 70 | #define CTRL_ACIE 0x1000 71 | #define CTRL_SLOT 0x2000 72 | 73 | enum { 74 | DEST_JOY = 0x01, 75 | DEST_MCD = 0x81 76 | }; 77 | 78 | enum { 79 | PAD_CONTROLLER_SDA, 80 | PAD_CONTROLLER_MOUSE, 81 | PAD_CONTROLLER_NAMCO_VOLUME, 82 | PAD_CONTROLLER_SANKYO_NASUKA, 83 | PAD_WHEEL_NEGCON, 84 | PAD_WHEEL_MADCATZ, 85 | PAD_WHEEL_MADCATZ_MC2 86 | }; 87 | 88 | /* 89 | To-do: Design API to interface any type of controller. 90 | 91 | Possible names: 92 | - psx_im (Input Method) 93 | - psx_controller 94 | - psx_input 95 | 96 | Private API should contain a way to get the ID of 97 | this controller, public API should contain the following 98 | functions: (WIP) 99 | - _write(data) 100 | - _read() 101 | _ _on_button_press(id) 102 | - _on_button_release(id) 103 | - _on_analog_change(id) 104 | */ 105 | 106 | typedef struct { 107 | uint32_t bus_delay; 108 | uint32_t io_base, io_size; 109 | 110 | psx_ic_t* ic; 111 | psx_input_t* joy_slot[2]; 112 | psx_mcd_t* mcd_slot[2]; 113 | 114 | int enable_once; 115 | int cycles_until_irq; 116 | int cycle_counter; 117 | int dest[2]; 118 | int irq_bit; 119 | 120 | uint16_t mode, ctrl, baud, stat; 121 | } psx_pad_t; 122 | 123 | psx_pad_t* psx_pad_create(void); 124 | void psx_pad_init(psx_pad_t*, psx_ic_t*); 125 | uint32_t psx_pad_read32(psx_pad_t*, uint32_t); 126 | uint16_t psx_pad_read16(psx_pad_t*, uint32_t); 127 | uint8_t psx_pad_read8(psx_pad_t*, uint32_t); 128 | void psx_pad_write32(psx_pad_t*, uint32_t, uint32_t); 129 | void psx_pad_write16(psx_pad_t*, uint32_t, uint16_t); 130 | void psx_pad_write8(psx_pad_t*, uint32_t, uint8_t); 131 | void psx_pad_destroy(psx_pad_t*); 132 | void psx_pad_button_press(psx_pad_t*, int, uint32_t); 133 | void psx_pad_button_release(psx_pad_t*, int, uint32_t); 134 | void psx_pad_analog_change(psx_pad_t*, int, uint32_t, uint16_t); 135 | void psx_pad_attach_joy(psx_pad_t*, int, psx_input_t*); 136 | void psx_pad_detach_joy(psx_pad_t*, int); 137 | int psx_pad_attach_mcd(psx_pad_t*, int, const char*); 138 | void psx_pad_detach_mcd(psx_pad_t*, int); 139 | void psx_pad_update(psx_pad_t*, int); 140 | 141 | #endif -------------------------------------------------------------------------------- /psx/dev/ram.c: -------------------------------------------------------------------------------- 1 | #include "ram.h" 2 | #include "../log.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | psx_ram_t* psx_ram_create(void) { 9 | return (psx_ram_t*)malloc(sizeof(psx_ram_t)); 10 | } 11 | 12 | void psx_ram_init(psx_ram_t* ram, psx_mc2_t* mc2, int size) { 13 | memset(ram, 0, sizeof(psx_ram_t)); 14 | 15 | ram->io_base = PSX_RAM_BEGIN; 16 | ram->io_size = PSX_RAM_SIZE; 17 | 18 | ram->mc2 = mc2; 19 | ram->buf = (uint8_t*)malloc(size); 20 | ram->size = size; 21 | 22 | // Size has to be a multiple of 2MB, default to 2MB 23 | if (size & 0x1ffff) 24 | size = RAM_SIZE_2MB; 25 | 26 | memset(ram->buf, RAM_INIT_FILL, size); 27 | } 28 | 29 | uint32_t psx_ram_read32(psx_ram_t* ram, uint32_t offset) { 30 | if (((ram->mc2->ram_size >> 9) & 7) == 3) 31 | if (offset >= 0x400000) 32 | return 0xffffffff; 33 | 34 | offset &= ram->size - 1; 35 | 36 | return *((uint32_t*)(ram->buf + offset)); 37 | } 38 | 39 | uint16_t psx_ram_read16(psx_ram_t* ram, uint32_t offset) { 40 | if (((ram->mc2->ram_size >> 9) & 7) == 3) 41 | if (offset >= 0x400000) 42 | return 0xffff; 43 | 44 | offset &= ram->size - 1; 45 | 46 | return *((uint16_t*)(ram->buf + offset)); 47 | } 48 | 49 | uint8_t psx_ram_read8(psx_ram_t* ram, uint32_t offset) { 50 | if (((ram->mc2->ram_size >> 9) & 7) == 3) 51 | if (offset >= 0x400000) 52 | return 0xff; 53 | 54 | offset &= ram->size - 1; 55 | 56 | return ram->buf[offset]; 57 | } 58 | 59 | void psx_ram_write32(psx_ram_t* ram, uint32_t offset, uint32_t value) { 60 | offset &= ram->size - 1; 61 | 62 | *((uint32_t*)(ram->buf + offset)) = value; 63 | } 64 | 65 | void psx_ram_write16(psx_ram_t* ram, uint32_t offset, uint16_t value) { 66 | offset &= ram->size - 1; 67 | 68 | *((uint16_t*)(ram->buf + offset)) = value; 69 | } 70 | 71 | void psx_ram_write8(psx_ram_t* ram, uint32_t offset, uint8_t value) { 72 | offset &= ram->size - 1; 73 | 74 | ram->buf[offset] = value; 75 | } 76 | 77 | void psx_ram_destroy(psx_ram_t* ram) { 78 | free(ram->buf); 79 | free(ram); 80 | } -------------------------------------------------------------------------------- /psx/dev/ram.h: -------------------------------------------------------------------------------- 1 | #ifndef RAM_H 2 | #define RAM_H 3 | 4 | #include 5 | 6 | #include "../log.h" 7 | #include "mc2.h" 8 | 9 | #define PSX_RAM_SIZE 0x800000 // 8MB window 10 | #define PSX_RAM_BEGIN 0x00000000 11 | //#define PSX_RAM_END 0x001fffff 12 | #define PSX_RAM_END 0x1effffff 13 | #define RAM_INIT_FILL 0 14 | 15 | #define RAM_SIZE_2MB 0x200000 16 | #define RAM_SIZE_4MB 0x400000 17 | #define RAM_SIZE_8MB 0x800000 18 | 19 | typedef struct { 20 | uint32_t bus_delay; 21 | uint32_t io_base, io_size; 22 | 23 | size_t size; 24 | 25 | psx_mc2_t* mc2; 26 | 27 | uint8_t* buf; 28 | } psx_ram_t; 29 | 30 | psx_ram_t* psx_ram_create(void); 31 | void psx_ram_init(psx_ram_t*, psx_mc2_t*, int size); 32 | uint32_t psx_ram_read32(psx_ram_t*, uint32_t); 33 | uint16_t psx_ram_read16(psx_ram_t*, uint32_t); 34 | uint8_t psx_ram_read8(psx_ram_t*, uint32_t); 35 | void psx_ram_write32(psx_ram_t*, uint32_t, uint32_t); 36 | void psx_ram_write16(psx_ram_t*, uint32_t, uint16_t); 37 | void psx_ram_write8(psx_ram_t*, uint32_t, uint8_t); 38 | void psx_ram_destroy(psx_ram_t*); 39 | 40 | #endif -------------------------------------------------------------------------------- /psx/dev/scratchpad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../log.h" 6 | #include "scratchpad.h" 7 | 8 | psx_scratchpad_t* psx_scratchpad_create(void) { 9 | return (psx_scratchpad_t*)malloc(sizeof(psx_scratchpad_t)); 10 | } 11 | 12 | void psx_scratchpad_init(psx_scratchpad_t* scratchpad) { 13 | memset(scratchpad, 0, sizeof(psx_scratchpad_t)); 14 | 15 | scratchpad->io_base = PSX_SCRATCHPAD_BEGIN; 16 | scratchpad->io_size = PSX_SCRATCHPAD_SIZE; 17 | 18 | scratchpad->buf = (uint8_t*)malloc(PSX_SCRATCHPAD_SIZE); 19 | } 20 | 21 | uint32_t psx_scratchpad_read32(psx_scratchpad_t* scratchpad, uint32_t offset) { 22 | return *((uint32_t*)(scratchpad->buf + offset)); 23 | } 24 | 25 | uint16_t psx_scratchpad_read16(psx_scratchpad_t* scratchpad, uint32_t offset) { 26 | return *((uint16_t*)(scratchpad->buf + offset)); 27 | } 28 | 29 | uint8_t psx_scratchpad_read8(psx_scratchpad_t* scratchpad, uint32_t offset) { 30 | return scratchpad->buf[offset]; 31 | } 32 | 33 | void psx_scratchpad_write32(psx_scratchpad_t* scratchpad, uint32_t offset, uint32_t value) { 34 | *((uint32_t*)(scratchpad->buf + offset)) = value; 35 | } 36 | 37 | void psx_scratchpad_write16(psx_scratchpad_t* scratchpad, uint32_t offset, uint16_t value) { 38 | *((uint16_t*)(scratchpad->buf + offset)) = value; 39 | } 40 | 41 | void psx_scratchpad_write8(psx_scratchpad_t* scratchpad, uint32_t offset, uint8_t value) { 42 | scratchpad->buf[offset] = value; 43 | } 44 | 45 | void psx_scratchpad_destroy(psx_scratchpad_t* scratchpad) { 46 | free(scratchpad->buf); 47 | free(scratchpad); 48 | } -------------------------------------------------------------------------------- /psx/dev/scratchpad.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRATCHPAD_H 2 | #define SCRATCHPAD_H 3 | 4 | #include 5 | 6 | #include "mc1.h" 7 | 8 | #define PSX_SCRATCHPAD_BEGIN 0x1f800000 9 | #define PSX_SCRATCHPAD_SIZE 0x400 10 | #define PSX_SCRATCHPAD_END 0x1f8003ff 11 | 12 | typedef struct { 13 | uint32_t bus_delay; 14 | uint32_t io_base, io_size; 15 | 16 | uint8_t* buf; 17 | } psx_scratchpad_t; 18 | 19 | psx_scratchpad_t* psx_scratchpad_create(void); 20 | void psx_scratchpad_init(psx_scratchpad_t*); 21 | uint32_t psx_scratchpad_read32(psx_scratchpad_t*, uint32_t); 22 | uint16_t psx_scratchpad_read16(psx_scratchpad_t*, uint32_t); 23 | uint8_t psx_scratchpad_read8(psx_scratchpad_t*, uint32_t); 24 | void psx_scratchpad_write32(psx_scratchpad_t*, uint32_t, uint32_t); 25 | void psx_scratchpad_write16(psx_scratchpad_t*, uint32_t, uint16_t); 26 | void psx_scratchpad_write8(psx_scratchpad_t*, uint32_t, uint8_t); 27 | void psx_scratchpad_destroy(psx_scratchpad_t*); 28 | 29 | #endif -------------------------------------------------------------------------------- /psx/dev/spu.h: -------------------------------------------------------------------------------- 1 | #ifndef SPU_H 2 | #define SPU_H 3 | 4 | #include 5 | 6 | #include "ic.h" 7 | 8 | #define PSX_SPU_BEGIN 0x1f801c00 9 | #define PSX_SPU_SIZE 0x400 10 | #define PSX_SPU_END 0x1f801fff 11 | 12 | #define SPU_RAM_SIZE 0x80000 13 | 14 | /* 15 | 1F801D88h - Voice 0..23 Key ON (Start Attack/Decay/Sustain) (KON) (W) 16 | 1F801D8Ch - Voice 0..23 Key OFF (Start Release) (KOFF) (W) 17 | 1F801D9Ch - Voice 0..23 ON/OFF (status) (ENDX) (R) 18 | 1F801DA6h - Sound RAM Data Transfer Address 19 | 1F801DA8h - Sound RAM Data Transfer Fifo 20 | 1F801DACh - Sound RAM Data Transfer Control (should be 0004h) 21 | */ 22 | 23 | #define SPUR_KONL 0x188 24 | #define SPUR_KONH 0x18a 25 | #define SPUR_KOFFL 0x18c 26 | #define SPUR_KOFFH 0x18e 27 | #define SPUR_EONL 0x198 28 | #define SPUR_EONH 0x19a 29 | #define SPUR_ENDXL 0x19c 30 | #define SPUR_ENDXH 0x19e 31 | #define SPUR_TADDR 0x1a6 32 | #define SPUR_TFIFO 0x1a8 33 | #define SPUR_SPUCNT 0x1aa 34 | #define SPUR_TCTRL 0x1ac 35 | #define SPUR_SPUSTAT 0x1ae 36 | #define SPUR_MBASE 0x1a2 37 | #define SPUR_SPUIRQA 0x1a4 38 | 39 | typedef struct __attribute__((__packed__)) { 40 | uint32_t bus_delay; 41 | uint32_t io_base, io_size; 42 | 43 | psx_ic_t* ic; 44 | uint8_t* ram; 45 | 46 | struct __attribute__((__packed__)) { 47 | uint16_t volumel; 48 | uint16_t volumer; 49 | uint16_t adsampr; 50 | uint16_t adsaddr; 51 | uint16_t envctl1; 52 | uint16_t envctl2; 53 | uint16_t envcvol; 54 | uint16_t adraddr; 55 | } voice[24]; 56 | 57 | uint16_t mainlvol; 58 | uint16_t mainrvol; 59 | uint16_t vlout; 60 | uint16_t vrout; 61 | uint32_t kon; 62 | uint32_t koff; 63 | uint32_t pmon; 64 | uint32_t non; 65 | uint32_t eon; 66 | uint32_t endx; 67 | uint16_t unk_da0; 68 | uint16_t mbase; 69 | uint16_t irq9addr; 70 | uint16_t ramdta; 71 | uint16_t ramdtf; 72 | uint16_t spucnt; 73 | uint16_t ramdtc; 74 | uint16_t spustat; 75 | uint32_t cdaivol; 76 | uint32_t extivol; 77 | uint32_t currvol; 78 | uint32_t unk_dbc; 79 | uint16_t dapf1; 80 | uint16_t dapf2; 81 | int16_t viir; 82 | int16_t vcomb1; 83 | int16_t vcomb2; 84 | int16_t vcomb3; 85 | int16_t vcomb4; 86 | int16_t vwall; 87 | int16_t vapf1; 88 | int16_t vapf2; 89 | uint16_t mlsame; 90 | uint16_t mrsame; 91 | uint16_t mlcomb1; 92 | uint16_t mrcomb1; 93 | uint16_t mlcomb2; 94 | uint16_t mrcomb2; 95 | uint16_t dlsame; 96 | uint16_t drsame; 97 | uint16_t mldiff; 98 | uint16_t mrdiff; 99 | uint16_t mlcomb3; 100 | uint16_t mrcomb3; 101 | uint16_t mlcomb4; 102 | uint16_t mrcomb4; 103 | uint16_t dldiff; 104 | uint16_t drdiff; 105 | uint16_t mlapf1; 106 | uint16_t mrapf1; 107 | uint16_t mlapf2; 108 | uint16_t mrapf2; 109 | int16_t vlin; 110 | int16_t vrin; 111 | 112 | // Internal registers unimplemented 113 | 114 | uint32_t taddr; 115 | uint16_t tfifo[32]; 116 | uint16_t tfifo_index; 117 | uint32_t revbaddr; 118 | int lrsl; 119 | int lrsr; 120 | int even_cycle; 121 | 122 | struct { 123 | int playing; 124 | uint32_t counter; 125 | uint32_t current_addr; 126 | uint32_t repeat_addr; 127 | uint32_t prev_sample_index; 128 | int16_t s[4]; 129 | int block_flags; 130 | int16_t buf[28]; 131 | int16_t h[2]; 132 | float lvol; 133 | float rvol; 134 | int cvol; 135 | int eon; 136 | int reverbl; 137 | int reverbr; 138 | 139 | /* 140 | ____lower 16bit (at 1F801C08h+N*10h)___________________________________ 141 | 15 Attack Mode (0=Linear, 1=Exponential) 142 | - Attack Direction (Fixed, always Increase) (until Level 7FFFh) 143 | 14-10 Attack Shift (0..1Fh = Fast..Slow) 144 | 9-8 Attack Step (0..3 = "+7,+6,+5,+4") 145 | - Decay Mode (Fixed, always Exponential) 146 | - Decay Direction (Fixed, always Decrease) (until Sustain Level) 147 | 7-4 Decay Shift (0..0Fh = Fast..Slow) 148 | - Decay Step (Fixed, always "-8") 149 | 3-0 Sustain Level (0..0Fh) ;Level=(N+1)*800h 150 | ____upper 16bit (at 1F801C0Ah+N*10h)___________________________________ 151 | 31 Sustain Mode (0=Linear, 1=Exponential) 152 | 30 Sustain Direction (0=Increase, 1=Decrease) (until Key OFF flag) 153 | 29 Not used? (should be zero) 154 | 28-24 Sustain Shift (0..1Fh = Fast..Slow) 155 | 23-22 Sustain Step (0..3 = "+7,+6,+5,+4" or "-8,-7,-6,-5") (inc/dec) 156 | 21 Release Mode (0=Linear, 1=Exponential) 157 | - Release Direction (Fixed, always Decrease) (until Level 0000h) 158 | 20-16 Release Shift (0..1Fh = Fast..Slow) 159 | - Release Step (Fixed, always "-8") 160 | */ 161 | 162 | int adsr_phase; 163 | int adsr_cycles_reload; 164 | int adsr_cycles; 165 | int adsr_mode; 166 | int adsr_dir; 167 | int adsr_shift; 168 | int adsr_step; 169 | int adsr_pending_step; 170 | int adsr_sustain_level; 171 | uint32_t envctl; 172 | } data[24]; 173 | } psx_spu_t; 174 | 175 | psx_spu_t* psx_spu_create(void); 176 | void psx_spu_init(psx_spu_t*, psx_ic_t*); 177 | uint32_t psx_spu_read32(psx_spu_t*, uint32_t); 178 | uint16_t psx_spu_read16(psx_spu_t*, uint32_t); 179 | uint8_t psx_spu_read8(psx_spu_t*, uint32_t); 180 | void psx_spu_write32(psx_spu_t*, uint32_t, uint32_t); 181 | void psx_spu_write16(psx_spu_t*, uint32_t, uint16_t); 182 | void psx_spu_write8(psx_spu_t*, uint32_t, uint8_t); 183 | void psx_spu_destroy(psx_spu_t*); 184 | void psx_spu_update_cdda_buffer(psx_spu_t*, void*); 185 | uint32_t psx_spu_get_sample(psx_spu_t*); 186 | 187 | #endif -------------------------------------------------------------------------------- /psx/dev/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include 5 | 6 | #include "ic.h" 7 | #include "gpu.h" 8 | 9 | #define PSX_TIMER_BEGIN 0x1f801100 10 | #define PSX_TIMER_SIZE 0x30 11 | #define PSX_TIMER_END 0x1f80112f 12 | 13 | /* 14 | 0 Synchronization Enable (0=Free Run, 1=Synchronize via Bit1-2) 15 | 1-2 Synchronization Mode (0-3, see lists below) 16 | Synchronization Modes for Counter 0: 17 | 0 = Pause counter during Hblank(s) 18 | 1 = Reset counter to 0000h at Hblank(s) 19 | 2 = Reset counter to 0000h at Hblank(s) and pause outside of Hblank 20 | 3 = Pause until Hblank occurs once, then switch to Free Run 21 | Synchronization Modes for Counter 1: 22 | Same as above, but using Vblank instead of Hblank 23 | Synchronization Modes for Counter 2: 24 | 0 or 3 = Stop counter at current value (forever, no h/v-blank start) 25 | 1 or 2 = Free Run (same as when Synchronization Disabled) 26 | 3 Reset counter to 0000h (0=After Counter=FFFFh, 1=After Counter=Target) 27 | 4 IRQ when Counter=Target (0=Disable, 1=Enable) 28 | 5 IRQ when Counter=FFFFh (0=Disable, 1=Enable) 29 | 6 IRQ Once/Repeat Mode (0=One-shot, 1=Repeatedly) 30 | 7 IRQ Pulse/Toggle Mode (0=Short Bit10=0 Pulse, 1=Toggle Bit10 on/off) 31 | 8-9 Clock Source (0-3, see list below) 32 | Counter 0: 0 or 2 = System Clock, 1 or 3 = Dotclock 33 | Counter 1: 0 or 2 = System Clock, 1 or 3 = Hblank 34 | Counter 2: 0 or 1 = System Clock, 2 or 3 = System Clock/8 35 | 10 Interrupt Request (0=Yes, 1=No) (Set after Writing) (W=1) (R) 36 | 11 Reached Target Value (0=No, 1=Yes) (Reset after Reading) (R) 37 | 12 Reached FFFFh Value (0=No, 1=Yes) (Reset after Reading) (R) 38 | 13-15 Unknown (seems to be always zero) 39 | 16-31 Garbage (next opcode) 40 | */ 41 | #define MODE_SYNCEN 0x0001 42 | #define MODE_SYNCMD 0x0006 43 | #define T0MD_HBLPAUSE 0 44 | #define T0MD_HBLRESET 1 45 | #define T0MD_HBLRANDP 2 46 | #define T0MD_HBLOSHOT 3 47 | // #define T1MD_VBLPAUSE 0 48 | // #define T1MD_VBLRESET 1 49 | // #define T1MD_VBLRANDP 2 50 | // #define T1MD_VBLOSHOT 3 51 | // #define T2MD_STOPMODE 0 52 | // #define T2MD_FREEMODE 1 53 | #define MODE_RESETC 0x0008 54 | #define MODE_TGTIRQ 0x0010 55 | #define MODE_MAXIRQ 0x0020 56 | #define MODE_IRQRMD 0x0040 57 | #define MODE_IRQPMD 0x0080 58 | #define MODE_CLK 0x0080 59 | 60 | /* 61 | 0 Synchronization Enable (0=Free Run, 1=Synchronize via Bit1-2) 62 | 1-2 Synchronization Mode (0-3, see lists below) 63 | Synchronization Modes for Counter 0: 64 | 0 = Pause counter during Hblank(s) 65 | 1 = Reset counter to 0000h at Hblank(s) 66 | 2 = Reset counter to 0000h at Hblank(s) and pause outside of Hblank 67 | 3 = Pause until Hblank occurs once, then switch to Free Run 68 | Synchronization Modes for Counter 1: 69 | Same as above, but using Vblank instead of Hblank 70 | Synchronization Modes for Counter 2: 71 | 0 or 3 = Stop counter at current value (forever, no h/v-blank start) 72 | 1 or 2 = Free Run (same as when Synchronization Disabled) 73 | 3 Reset counter to 0000h (0=After Counter=FFFFh, 1=After Counter=Target) 74 | 4 IRQ when Counter=Target (0=Disable, 1=Enable) 75 | 5 IRQ when Counter=FFFFh (0=Disable, 1=Enable) 76 | 6 IRQ Once/Repeat Mode (0=One-shot, 1=Repeatedly) 77 | 7 IRQ Pulse/Toggle Mode (0=Short Bit10=0 Pulse, 1=Toggle Bit10 on/off) 78 | 8-9 Clock Source (0-3, see list below) 79 | Counter 0: 0 or 2 = System Clock, 1 or 3 = Dotclock 80 | Counter 1: 0 or 2 = System Clock, 1 or 3 = Hblank 81 | Counter 2: 0 or 1 = System Clock, 2 or 3 = System Clock/8 82 | 10 Interrupt Request (0=Yes, 1=No) (Set after Writing) (W=1) (R) 83 | 11 Reached Target Value (0=No, 1=Yes) (Reset after Reading) (R) 84 | 12 Reached FFFFh Value (0=No, 1=Yes) (Reset after Reading) (R) 85 | 13-15 Unknown (seems to be always zero) 86 | 16-31 Garbage (next opcode) 87 | */ 88 | 89 | typedef struct { 90 | uint32_t bus_delay; 91 | uint32_t io_base, io_size; 92 | 93 | psx_ic_t* ic; 94 | psx_gpu_t* gpu; 95 | 96 | int hblank, prev_hblank; 97 | int vblank, prev_vblank; 98 | 99 | struct { 100 | float counter; 101 | uint32_t target; 102 | int sync_enable; 103 | int sync_mode; 104 | int reset_target; 105 | int irq_target; 106 | int irq_max; 107 | int irq_repeat; 108 | int irq_toggle; 109 | int clk_source; 110 | int irq; 111 | int target_reached; 112 | int max_reached; 113 | int irq_fired; 114 | uint32_t div_counter; 115 | 116 | int paused; 117 | int blank_once; 118 | } timer[3]; 119 | } psx_timer_t; 120 | 121 | psx_timer_t* psx_timer_create(void); 122 | void psx_timer_init(psx_timer_t*, psx_ic_t*, psx_gpu_t*); 123 | uint32_t psx_timer_read32(psx_timer_t*, uint32_t); 124 | uint16_t psx_timer_read16(psx_timer_t*, uint32_t); 125 | uint8_t psx_timer_read8(psx_timer_t*, uint32_t); 126 | void psx_timer_write32(psx_timer_t*, uint32_t, uint32_t); 127 | void psx_timer_write16(psx_timer_t*, uint32_t, uint16_t); 128 | void psx_timer_write8(psx_timer_t*, uint32_t, uint8_t); 129 | void psx_timer_update(psx_timer_t*, int); 130 | void psx_timer_destroy(psx_timer_t*); 131 | 132 | // GPU event handlers 133 | void psxe_gpu_hblank_event_cb(psx_gpu_t*); 134 | void psxe_gpu_hblank_end_event_cb(psx_gpu_t*); 135 | void psxe_gpu_vblank_timer_event_cb(psx_gpu_t*); 136 | void psxe_gpu_vblank_end_event_cb(psx_gpu_t*); 137 | 138 | #endif -------------------------------------------------------------------------------- /psx/dev/xa.c: -------------------------------------------------------------------------------- 1 | #include "xa.h" 2 | 3 | #include 4 | 5 | void xa_decode_audio(uint8_t* src, uint16_t* dst) { 6 | // Not a XA sector 7 | if (src[XA_HDR_MODE] != 0x02) 8 | return; 9 | 10 | // uint8_t ci = src[XA_SHDR_CODINGINFO]; 11 | } -------------------------------------------------------------------------------- /psx/dev/xa.h: -------------------------------------------------------------------------------- 1 | #ifndef XA_H 2 | #define XA_H 3 | 4 | /* 5 | 000h 0Ch Sync 6 | 00Ch 4 Header (Minute,Second,Sector,Mode=02h) 7 | 010h 4 Sub-Header (File, Channel, Submode with bit5=1, Codinginfo) 8 | 014h 4 Copy of Sub-Header 9 | 018h 914h Data (2324 bytes) 10 | 92Ch 4 EDC (checksum accross [010h..92Bh]) (or 00000000h if no EDC) 11 | */ 12 | 13 | enum { 14 | XA_HDR_MINUTE = 0x0c, 15 | XA_HDR_SECOND, 16 | XA_HDR_SECTOR, 17 | XA_HDR_MODE, 18 | XA_SHDR_FILE, 19 | XA_SHDR_CHANNEL, 20 | XA_SHDR_SUBMODE, 21 | XA_SHDR_CODINGINFO 22 | }; 23 | 24 | /* 25 | 0 End of Record (EOR) (all Volume Descriptors, and all sectors with EOF) 26 | 1 Video ;\Sector Type (usually ONE of these bits should be set) 27 | 2 Audio ; Note: PSX .STR files are declared as Data (not as Video) 28 | 3 Data ;/ 29 | 4 Trigger (for application use) 30 | 5 Form2 (0=Form1/800h-byte data, 1=Form2, 914h-byte data) 31 | 6 Real Time (RT) 32 | 7 End of File (EOF) (or end of Directory/PathTable/VolumeTerminator) 33 | */ 34 | 35 | enum { 36 | XA_SM_EOR = 0x01, 37 | XA_SM_VIDEO = 0x02, 38 | XA_SM_AUDIO = 0x04, 39 | XA_SM_DATA = 0x08, 40 | XA_SM_TRIG = 0x10, 41 | XA_SM_FORM2 = 0x20, 42 | XA_SM_RT = 0x40, 43 | XA_SM_EOF = 0x80 44 | }; 45 | 46 | /* 47 | 0-1 Mono/Stereo (0=Mono, 1=Stereo, 2-3=Reserved) 48 | 2-2 Sample Rate (0=37800Hz, 1=18900Hz, 2-3=Reserved) 49 | 4-5 Bits per Sample (0=Normal/4bit, 1=8bit, 2-3=Reserved) 50 | 6 Emphasis (0=Normal/Off, 1=Emphasis) 51 | 7 Reserved (0) 52 | */ 53 | 54 | enum { 55 | XA_CI_MODE = 0x03, 56 | XA_CI_SAMPLERATE = 0x0c, 57 | XA_CI_BPS = 0x30, 58 | XA_CI_EMPHASIS = 0x40, 59 | XA_CI_MONO = 0x00, 60 | XA_CI_STEREO = 0x01, 61 | XA_CI_37800HZ = 0x00, 62 | XA_CI_18900HZ = 0x04, 63 | XA_CI_4BIT = 0x00, 64 | XA_CI_8BIT = 0x10 65 | }; 66 | 67 | void xa_decode_sector(void*, void*); 68 | 69 | #endif -------------------------------------------------------------------------------- /psx/exe.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "exe.h" 4 | #include "log.h" 5 | 6 | int psx_exe_load(psx_cpu_t* cpu, const char* path) { 7 | if (!path) 8 | return 0; 9 | 10 | FILE* file = fopen(path, "rb"); 11 | 12 | if (!file) 13 | return 1; 14 | 15 | // Read header 16 | psx_exe_hdr_t hdr; 17 | 18 | if (!fread((char*)&hdr, 1, sizeof(psx_exe_hdr_t), file)) 19 | return 2; 20 | 21 | // Seek to program start 22 | fseek(file, 0x800, SEEK_SET); 23 | 24 | // Read to RAM directly 25 | uint32_t offset = hdr.ramdest & 0x7fffffff; 26 | 27 | if (!fread(cpu->bus->ram->buf + offset, 1, hdr.filesz, file)) 28 | return 3; 29 | 30 | // Load initial register values 31 | cpu->pc = hdr.ipc; 32 | cpu->next_pc = cpu->pc + 4; 33 | cpu->r[28] = hdr.igp; 34 | 35 | if (hdr.ispb) { 36 | cpu->r[29] = hdr.ispb + hdr.ispoff; 37 | cpu->r[30] = cpu->r[29]; 38 | } 39 | 40 | log_info("Loaded PS-X EXE file \"%s\"", path); 41 | log_fatal("PC=%08x SP=%08x (%08x) GP=%08x", cpu->pc, cpu->r[29], hdr.ispb, cpu->r[28]); 42 | 43 | fclose(file); 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /psx/exe.h: -------------------------------------------------------------------------------- 1 | #ifndef EXE_H 2 | #define EXE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cpu.h" 9 | #include "bus_init.h" 10 | 11 | /* 12 | PSX executables are having an 800h-byte header, followed by the code/data. 13 | 14 | 000h-007h ASCII ID "PS-X EXE" 15 | 008h-00Fh Zerofilled 16 | 010h Initial PC (usually 80010000h, or higher) 17 | 014h Initial GP/R28 (usually 0) 18 | 018h Destination Address in RAM (usually 80010000h, or higher) 19 | 01Ch Filesize (must be N*800h) (excluding 800h-byte header) 20 | 020h Unknown/Unused ;Addr (usually 0) ;\optional overlay? 21 | 024h Unknown/Unused ;Size (usually 0) ;/(not auto-loaded) 22 | 028h Memfill Start Address (usually 0) (when below Size=None) 23 | 02Ch Memfill Size in bytes (usually 0) (0=None) 24 | 030h Initial SP/R29 & FP/R30 Base (usually 801FFFF0h) (or 0=None) 25 | 034h Initial SP/R29 & FP/R30 Offs (usually 0, added to above Base) 26 | 038h-04Bh Reserved for A(43h) Function (should be zerofilled in exefile) 27 | 04Ch-xxxh ASCII marker 28 | "Sony Computer Entertainment Inc. for Japan area" ;NTSC 29 | "Sony Computer Entertainment Inc. for Europe area" ;PAL 30 | "Sony Computer Entertainment Inc. for North America area" ;NTSC 31 | (or often zerofilled in some homebrew files) 32 | (the BIOS doesn't verify this string, and boots fine without it) 33 | xxxh-7FFh Zerofilled 34 | 800h... Code/Data (loaded to entry[018h] and up) 35 | */ 36 | 37 | typedef struct { 38 | char id[16]; 39 | uint32_t ipc; 40 | uint32_t igp; 41 | uint32_t ramdest; 42 | uint32_t filesz; 43 | uint32_t unk20; 44 | uint32_t unk24; 45 | uint32_t mfill_start; 46 | uint32_t mfill_size; 47 | uint32_t ispb; 48 | uint32_t ispoff; 49 | } psx_exe_hdr_t; 50 | 51 | int psx_exe_load(psx_cpu_t*, const char*); 52 | 53 | #endif -------------------------------------------------------------------------------- /psx/input/guncon.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the PSXE Emulator Project 3 | 4 | Sony PlayStation Standard Digital/Analog Controller Emulator 5 | */ 6 | 7 | #include "guncon.h" 8 | #include "../log.h" 9 | 10 | #include 11 | #include 12 | 13 | const char* states[] = { 14 | "HIZ", 15 | "IDL", 16 | "IDH", 17 | "SWL", 18 | "SWH", 19 | "XPL", 20 | "XPH", 21 | "YPL", 22 | "YPH" 23 | }; 24 | 25 | psxi_guncon_t* psxi_guncon_create(void) { 26 | return (psxi_guncon_t*)malloc(sizeof(psxi_guncon_t)); 27 | } 28 | 29 | void psxi_guncon_init(psxi_guncon_t* guncon) { 30 | memset(guncon, 0, sizeof(psxi_guncon_t)); 31 | 32 | guncon->tx_data = 0xff; 33 | guncon->tx_data_ready = 1; 34 | guncon->state = GUNCON_STATE_TX_HIZ; 35 | guncon->sw = 0xffff; 36 | guncon->x = 0x4d; 37 | guncon->y = 0x19; 38 | guncon->sx = 384; 39 | guncon->sy = 240; 40 | } 41 | 42 | uint32_t psxi_guncon_read(void* udata) { 43 | psxi_guncon_t* guncon = (psxi_guncon_t*)udata; 44 | 45 | switch (guncon->state) { 46 | case GUNCON_STATE_TX_HIZ: guncon->tx_data = 0xff; break; 47 | case GUNCON_STATE_TX_IDL: guncon->tx_data = GUNCON_IDL; break; 48 | case GUNCON_STATE_TX_IDH: guncon->tx_data = GUNCON_IDH; break; 49 | case GUNCON_STATE_TX_SWL: guncon->tx_data = guncon->sw & 0xff; break; 50 | case GUNCON_STATE_TX_SWH: guncon->tx_data = guncon->sw >> 8; break; 51 | case GUNCON_STATE_TX_XPL: guncon->tx_data = guncon->x & 0xff; break; 52 | case GUNCON_STATE_TX_XPH: guncon->tx_data = guncon->x >> 8; break; 53 | case GUNCON_STATE_TX_YPL: guncon->tx_data = guncon->y & 0xff; break; 54 | case GUNCON_STATE_TX_YPH: { 55 | // printf("guncon: read state %u (%s) -> %02x\n", guncon->state, states[guncon->state], guncon->y >> 8); 56 | 57 | guncon->tx_data_ready = 0; 58 | guncon->state = GUNCON_STATE_TX_HIZ; 59 | 60 | return guncon->y >> 8; 61 | } break; 62 | } 63 | 64 | // printf("guncon: read state %u (%s) -> %02x\n", guncon->state, states[guncon->state], guncon->tx_data); 65 | 66 | guncon->tx_data_ready = 1; 67 | guncon->state++; 68 | 69 | return guncon->tx_data; 70 | } 71 | 72 | void psxi_guncon_write(void* udata, uint16_t data) { 73 | psxi_guncon_t* guncon = (psxi_guncon_t*)udata; 74 | 75 | // printf("guncon: write %02x\n", data); 76 | 77 | (void)guncon; 78 | } 79 | 80 | void psxi_guncon_on_button_press(void* udata, uint32_t data) { 81 | psxi_guncon_t* guncon = (psxi_guncon_t*)udata; 82 | 83 | guncon->sw &= ~data; 84 | } 85 | 86 | void psxi_guncon_on_button_release(void* udata, uint32_t data) { 87 | psxi_guncon_t* guncon = (psxi_guncon_t*)udata; 88 | 89 | guncon->sw |= data; 90 | } 91 | 92 | void psxi_guncon_on_analog_change(void* udata, uint32_t axis, uint16_t data) { 93 | psxi_guncon_t* guncon = (psxi_guncon_t*)udata; 94 | 95 | switch (axis) { 96 | case PSXI_AX_GUNCON_X: { 97 | data *= (461.0f - 77.0f) / (float)guncon->sx; 98 | data += 77; 99 | 100 | guncon->x = data; 101 | } break; 102 | 103 | case PSXI_AX_GUNCON_Y: { 104 | data *= (248.0f - 25.0f) / (float)guncon->sy; 105 | data += 25; 106 | 107 | guncon->y = data; 108 | } break; 109 | 110 | case PSXI_AX_GUNCON_SX: { 111 | guncon->sx = data; 112 | } break; 113 | 114 | case PSXI_AX_GUNCON_SY: { 115 | guncon->sy = data; 116 | } break; 117 | } 118 | } 119 | 120 | int psxi_guncon_query_fifo(void* udata) { 121 | psxi_guncon_t* guncon = (psxi_guncon_t*)udata; 122 | 123 | return guncon->tx_data_ready; 124 | } 125 | 126 | void psxi_guncon_init_input(psxi_guncon_t* guncon, psx_input_t* input) { 127 | input->udata = guncon; 128 | input->write_func = psxi_guncon_write; 129 | input->read_func = psxi_guncon_read; 130 | input->on_button_press_func = psxi_guncon_on_button_press; 131 | input->on_button_release_func = psxi_guncon_on_button_release; 132 | input->on_analog_change_func = psxi_guncon_on_analog_change; 133 | input->query_fifo_func = psxi_guncon_query_fifo; 134 | } 135 | 136 | void psxi_guncon_destroy(psxi_guncon_t* guncon) { 137 | free(guncon); 138 | } -------------------------------------------------------------------------------- /psx/input/guncon.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the PSXE Emulator Project 3 | 4 | Namco GunCon emulation 5 | */ 6 | 7 | #ifndef GUNCON_H 8 | #define GUNCON_H 9 | 10 | #include "../dev/input.h" 11 | 12 | /* 13 | __Halfword 0 (Controller Info)___________________ 14 | 0-15 Controller Info (5A63h=Namco Lightgun; GunCon/Cinch Type) 15 | __Halfword 1 (Buttons)___________________________ 16 | 0-2 Not used (All bits always 1) 17 | 3 Button A (Left Side) (0=Pressed, 1=Released) ;aka Joypad Start 18 | 4-12 Not used (All bits always 1) 19 | 13 Trigger Button (0=Pressed, 1=Released) ;aka Joypad O-Button 20 | 14 Button B (Right Side) (0=Pressed, 1=Released) ;aka Joypad X-Button 21 | 15 Not used (All bits always 1) 22 | __Halfword 2 (X)_________________________________ 23 | 0-15 8MHz clks since HSYNC (01h=Error, or 04Dh..1CDh) 24 | __Halfword 3 (Y)_________________________________ 25 | 0-15 Scanlines since VSYNC (05h/0Ah=Error, PAL=20h..127h, NTSC=19h..F8h) 26 | */ 27 | 28 | #define GUNCON_IDL 0x63 29 | #define GUNCON_IDH 0x5a 30 | 31 | #define PSXI_SW_GUNCON_A 0x00000008 32 | #define PSXI_SW_GUNCON_TRIGGER 0x00002000 33 | #define PSXI_SW_GUNCON_B 0x00004000 34 | #define PSXI_AX_GUNCON_X 0x00010000 35 | #define PSXI_AX_GUNCON_Y 0x00020000 36 | #define PSXI_AX_GUNCON_SX 0x00030000 37 | #define PSXI_AX_GUNCON_SY 0x00040000 38 | 39 | enum { 40 | GUNCON_STATE_TX_HIZ = 0, 41 | GUNCON_STATE_TX_IDL, 42 | GUNCON_STATE_TX_IDH, 43 | GUNCON_STATE_TX_SWL, 44 | GUNCON_STATE_TX_SWH, 45 | GUNCON_STATE_TX_XPL, 46 | GUNCON_STATE_TX_XPH, 47 | GUNCON_STATE_TX_YPL, 48 | GUNCON_STATE_TX_YPH 49 | }; 50 | 51 | typedef struct { 52 | int state; 53 | uint16_t sw; 54 | uint8_t tx_data; 55 | int tx_data_ready; 56 | uint16_t x; 57 | uint16_t y; 58 | uint16_t sx; 59 | uint16_t sy; 60 | } psxi_guncon_t; 61 | 62 | psxi_guncon_t* psxi_guncon_create(void); 63 | void psxi_guncon_init(psxi_guncon_t*); 64 | void psxi_guncon_init_input(psxi_guncon_t*, psx_input_t*); 65 | void psxi_guncon_destroy(psxi_guncon_t*); 66 | 67 | #endif -------------------------------------------------------------------------------- /psx/input/sda.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the PSXE Emulator Project 3 | 4 | Sony PlayStation Standard Digital/Analog Controller Emulator 5 | */ 6 | 7 | #include "sda.h" 8 | #include "../log.h" 9 | 10 | #include 11 | #include 12 | 13 | psxi_sda_t* psxi_sda_create(void) { 14 | return (psxi_sda_t*)malloc(sizeof(psxi_sda_t)); 15 | } 16 | 17 | void psxi_sda_init(psxi_sda_t* sda, uint16_t model) { 18 | memset(sda, 0, sizeof(psxi_sda_t)); 19 | 20 | sda->tx_data = 0xff; 21 | sda->tx_data_ready = 1; 22 | sda->prev_model = model; 23 | sda->model = model; 24 | sda->state = SDA_STATE_TX_HIZ; 25 | sda->sw = 0xffff; 26 | sda->sa_mode = SA_MODE_DIGITAL; 27 | sda->adc0 = 0x80; 28 | sda->adc1 = 0x80; 29 | sda->adc2 = 0x80; 30 | sda->adc3 = 0x80; 31 | } 32 | 33 | uint32_t psxi_sda_read(void* udata) { 34 | psxi_sda_t* sda = (psxi_sda_t*)udata; 35 | 36 | switch (sda->state) { 37 | case SDA_STATE_TX_HIZ: sda->tx_data = 0xff; break; 38 | case SDA_STATE_TX_IDL: sda->tx_data = sda->model; break; 39 | case SDA_STATE_TX_IDH: sda->tx_data = 0x5a; break; 40 | case SDA_STATE_TX_SWL: sda->tx_data = sda->sw & 0xff; break; 41 | 42 | // Digital pad stops sending data here 43 | case SDA_STATE_TX_SWH: { 44 | if (sda->sa_mode == SA_MODE_ANALOG) { 45 | sda->tx_data_ready = 1; 46 | sda->state = SDA_STATE_TX_ADC0; 47 | } else { 48 | sda->tx_data_ready = 0; 49 | sda->state = SDA_STATE_TX_HIZ; 50 | } 51 | 52 | return sda->sw >> 8; 53 | } break; 54 | 55 | case SDA_STATE_TX_ADC0: sda->tx_data = sda->adc0; break; 56 | case SDA_STATE_TX_ADC1: sda->tx_data = sda->adc1; break; 57 | case SDA_STATE_TX_ADC2: sda->tx_data = sda->adc2; break; 58 | 59 | // Analog pad stops sending data here 60 | case SDA_STATE_TX_ADC3: { 61 | sda->tx_data_ready = 0; 62 | sda->state = SDA_STATE_TX_HIZ; 63 | 64 | if (sda->model == 0xf3) 65 | sda->model = sda->prev_model; 66 | 67 | return sda->adc3; 68 | } break; 69 | 70 | } 71 | 72 | // printf(" sda read %u -> %02x\n", sda->state, sda->tx_data); 73 | 74 | sda->tx_data_ready = 1; 75 | sda->state++; 76 | 77 | return sda->tx_data; 78 | } 79 | 80 | void psxi_sda_write(void* udata, uint16_t data) { 81 | psxi_sda_t* sda = (psxi_sda_t*)udata; 82 | 83 | // To-do: Handle TAP and MOT bytes here 84 | 85 | if (data == 0x43) { 86 | if (sda->sa_mode == SA_MODE_ANALOG) { 87 | sda->prev_model = sda->model; 88 | sda->model = 0xf3; 89 | } else { 90 | sda->tx_data = 0xff; 91 | sda->tx_data_ready = 0; 92 | sda->state = SDA_STATE_TX_HIZ; 93 | } 94 | } 95 | } 96 | 97 | void psxi_sda_on_button_press(void* udata, uint32_t data) { 98 | psxi_sda_t* sda = (psxi_sda_t*)udata; 99 | 100 | if (data == PSXI_SW_SDA_ANALOG) { 101 | sda->sa_mode ^= 1; 102 | 103 | if (sda->sa_mode) { 104 | sda->prev_model = sda->model; 105 | sda->model = SDA_MODEL_ANALOG_STICK; 106 | } else { 107 | sda->model = sda->prev_model; 108 | } 109 | 110 | printf("sda: Switched to %s mode\n", sda->sa_mode ? "analog" : "digital"); 111 | 112 | return; 113 | } 114 | 115 | sda->sw &= ~data; 116 | } 117 | 118 | void psxi_sda_on_button_release(void* udata, uint32_t data) { 119 | psxi_sda_t* sda = (psxi_sda_t*)udata; 120 | 121 | sda->sw |= data; 122 | } 123 | 124 | // To-do: Implement analog mode 125 | void psxi_sda_on_analog_change(void* udata, uint32_t axis, uint16_t data) { 126 | // Suppress warning until we implement analog mode 127 | psxi_sda_t* sda = (psxi_sda_t*)udata; 128 | 129 | switch (axis) { 130 | case PSXI_AX_SDA_RIGHT_HORZ: sda->adc0 = data; break; 131 | case PSXI_AX_SDA_RIGHT_VERT: sda->adc1 = data; break; 132 | case PSXI_AX_SDA_LEFT_HORZ: sda->adc2 = data; break; 133 | case PSXI_AX_SDA_LEFT_VERT: sda->adc3 = data; break; 134 | } 135 | } 136 | 137 | int psxi_sda_query_fifo(void* udata) { 138 | psxi_sda_t* sda = (psxi_sda_t*)udata; 139 | 140 | return sda->tx_data_ready; 141 | } 142 | 143 | void psxi_sda_init_input(psxi_sda_t* sda, psx_input_t* input) { 144 | input->udata = sda; 145 | input->write_func = psxi_sda_write; 146 | input->read_func = psxi_sda_read; 147 | input->on_button_press_func = psxi_sda_on_button_press; 148 | input->on_button_release_func = psxi_sda_on_button_release; 149 | input->on_analog_change_func = psxi_sda_on_analog_change; 150 | input->query_fifo_func = psxi_sda_query_fifo; 151 | } 152 | 153 | void psxi_sda_destroy(psxi_sda_t* sda) { 154 | free(sda); 155 | } -------------------------------------------------------------------------------- /psx/input/sda.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the PSXE Emulator Project 3 | 4 | Sony PlayStation Standard Digital/Analog Controller Emulator 5 | */ 6 | 7 | #ifndef SDA_H 8 | #define SDA_H 9 | 10 | #include "../dev/input.h" 11 | 12 | // Controller/Input IDs 13 | #define SDA_MODEL_DIGITAL 0x41 14 | #define SDA_MODEL_ANALOG_PAD 0x73 15 | #define SDA_MODEL_ANALOG_STICK 0x53 16 | 17 | #define PSXI_SW_SDA_SELECT 0x00000001 18 | #define PSXI_SW_SDA_L3 0x00000002 19 | #define PSXI_SW_SDA_R3 0x00000004 20 | #define PSXI_SW_SDA_START 0x00000008 21 | #define PSXI_SW_SDA_PAD_UP 0x00000010 22 | #define PSXI_SW_SDA_PAD_RIGHT 0x00000020 23 | #define PSXI_SW_SDA_PAD_DOWN 0x00000040 24 | #define PSXI_SW_SDA_PAD_LEFT 0x00000080 25 | #define PSXI_SW_SDA_L2 0x00000100 26 | #define PSXI_SW_SDA_R2 0x00000200 27 | #define PSXI_SW_SDA_L1 0x00000400 28 | #define PSXI_SW_SDA_R1 0x00000800 29 | #define PSXI_SW_SDA_TRIANGLE 0x00001000 30 | #define PSXI_SW_SDA_CIRCLE 0x00002000 31 | #define PSXI_SW_SDA_CROSS 0x00004000 32 | #define PSXI_SW_SDA_SQUARE 0x00008000 33 | #define PSXI_SW_SDA_ANALOG 0x00010000 34 | #define PSXI_AX_SDA_RIGHT_HORZ 0x00020000 35 | #define PSXI_AX_SDA_RIGHT_VERT 0x00030000 36 | #define PSXI_AX_SDA_LEFT_HORZ 0x00040000 37 | #define PSXI_AX_SDA_LEFT_VERT 0x00050000 38 | 39 | enum { 40 | SDA_STATE_TX_HIZ = 0, 41 | SDA_STATE_TX_IDL, 42 | SDA_STATE_TX_IDH, 43 | SDA_STATE_TX_SWL, 44 | SDA_STATE_TX_SWH, 45 | SDA_STATE_TX_ADC0, 46 | SDA_STATE_TX_ADC1, 47 | SDA_STATE_TX_ADC2, 48 | SDA_STATE_TX_ADC3 49 | }; 50 | 51 | enum { 52 | SA_MODE_DIGITAL = 0, 53 | SA_MODE_ANALOG 54 | }; 55 | 56 | typedef struct { 57 | uint8_t prev_model; 58 | uint8_t model; 59 | int state; 60 | int sa_mode; 61 | uint16_t sw; 62 | uint8_t tx_data; 63 | int tx_data_ready; 64 | uint8_t adc0; 65 | uint8_t adc1; 66 | uint8_t adc2; 67 | uint8_t adc3; 68 | } psxi_sda_t; 69 | 70 | psxi_sda_t* psxi_sda_create(void); 71 | void psxi_sda_init(psxi_sda_t*, uint16_t); 72 | void psxi_sda_init_input(psxi_sda_t*, psx_input_t*); 73 | void psxi_sda_destroy(psxi_sda_t*); 74 | 75 | #endif -------------------------------------------------------------------------------- /psx/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "log.h" 24 | 25 | #define MAX_CALLBACKS 32 26 | 27 | typedef struct { 28 | log_LogFn fn; 29 | void *udata; 30 | int level; 31 | } Callback; 32 | 33 | static struct { 34 | void *udata; 35 | log_LockFn lock; 36 | int level; 37 | bool quiet; 38 | Callback callbacks[MAX_CALLBACKS]; 39 | } L; 40 | 41 | 42 | static const char *level_strings[] = { 43 | "trace", "debug", "info", "warn", "error", "fatal" 44 | }; 45 | 46 | #ifdef LOG_USE_COLOR 47 | static const char *level_colors[] = { 48 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 49 | }; 50 | #endif 51 | 52 | 53 | static void stdout_callback(log_Event *ev) { 54 | #ifdef LOG_USE_COLOR 55 | fprintf( 56 | ev->udata, "psx: %s%-5s\x1b[0m \x1b[90m%s:\x1b[0m ", 57 | level_colors[ev->level], level_strings[ev->level], 58 | ev->file); 59 | #else 60 | fprintf( 61 | ev->udata, "psx: %-5s %s: ", 62 | level_strings[ev->level], ev->file); 63 | #endif 64 | vfprintf(ev->udata, ev->fmt, ev->ap); 65 | fprintf(ev->udata, "\n"); 66 | fflush(ev->udata); 67 | } 68 | 69 | 70 | static void file_callback(log_Event *ev) { 71 | char buf[64]; 72 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; 73 | fprintf( 74 | ev->udata, "%s %-5s %s:%d: ", 75 | buf, level_strings[ev->level], ev->file, ev->line); 76 | vfprintf(ev->udata, ev->fmt, ev->ap); 77 | fprintf(ev->udata, "\n"); 78 | fflush(ev->udata); 79 | } 80 | 81 | 82 | static void lock(void) { 83 | if (L.lock) { L.lock(true, L.udata); } 84 | } 85 | 86 | 87 | static void unlock(void) { 88 | if (L.lock) { L.lock(false, L.udata); } 89 | } 90 | 91 | 92 | const char* log_level_string(int level) { 93 | return level_strings[level]; 94 | } 95 | 96 | 97 | void log_set_lock(log_LockFn fn, void *udata) { 98 | L.lock = fn; 99 | L.udata = udata; 100 | } 101 | 102 | 103 | void log_set_level(int level) { 104 | L.level = level; 105 | } 106 | 107 | 108 | void log_set_quiet(bool enable) { 109 | L.quiet = enable; 110 | } 111 | 112 | 113 | int log_add_callback(log_LogFn fn, void *udata, int level) { 114 | for (int i = 0; i < MAX_CALLBACKS; i++) { 115 | if (!L.callbacks[i].fn) { 116 | L.callbacks[i] = (Callback) { fn, udata, level }; 117 | return 0; 118 | } 119 | } 120 | return -1; 121 | } 122 | 123 | 124 | int log_add_fp(FILE *fp, int level) { 125 | return log_add_callback(file_callback, fp, level); 126 | } 127 | 128 | 129 | static void init_event(log_Event *ev, void *udata) { 130 | if (!ev->time) { 131 | time_t t = time(NULL); 132 | ev->time = localtime(&t); 133 | } 134 | ev->udata = udata; 135 | } 136 | 137 | 138 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 139 | log_Event ev = { 140 | .fmt = fmt, 141 | .file = file, 142 | .line = line, 143 | .level = level, 144 | }; 145 | 146 | lock(); 147 | 148 | if (!L.quiet && level >= L.level) { 149 | init_event(&ev, stderr); 150 | va_start(ev.ap, fmt); 151 | stdout_callback(&ev); 152 | va_end(ev.ap); 153 | } 154 | 155 | for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { 156 | Callback *cb = &L.callbacks[i]; 157 | if (level >= cb->level) { 158 | init_event(&ev, cb->udata); 159 | va_start(ev.ap, fmt); 160 | cb->fn(&ev); 161 | va_end(ev.ap); 162 | } 163 | } 164 | 165 | unlock(); 166 | } 167 | -------------------------------------------------------------------------------- /psx/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define LOG_VERSION "0.1.0" 17 | 18 | typedef struct { 19 | va_list ap; 20 | const char *fmt; 21 | const char *file; 22 | struct tm *time; 23 | void *udata; 24 | int line; 25 | int level; 26 | } log_Event; 27 | 28 | typedef void (*log_LogFn)(log_Event *ev); 29 | typedef void (*log_LockFn)(bool lock, void *udata); 30 | 31 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 32 | 33 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 34 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 35 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 36 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 37 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 38 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 39 | 40 | const char* log_level_string(int level); 41 | void log_set_lock(log_LockFn fn, void *udata); 42 | void log_set_level(int level); 43 | void log_set_quiet(bool enable); 44 | int log_add_callback(log_LogFn fn, void *udata, int level); 45 | int log_add_fp(FILE *fp, int level); 46 | 47 | void log_log(int level, const char *file, int line, const char *fmt, ...); 48 | 49 | #endif -------------------------------------------------------------------------------- /psx/psx.c: -------------------------------------------------------------------------------- 1 | #include "psx.h" 2 | 3 | psx_t* psx_create(void) { 4 | return (psx_t*)malloc(sizeof(psx_t)); 5 | } 6 | 7 | int psx_load_bios(psx_t* psx, const char* path) { 8 | return psx_bios_load(psx->bios, path); 9 | } 10 | 11 | void psx_load_state(psx_t* psx, const char* path) { 12 | log_fatal("State saving/loading is not yet supported"); 13 | 14 | exit(1); 15 | } 16 | 17 | void psx_save_state(psx_t* psx, const char* path) { 18 | log_fatal("State saving/loading is not yet supported"); 19 | 20 | exit(1); 21 | } 22 | 23 | void psx_load_exe(psx_t* psx, const char* path) { 24 | psx_exe_load(psx->cpu, path); 25 | } 26 | 27 | void psx_update(psx_t* psx) { 28 | psx_cpu_cycle(psx->cpu); 29 | 30 | psx_cdrom_update(psx->cdrom, psx->cpu->last_cycles); 31 | psx_gpu_update(psx->gpu, psx->cpu->last_cycles); 32 | psx_pad_update(psx->pad, psx->cpu->last_cycles); 33 | psx_timer_update(psx->timer, psx->cpu->last_cycles); 34 | psx_dma_update(psx->dma, psx->cpu->last_cycles); 35 | } 36 | 37 | void psx_run_frame(psx_t* psx) { 38 | // NTSC: 59.29 Hz, PAL: 49.76 Hz 39 | float framerate = (psx->gpu->display_mode & 0x8) ? 59.29 : 49.76; 40 | 41 | unsigned int counter = (float)PSX_CPU_CPS / framerate; 42 | 43 | while (counter--) { 44 | psx_update(psx); 45 | } 46 | } 47 | 48 | void* psx_get_display_buffer(psx_t* psx) { 49 | return psx_gpu_get_display_buffer(psx->gpu); 50 | } 51 | 52 | void* psx_get_vram(psx_t* psx) { 53 | return psx->gpu->vram; 54 | } 55 | 56 | uint32_t psx_get_display_width(psx_t* psx) { 57 | int width = psx_get_dmode_width(psx); 58 | 59 | if (width == 368) 60 | width = 384; 61 | 62 | return width; 63 | } 64 | 65 | uint32_t psx_get_display_height(psx_t* psx) { 66 | return psx_get_dmode_height(psx); 67 | } 68 | 69 | uint32_t psx_get_display_format(psx_t* psx) { 70 | return (psx->gpu->display_mode >> 4) & 1; 71 | } 72 | 73 | uint32_t psx_get_dmode_width(psx_t* psx) { 74 | static int dmode_hres_table[] = { 75 | 256, 320, 512, 640 76 | }; 77 | 78 | if (psx->gpu->display_mode & 0x40) { 79 | return 368; 80 | } else { 81 | return dmode_hres_table[psx->gpu->display_mode & 0x3]; 82 | } 83 | } 84 | 85 | uint32_t psx_get_dmode_height(psx_t* psx) { 86 | if (psx->gpu->display_mode & 0x4) 87 | return 480; 88 | 89 | int disp = psx->gpu->disp_y2 - psx->gpu->disp_y1; 90 | 91 | if (disp < (255-16)) 92 | return disp; 93 | 94 | return 240; 95 | } 96 | 97 | double psx_get_display_aspect(psx_t* psx) { 98 | double width = psx_get_dmode_width(psx); 99 | double height = psx_get_dmode_height(psx); 100 | 101 | if (height > width) 102 | return 4.0 / 3.0; 103 | 104 | double aspect = width / height; 105 | 106 | if (aspect > (4.0 / 3.0)) 107 | return 4.0 / 3.0; 108 | 109 | return aspect; 110 | } 111 | 112 | void atcons_tx(void* udata, unsigned char c) { 113 | putchar(c); 114 | } 115 | 116 | int psx_init(psx_t* psx, const char* bios_path, const char* exp_path) { 117 | memset(psx, 0, sizeof(psx_t)); 118 | 119 | psx->bios = psx_bios_create(); 120 | psx->ram = psx_ram_create(); 121 | psx->dma = psx_dma_create(); 122 | psx->exp1 = psx_exp1_create(); 123 | psx->exp2 = psx_exp2_create(); 124 | psx->mc1 = psx_mc1_create(); 125 | psx->mc2 = psx_mc2_create(); 126 | psx->mc3 = psx_mc3_create(); 127 | psx->ic = psx_ic_create(); 128 | psx->scratchpad = psx_scratchpad_create(); 129 | psx->gpu = psx_gpu_create(); 130 | psx->spu = psx_spu_create(); 131 | psx->bus = psx_bus_create(); 132 | psx->cpu = psx_cpu_create(); 133 | psx->timer = psx_timer_create(); 134 | psx->cdrom = psx_cdrom_create(); 135 | psx->pad = psx_pad_create(); 136 | psx->mdec = psx_mdec_create(); 137 | 138 | psx_bus_init(psx->bus); 139 | 140 | psx_bus_init_bios(psx->bus, psx->bios); 141 | psx_bus_init_ram(psx->bus, psx->ram); 142 | psx_bus_init_dma(psx->bus, psx->dma); 143 | psx_bus_init_exp1(psx->bus, psx->exp1); 144 | psx_bus_init_exp2(psx->bus, psx->exp2); 145 | psx_bus_init_mc1(psx->bus, psx->mc1); 146 | psx_bus_init_mc2(psx->bus, psx->mc2); 147 | psx_bus_init_mc3(psx->bus, psx->mc3); 148 | psx_bus_init_ic(psx->bus, psx->ic); 149 | psx_bus_init_scratchpad(psx->bus, psx->scratchpad); 150 | psx_bus_init_gpu(psx->bus, psx->gpu); 151 | psx_bus_init_spu(psx->bus, psx->spu); 152 | psx_bus_init_timer(psx->bus, psx->timer); 153 | psx_bus_init_cdrom(psx->bus, psx->cdrom); 154 | psx_bus_init_pad(psx->bus, psx->pad); 155 | psx_bus_init_mdec(psx->bus, psx->mdec); 156 | 157 | // Init devices 158 | psx_bios_init(psx->bios); 159 | 160 | if (psx_bios_load(psx->bios, bios_path)) 161 | return 1; 162 | 163 | psx_mc1_init(psx->mc1); 164 | psx_mc2_init(psx->mc2); 165 | psx_mc3_init(psx->mc3); 166 | psx_ram_init(psx->ram, psx->mc2, RAM_SIZE_2MB); 167 | psx_dma_init(psx->dma, psx->bus, psx->ic); 168 | 169 | if (psx_exp1_init(psx->exp1, psx->mc1, exp_path)) 170 | return 2; 171 | 172 | psx_exp2_init(psx->exp2, atcons_tx, NULL); 173 | psx_ic_init(psx->ic, psx->cpu); 174 | psx_scratchpad_init(psx->scratchpad); 175 | psx_gpu_init(psx->gpu, psx->ic); 176 | psx_spu_init(psx->spu, psx->ic); 177 | psx_timer_init(psx->timer, psx->ic, psx->gpu); 178 | psx_cdrom_init(psx->cdrom, psx->ic); 179 | psx_pad_init(psx->pad, psx->ic); 180 | psx_mdec_init(psx->mdec); 181 | psx_cpu_init(psx->cpu, psx->bus); 182 | 183 | return 0; 184 | } 185 | 186 | int psx_load_expansion(psx_t* psx, const char* path) { 187 | return psx_exp1_init(psx->exp1, psx->mc1, path); 188 | } 189 | 190 | void psx_hard_reset(psx_t* psx) { 191 | log_fatal("Hard reset not yet implemented"); 192 | 193 | exit(1); 194 | } 195 | 196 | void psx_soft_reset(psx_t* psx) { 197 | psx_cpu_init(psx->cpu, psx->bus); 198 | } 199 | 200 | uint32_t* psx_take_screenshot(psx_t* psx) { 201 | log_fatal("Screenshots not yet supported"); 202 | 203 | exit(1); 204 | } 205 | 206 | int psx_swap_disc(psx_t* psx, const char* path) { 207 | psx_cdrom_destroy(psx->cdrom); 208 | 209 | psx->cdrom = psx_cdrom_create(); 210 | 211 | psx_bus_init_cdrom(psx->bus, psx->cdrom); 212 | 213 | psx_cdrom_init(psx->cdrom, psx->ic); 214 | 215 | return psx_cdrom_open(psx->cdrom, path); 216 | } 217 | 218 | void psx_destroy(psx_t* psx) { 219 | psx_cpu_destroy(psx->cpu); 220 | psx_bios_destroy(psx->bios); 221 | psx_bus_destroy(psx->bus); 222 | psx_ram_destroy(psx->ram); 223 | psx_exp1_destroy(psx->exp1); 224 | psx_mc1_destroy(psx->mc1); 225 | psx_mc2_destroy(psx->mc2); 226 | psx_mc3_destroy(psx->mc3); 227 | psx_ic_destroy(psx->ic); 228 | psx_scratchpad_destroy(psx->scratchpad); 229 | psx_gpu_destroy(psx->gpu); 230 | psx_spu_destroy(psx->spu); 231 | psx_timer_destroy(psx->timer); 232 | psx_cdrom_destroy(psx->cdrom); 233 | psx_pad_destroy(psx->pad); 234 | psx_mdec_destroy(psx->mdec); 235 | 236 | free(psx); 237 | } 238 | 239 | psx_bios_t* psx_get_bios(psx_t* psx) { 240 | return psx->bios; 241 | } 242 | 243 | psx_ram_t* psx_get_ram(psx_t* psx) { 244 | return psx->ram; 245 | } 246 | 247 | psx_dma_t* psx_get_dma(psx_t* psx) { 248 | return psx->dma; 249 | } 250 | 251 | psx_exp1_t* psx_get_exp1(psx_t* psx) { 252 | return psx->exp1; 253 | } 254 | 255 | psx_exp2_t* psx_get_exp2(psx_t* psx) { 256 | return psx->exp2; 257 | } 258 | 259 | psx_mc1_t* psx_get_mc1(psx_t* psx) { 260 | return psx->mc1; 261 | } 262 | 263 | psx_mc2_t* psx_get_mc2(psx_t* psx) { 264 | return psx->mc2; 265 | } 266 | 267 | psx_mc3_t* psx_get_mc3(psx_t* psx) { 268 | return psx->mc3; 269 | } 270 | 271 | psx_ic_t* psx_get_ic(psx_t* psx) { 272 | return psx->ic; 273 | } 274 | 275 | psx_scratchpad_t* psx_get_scratchpad(psx_t* psx) { 276 | return psx->scratchpad; 277 | } 278 | 279 | psx_gpu_t* psx_get_gpu(psx_t* psx) { 280 | return psx->gpu; 281 | } 282 | 283 | psx_spu_t* psx_get_spu(psx_t* psx) { 284 | return psx->spu; 285 | } 286 | 287 | psx_bus_t* psx_get_bus(psx_t* psx) { 288 | return psx->bus; 289 | } 290 | 291 | psx_timer_t* psx_get_timer(psx_t* psx) { 292 | return psx->timer; 293 | } 294 | 295 | psx_cdrom_t* psx_get_cdrom(psx_t* psx) { 296 | return psx->cdrom; 297 | } 298 | 299 | psx_pad_t* psx_get_pad(psx_t* psx) { 300 | return psx->pad; 301 | } 302 | 303 | psx_mdec_t* psx_get_mdec(psx_t* psx) { 304 | return psx->mdec; 305 | } 306 | 307 | psx_cpu_t* psx_get_cpu(psx_t* psx) { 308 | return psx->cpu; 309 | } -------------------------------------------------------------------------------- /psx/psx.h: -------------------------------------------------------------------------------- 1 | #ifndef PSX_H 2 | #define PSX_H 3 | 4 | #include "cpu.h" 5 | #include "log.h" 6 | #include "exe.h" 7 | 8 | #include 9 | 10 | #define STR1(m) #m 11 | #define STR(m) STR1(m) 12 | 13 | #define PSXE_VERSION STR(REP_VERSION) 14 | #define PSXE_COMMIT STR(REP_COMMIT_HASH) 15 | #define PSXE_BUILD_OS STR(OS_INFO) 16 | 17 | typedef struct { 18 | psx_bios_t* bios; 19 | psx_ram_t* ram; 20 | psx_dma_t* dma; 21 | psx_exp1_t* exp1; 22 | psx_exp2_t* exp2; 23 | psx_mc1_t* mc1; 24 | psx_mc2_t* mc2; 25 | psx_mc3_t* mc3; 26 | psx_ic_t* ic; 27 | psx_scratchpad_t* scratchpad; 28 | psx_gpu_t* gpu; 29 | psx_spu_t* spu; 30 | psx_bus_t* bus; 31 | psx_cpu_t* cpu; 32 | psx_timer_t* timer; 33 | psx_cdrom_t* cdrom; 34 | psx_pad_t* pad; 35 | psx_mdec_t* mdec; 36 | } psx_t; 37 | 38 | psx_t* psx_create(void); 39 | int psx_init(psx_t*, const char*, const char*); 40 | int psx_load_expansion(psx_t*, const char*); 41 | int psx_load_bios(psx_t*, const char*); 42 | void psx_hard_reset(psx_t*); 43 | void psx_soft_reset(psx_t*); 44 | void psx_load_state(psx_t*, const char*); 45 | void psx_save_state(psx_t*, const char*); 46 | void psx_load_exe(psx_t*, const char*); 47 | void psx_update(psx_t*); 48 | void psx_run_frame(psx_t*); 49 | void* psx_get_display_buffer(psx_t*); 50 | void* psx_get_vram(psx_t*); 51 | uint32_t psx_get_dmode_width(psx_t*); 52 | uint32_t psx_get_dmode_height(psx_t*); 53 | uint32_t psx_get_display_width(psx_t*); 54 | uint32_t psx_get_display_height(psx_t*); 55 | uint32_t psx_get_display_format(psx_t*); 56 | double psx_get_display_aspect(psx_t*); 57 | uint32_t* psx_take_screenshot(psx_t*); 58 | int psx_swap_disc(psx_t*, const char*); 59 | psx_bios_t* psx_get_bios(psx_t*); 60 | psx_ram_t* psx_get_ram(psx_t*); 61 | psx_dma_t* psx_get_dma(psx_t*); 62 | psx_exp1_t* psx_get_exp1(psx_t*); 63 | psx_exp2_t* psx_get_exp2(psx_t*); 64 | psx_mc1_t* psx_get_mc1(psx_t*); 65 | psx_mc2_t* psx_get_mc2(psx_t*); 66 | psx_mc3_t* psx_get_mc3(psx_t*); 67 | psx_ic_t* psx_get_ic(psx_t*); 68 | psx_scratchpad_t* psx_get_scratchpad(psx_t*); 69 | psx_gpu_t* psx_get_gpu(psx_t*); 70 | psx_spu_t* psx_get_spu(psx_t*); 71 | psx_bus_t* psx_get_bus(psx_t*); 72 | psx_timer_t* psx_get_timer(psx_t*); 73 | psx_cdrom_t* psx_get_cdrom(psx_t*); 74 | psx_pad_t* psx_get_pad(psx_t*); 75 | psx_mdec_t* psx_get_mdec(psx_t*); 76 | psx_cpu_t* psx_get_cpu(psx_t*); 77 | void psx_destroy(psx_t*); 78 | 79 | #endif --------------------------------------------------------------------------------