├── .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 | |  |  |  |
8 | |  |  |  |
9 |
10 | ### CI status
11 | 
12 | 
13 | 
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
--------------------------------------------------------------------------------