Giter VIP home page Giter VIP logo

rustation's Introduction

Build Status

This project is no longer being developed.

I decided to restart from scratch to fix some architectural flaws in Rustation. Some of the code has been imported from this project, other bits have been thoroughly rewritten. The new repository is rustation-ng. It's now more advanced than this project, with proper sound support and generally more accurate emulation.

One significant difference between the two projects is than rustation-ng dropped the GL renderer in favor of a threaded software one. The GL renderer of Rustation has been ported to C++ and has been integrated in the Beetle PSX emulator however, so it's not completely dead.

Rustation PlayStation emulator

Rustation logo

PlayStation emulator in the Rust programing language.

This repository only contains the source code for the core of the emulator. The OpenGL renderer and the libretro interface is is the rustation-libretro repository.

The focus of this emulator is to write code that's clean, accurate and hopefully easy to understand. There's no plugin infrastructure, the emulator is monolithic.

Performance is pretty poor at the moment but it should be enough to run close to realtime on a modern CPU.

For the time being it can only boot a few games. Crash Bandicoot (Japanese version) is mostly playable, although I've had random crashes. Some other games (like Spyro) freeze after or during the intro.

If you have any questions, in particular if something in the code is not clear or properly commented don't hesitate to fill an issue.

I also created a /psx/ board on 8chan if you prefer something less formal to discuss this emulator and all things PlayStation. We'll see if this turns out to be a good idea...

Currently implemented (even partially)

Crash Bandicoot (Japan)

  • CPU
  • Basic GTE support (ported from mednafen PSX)
  • Instruction cache
  • Interrupts
  • Basic GPU (no semi-transparency or mask bit emulation)
  • Timers (incomplete)
  • DMA
  • Debugger
  • CDROM controller (missing many commands)
  • Gamepad controller (only digital pad for now)

Todo list

  • Many things in the GPU
  • MDEC
  • SPU
  • Memory card
  • CPU pipeline emulation
  • More accurate timings
  • Many, many other things...

Build

You'll need Rust and its package manager Cargo, SDL2 and a PlayStation BIOS. The emulator is mainly tested with BIOS version SCPH1001 whose SHA-1 is 10155d8d6e6e832d6ea66db9bc098321fb5e8ebf.

You should then be able to build the emulator with:

cargo build --release

Don't forget the --release flag in order to turn optimizations on. Without them the resulting binary will be absurdly slow.

If the build is succesful you can run the emulator using:

cargo run --release /path/to/SCPH1001.BIN

For Windows check issue #12.

Use the Escape key to exit the emulator, Pause/Break to "break" into the debugger, the emulator will then listen on TCP port 9001 for a GDB connection.

Debugger

In order to debug you'll need a GDB targetting mipsel-unknown-elf. Once the emulator is running press the Pause/Break key to trigger the debugger and then connect GDB to it using (at the gdb command prompt):

target remote localhost:9001

GDB might complain about not finding symbols or the boundaries of the current function but you can ignore that. From then you should be able to use the familiar GDB commands to debug the live emulator.

A few examples:

# Dump the CPU registers
info registers
# Disassemble 20 instructions around PC
disassemble $pc-40,+80
# Display word at address 0x1f801814 (GPU status)
x/x 0x1f801814
# Add code breakpoint at address 0x00004588
break *0x00004588
# Add write watchpoint at address 0x1f801070 (IRQ ack)
watch *0x1f801070
# Step over a single instruction
stepi
# Continue until a break/watchpoint is reached (or Pause/Break is pressed)
continue

The debugger support is pretty experimental and quircky but it works for basic debugging needs.

Guide

I'm also attempting to document the emulator writing process in a LaTeX document available in the psx-guide repository. It's generally lagging behind the actual code but I'll try to update it as often as possible.

Resources

I try to cite all of my sources in the guide above but I'm mainly using the priceless No$ PSX specifications as well as mednafen's source code when I feel like cheating.

I also run tests on the real hardware and store them in the psx-hardware-tests repository.

rustation's People

Contributors

simias avatar yamakaky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rustation's Issues

Replace RustcEncodable with Serde for savestate serialization

At the time the savestate architecture was first implemented Serde was not yet stable. Since then it's become the de-facto serialization library for Rust, superseeding the hacky RustcEncodable/RustcEncodable.

While there's no emergency it'd be nice to move to Serde now that it's stabilized. It shouldn't be too difficult but it's quite a big change which impacts both rustation and rustation-libretro. In particular the current binary serializer would have to be ported (or rewritten). I'm not sure how straightforward that would be.

Emulate GTE register access latency

There appears to be a 2 cycle latency when storing a value to a GTE register with the ctc2 instruction. For instance the following code puts 0x456 (the value of $t1) in $a1 even though there are two other ctc2 instructions in between:

    li    $t0, 0x123
    li    $t1, 0x456
    li    $t2, 0x789
    li    $t3, 0xabc

    ctc2  $t0, $26
    ctc2  $t1, $26
    ctc2  $t2, $26
    ctc2  $t3, $26

    cfc2  $a1, $26

I have to see if this also concerns the GTE command: what happens if you run an instruction immediately after setting a register?

Subchannel data reading and CD copy protection Libcrypt.

Many PSX emulators/CD plugins including Mednafen have problems with game protection in some games and require real discs will this be a problem with this emulator too? Will this emulator be able to decrypt Libcrypt-protected ISOs?

http://www.razyboard.com/system/morethread-subchannel-woes-pete_bernert-41714-382429-0.html
http://www.razyboard.com/system/morethread-ff9-black-screen-pete_bernert-41709-1031440-0.html
http://www.razyboard.com/system/morethread-sub-channel-reading-problem-pete_bernert-41714-176709-0.html
http://www.razyboard.com/system/morethread-cdr-pluging-dosenund039t-read-drives-pete_bernert-41714-1398147-0.html
http://forums.pcsx2.net/Thread-Why-isn-t-there-Copy-Protection
http://psx-scene.com/forums/f20/ps2-disc-security-explanations-50245/
http://psx-scene.com/forums/f13/ps2-copy-protection-15762/
http://everything2.com/title/Playstation+copy+protection
http://psx-scene.com/forums/f10/subchannel-data-102099/
http://www.tomsguide.com/forum/89-4-what-copy-protection
http://consolecopyworld.com/psx/psx_protected_games.shtml
http://consolecopyworld.com/psx/psx_libcrypt_tutorial.shtml
http://snesorama.us/board/showthread.php?40865-How-to-Play-(libcrypt)-a.k.a.-protected-ps1-games.-Playing-multiple-disc-backups.
https://forum.slysoft.com/threads/ps1-psone-backups.25000/
http://forum.fobby.net/index.php?t=msg&goto=2914&
http://forum.fobby.net/index.php?t=msg&goto=3759&

Another nice option is automatic unpacking of ECM compressed ISOs OSX and Linux versions of PCSXR have it so probably it can be copied from it.

Waifu2x in real time

Hello

I was told to check with you guys about my request too.
I don't know if you are familiar with the Waifu2x upscaler and filter. It's both web-based and in program form.
You can grab the program here

https://translate.googleusercontent.com/translate_c?depth=1&hl=en&prev=search&rurl=translate.google.se&sl=ja&u=https://github.com/lltcggie/waifu2x-caffe/releases&usg=ALkJrhivu32FwUwuPxodRAFRTS4bRFPimA

And here is a GLSL impementation.

https://github.com/ueshita/waifu2x-converter-glsl

My request is originaly to have this kind of upscaler and filter mainly for PS1 emulation since those games were rendered in really, really low res.
Here is some examples I have done by upscaling original backgrounds from FFVII and FFIX.

FFVII
http://i.imgur.com/X27lVJ5.jpg

FFVII
http://i.imgur.com/fYwxIBY.png

FFIX
http://i.imgur.com/zR1kjlE.jpg

FFVII
http://i.imgur.com/CzRczDb.jpg

And here's another batch of FFVII pictures
http://imgur.com/a/zzdBm

Well, as you can see this is probably the best option for making these games look as "hd" as possible. And it do looks really nice.

So, is there any possibility that we sometime can have a Waifu2x filter for realtime gaming?
Please
/Mike

Visual GPU debugger

This a project I've been thinking about for a while but I have so much stuff on my todo list at the moment that I can't imagine tackling it anytime soon. I would like an external tool that could take PlayStation GPU command logs and lets me see what individual commands draw. I'd also like to be able to navigate the commands, go back in time etc... That would be a huge time saver when tracking down certain graphical bugs.

I'm posting this here in case somebody else wants to give it a try, it's a relatively simple, well bounded and, in my opinion, fun project for somebody who likes making user interfaces and wants to try their hands at emulation. It's not super performance-critical either so you can really use the language and environment of your choice (although portability is strongly encouraged...).

This tool would be useful in the development of Rustation but it need not be tied to it and could be used for other emulators or even a real console as long as you're able to dump incoming GPU command words.

Context

From a high level perspective the PlayStation GPU is fairly simple. It has 1Megabyte of VRAM laid out as a 1024x512 16bits-per-pixel video buffer. The GPU draws lines, triangles and quadrilaterals to this buffer and then sends a portion of it to the video output.

In order to control it the GPU has two 32bit registers called GP0 and GP1. The CPU and/or DMA sends command words through those registers and the GPU executes them.

Here's the GP0 decoding function in rustation https://github.com/simias/rustation/blob/master/src/gpu/mod.rs#L592-L659

And here's GP1's: https://github.com/simias/rustation/blob/master/src/gpu/mod.rs#L1220-L1252

No$ is also a good source for understanding the GPU and its commands: http://problemkaputt.de/psx-spx.htm#graphicsprocessingunitgpu

You can see that GP0 commands are mainly draw commands like "draw a triangle at these coordinates", "draw a line at these coordinates", "fill this rectangle in VRAM with this color" etc... It's relatively straightforward.

GP1 on the other hand is more for configuration, things like turning the video output on or off or configuring which part of the VRAM is displayed on the screen.

The problem

My current problem is that linking a visual glitch on the output with an actual individual GPU command in the emulator can be very tedious and time consuming without any kind of tool to assist me.

For instance here's the complete log of GP0 and GP1 commands during the first few seconds after boot.

That's 19162 individual command words making up 2756 GP0 and 2282 GP1 commands. And all it does is draw the little animation of the "Sony Computer Entertainment" logo on boot.

For instance if you dig into the log you'll find the following sequence:

GP0 0x300000b2
GP0 0x00f101bd
GP0 0xca008cb2
GP0 0x00740140
GP0 0xca008cb2
GP0 0x016e0140

This draws one of the shaded triangles. Took me a little while to extract it out. The general form of this command is:

GP0 0x30BBGGRR <- 0x30 is the command opcode, followed by the 24bit RGB color of the 1st vertex
GP0 0xYYYYXXXX <- 1st vertex X and Y coordinates
GP0 0xXXBBGGRR <- 2nd vertex 24bit RGB color (high byte is ignored)
GP0 0xYYYYXXXX <- 1st vertex X and Y coordinates
GP0 0xXXBBGGRR <- 3rd vertex 24bit RGB color (high byte is ignored)
GP0 0xYYYYXXXX <- 3rd vertex X and Y coordinates

Now you can go back to the previous command and manually decode it if you want. As you can see this is pretty time consuming.

Now here's an actual bug I'm trying to fix right now:

retroarch-0524-013225

Crash's shadow on the ground is made up of two triangles, and you can see that one of them has the wrong transparency applied.

In order to fix this I'd like to be able to track the individual draw command responsible for this particular triangle and figure out why I don't interpreted it correctly. Isolating a single command from the thousands of calls making up each Crash Bandicoot frame is very tedious. Parsing the log I posted above is already non-trivial and it's only drawing a dozen triangles in a simple animation.

What I'd like is a visual tool that would take this command stream and show me what they do directly on the screen. No$ seems to have something like that already:

psxdebug

You can see that the "Vram Viewer" window has a list of commands at the upper right and seems to highlight the selected quadrilateral in the main view. Using a tool like this I could track down the bogus command in my Crash Bandicoot screenshot in seconds. This looks like a great tool but unfortunately it's closed source as far as I know.

So if somebody wants to give this a try or wants more precisions don't hesitate to ask for my help.

Low level emulation of the CD controller

As far as I know nobody has attempted to LLE the PlayStation CD controller. It would be quite performance intensive and an HLE implementation is probably good enough 99.9% of the time but if somebody wants to give it a try it could be interesting for accuracy's sake.

No$ managed to dump some of the ROMs of the 8bit controller: http://www.psxdev.net/forum/viewtopic.php?f=70&t=557

He also has many details about how this controller works in his spec. That could be a starting point.

bin target location ??

Do you run from the rustation dir, or do you have to change to another dir?

eli@hp-pav ~/ROMS/rustation $ cargo build --release
  Compiling rustation v0.0.3 (file:///home/eli/ROMS/rustation)
    Finished release [optimized] target(s) in 26.17 secs

eli@hp-pav ~/ROMS/rustation $ cargo run --release /home/eli/ROMS/BIOS/PSX/SCPH-1001.BIN
error: a bin target must be available for `cargo run`

eli@hp-pav ~/ROMS/rustation $ cd target/release
eli@hp-pav ~/ROMS/rustation/target/release $ cargo run /home/eli/ROMS/BIOS/PSX/SCPH-1001.BIN
error: a bin target must be available for `cargo run`

Use double or triple buffering to make the renderer non-blocking

See the discussion in #15, in particular tomaka's comments at the end.

Currently since we only have a single-buffered vertex buffer the emulator stops processing while a frame is being rendered. By multi-buffering it we could start processing the next frame while the current one in being rendered.

panic on startup on OS X Yosemite when loading BindBuffersBase

thread '<main>' panicked at 'called `Option::unwrap()` on a `None` value', /private/tmp/rust20150626-13301-uiklsd/rustc-1.1.0/src/libcore/option.rs:362
stack backtrace:
   1:        0x102f05e1f - sys::backtrace::write::h4d34c75aaa44ebb3c2r
   2:        0x102f09584 - panicking::on_panic::hacede89293da7355Lgw
   3:        0x102efcc35 - rt::unwind::begin_unwind_inner::h5f5b5041038f2bdbuYv
   4:        0x102efcfdc - rt::unwind::begin_unwind_fmt::hf422928da74ad1a4AXv
   5:        0x102f08e3c - rust_begin_unwind
   6:        0x102f27405 - panicking::panic_fmt::h0ece6850f56551b8f3B
   7:        0x102f26b64 - panicking::panic::h8e0828861061ce45M1B
   8:        0x102ed6278 - option::Option<T>::unwrap::h7340955155689965868
   9:        0x102e56e58 - gpu::opengl::Renderer::new::closure.12112
  10:        0x102e58e9e - load_with::closure.12217
  11:        0x102e58cf4 - metaloadfn::h7713124497418738431
  12:        0x102e58c3d - BindBuffersBase::load_with::h18178951034605948107
  13:        0x102e526a3 - load_with::h2438887132135737744
  14:        0x102e517d0 - gpu::opengl::Renderer::new::h7c8e68b4fea3e7c0pnd
  15:        0x102ee78eb - main::h3e82e9ed9324e15bN2e
  16:        0x102f0a6a8 - rust_try_inner
  17:        0x102f0a695 - rust_try
  18:        0x102f09d90 - rt::lang_start::h204325cf25bd6ae3gbw
  19:        0x102eeb5ee - main

I'm not sure why BindBuffersBase would have an issue?

Old version compatible with Guide?

Sorry for writing to inactive project, but maybe someone have old version of this code, compatible with Guide.pdf that explains process of writing this emulator? I'm currently trying to implement it in c++ and have small bug, comparing my project with old v of this one would sure help.

Issues building libretro core on Debian 8 amd64

  Compiling gl v0.5.2
   Compiling rustation-retro v0.1.0 (file:///tmp/buildd/libretro-rustation-0.0.0+git20161116.562d367~1)
src/libretro.rs:785:33: 785:56 error: unresolved name `panic::AssertUnwindSafe` [E0425]
src/libretro.rs:785     let r = panic::catch_unwind(panic::AssertUnwindSafe(|| {
                                                    ^~~~~~~~~~~~~~~~~~~~~~~
src/libretro.rs:785:33: 785:56 help: run `rustc --explain E0425` to see a detailed explanation
src/libretro.rs:785:13: 785:32 error: unresolved name `panic::catch_unwind` [E0425]
src/libretro.rs:785     let r = panic::catch_unwind(panic::AssertUnwindSafe(|| {
                                ^~~~~~~~~~~~~~~~~~~
src/libretro.rs:785:13: 785:32 help: run `rustc --explain E0425` to see a detailed explanation
src/libretro.rs:795:8: 795:18 error: the type of this value must be known in this context
src/libretro.rs:795     if r.is_err() {
                           ^~~~~~~~~~
error: aborting due to previous error
Could not compile `rustation-retro`.

To learn more, run the command again with --verbose.
debian/rules:14: recipe for target 'override_dh_auto_build' failed

unhelpful error message when rom is missing

thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: Error { repr: Os(2) }', /private/tmp/rust20150626-13301-uiklsd/rustc-1.1.0/src/libcore/result.rs:729

would be nice if this were user-friendly

Vec<u8> -> [u8]

As the ram and bios have fixed size, did you consider using arrays instead of Vec ?

You could use into_boxed_slice, or use directly a [u8] for reading the file.

I was thinking...

I have the Psyq PSX(PS1) SDK installed would those C / C++ headers be useful to help implementing some things in Rust from the actual Playstation?

I even found the headers that contains information about things like loading / saving to / from memory cards. It could be helpful.

Also I wonder how quicksaves was done in emulators like ePSXe. I always wanted an Open source PSX emulator though.

(funny how the games on the PSX was created using C and/or C++ (Although not modern ones))

C:\psyq\include\KERNEL.H

#ifndef _KERNEL_H
#define _KERNEL_H

/*
 * File:kernel.h 	Rev. 3
*/
/*
 * $PSLibId: Run-time Library Release 4.6$
 */
#ifndef _R3000_H
#include <r3000.h>
#endif

#ifndef _ASM_H
#include <asm.h>
#endif

/* don't change these macros and structures which is refereced in kernel code */ 

#define DescMask 	0xff000000
#define DescTH 		DescMask
#define DescHW   	0xf0000000
#define DescEV   	0xf1000000
#define DescRC   	0xf2000000
#define DescUEV   	0xf3000000	/* User event */
#define DescSW   	0xf4000000	/* BIOS */

#define HwVBLANK	(DescHW|0x01)	/* VBLANK */
#define HwGPU		(DescHW|0x02)	/* GPU */
#define HwCdRom		(DescHW|0x03)	/* CDROM Decorder */
#define HwDMAC		(DescHW|0x04)	/* DMA controller */
#define HwRTC0		(DescHW|0x05)	/* RTC0 */
#define HwRTC1		(DescHW|0x06)	/* RTC1 */
#define HwRTC2		(DescHW|0x07)	/* RTC2 */
#define HwCNTL		(DescHW|0x08)	/* Controller */
#define HwSPU		(DescHW|0x09)	/* SPU */
#define HwPIO		(DescHW|0x0a)	/* PIO */
#define HwSIO		(DescHW|0x0b)	/* SIO */

#define HwCPU		(DescHW|0x10)	/* Exception */
#define HwCARD		(DescHW|0x11)	/* memory card */
#define HwCARD_0	(DescHW|0x12)	/* memory card */
#define HwCARD_1	(DescHW|0x13)	/* memory card */
#define SwCARD		(DescSW|0x01)	/* memory card */
#define SwMATH          (DescSW|0x02)	/* libmath */

#define RCntCNT0     	(DescRC|0x00)  	/* display pixel */
#define RCntCNT1  	(DescRC|0x01)  	/* horizontal sync */
#define RCntCNT2  	(DescRC|0x02)  	/* one-eighth of system clock */
#define RCntCNT3  	(DescRC|0x03)  	/* vertical sync target value fixed to 1 */

#define RCntMdINTR	0x1000
#define RCntMdNOINTR	0x2000
#define RCntMdSC	0x0001
#define RCntMdSP	0x0000
#define RCntMdFR	0x0000
#define RCntMdGATE	0x0010

#define EvSpCZ		0x0001		/* counter becomes zero */
#define EvSpINT		0x0002		/* interrupted */
#define EvSpIOE		0x0004		/* end of i/o */
#define EvSpCLOSE	0x0008		/* file was closed */
#define EvSpACK		0x0010		/* command acknowledged */
#define EvSpCOMP	0x0020		/* command completed */
#define EvSpDR		0x0040		/* data ready */
#define EvSpDE		0x0080		/* data end */
#define EvSpTIMOUT      0x0100          /* time out */
#define EvSpUNKNOWN     0x0200          /* unknown command */
#define EvSpIOER	0x0400		/* end of read buffer */
#define EvSpIOEW	0x0800		/* end of write buffer */
#define EvSpTRAP       	0x1000          /* general interrupt */
#define EvSpNEW		0x2000		/* new device */
#define EvSpSYSCALL	0x4000		/* system call instruction */
#define EvSpERROR	0x8000		/* error happned */
#define EvSpPERROR	0x8001		/* previous write error happned */
#define EvSpEDOM        0x0301		/* domain error in libmath */
#define EvSpERANGE      0x0302		/* range error in libmath */

#define EvMdINTR	0x1000
#define EvMdNOINTR	0x2000

#define EvStUNUSED    	0x0000
#define EvStWAIT    	0x1000
#define EvStACTIVE    	0x2000
#define EvStALREADY   	0x4000

#define TcbMdRT		0x1000		/* reserved by system */
#define TcbMdPRI	0x2000		/* reserved by system */

#define TcbStUNUSED	0x1000
#define	TcbStACTIVE	0x4000

#if defined(_LANGUAGE_C)||defined(LANGUAGE_C)||defined(_LANGUAGE_C_PLUS_PLUS)||defined(__cplusplus)||defined(c_plusplus)
struct ToT {
	unsigned long *head;
	long size;
};

struct TCBH {
	struct TCB *entry;	/* NULL */
	long flag;
};

struct TCB {
	long status;
	long mode;
	unsigned long reg[NREGS];	/* never change the offset of this */
	long system[6];			/* reserved by system */
};

struct EvCB {
	unsigned long desc;	
	long status;
	long spec;
	long mode;
	long (*FHandler)();
	long system[2];			/* reserved by system */
};


struct EXEC {                   
        unsigned long pc0;      
        unsigned long gp0;      
        unsigned long t_addr;   
        unsigned long t_size;   
        unsigned long d_addr;   
        unsigned long d_size;   
        unsigned long b_addr;   
        unsigned long b_size;   
	unsigned long s_addr;
	unsigned long s_size;
	unsigned long sp,fp,gp,ret,base;
};


struct XF_HDR {
	char key[8];
	unsigned long text;
	unsigned long data;
	struct EXEC exec;
	char title[60];		/* "PlayStation(tm) Executable A1" */
};


struct DIRENTRY {
	char name[20];
	long attr;
	long size;
	struct DIRENTRY *next;
	long head;
	char system[4];
};


extern struct ToT SysToT[32];

extern long SysClearRCnt[];

#ifndef NULL
#define NULL (0)
#endif

#if defined(_LANGUAGE_C)||defined(LANGUAGE_C)
#define delete  erase
#endif /* LANGUAGE_C */

#endif /* LANGUAGE_C||_LANGUAGE_C_PLUS_PLUS||__cplusplus||c_plusplus */

#endif /* _KERNEL_H */

But yeah maybe the psyq can actually help finish the emulator up to become as good as the original Playstation.

Is there any way to recognize when a game needs to change cd?

I already tried to ask about this on other emulators and the response i generally got was negative, that the games didn't actually have a bios command for cd change or waiting for new cd, so i don't expect anything different just confirmation.

I was hoping of it being used to auto change cds on games with more than one. Originally i though the psx had a sensible design of a bios barrier function waiting for the game ID and only when the cd was ready verify it and then the dev could read files - the emulator could intercept the game id then load the appropriate cd - then i lowered my expectations and asked if it had any call for 'expect cd change' (as a hint to put in CD++ ).

But as said, this issue is just to ask confirmation that this 'auto'/no user intervention cd changing is impossible.

HLE Bios

Will rustation have HLE bios like PCSXR.

Optimization: draw polygons front to back in the GPU when possible

Since the PSX doesn't have a z-buffer all the primitives are drawn from farthest to closest. This means massive amounts of overdraw as many polygons are drawn only to be hidden by a closer one later on.

This is basically the worst case as far as the OpenGL renderer is concerned, generally we'd prefer to render from front to back and ignore hidden pixels instead invoking our shader millions of time for fragments that will never make it to the screen.

Implementation

This shouldn't be too difficult to implement: when we receive primitives in the GPU we could store them in the vertex buffers in reverse order. We'd also need a way for the GPU to test which parts of the screen have already been drawn, I can think of two simple ways to do that:

  • The Z-buffer: we can assign arbitrarily increasing values to the z-coordinate of the primitive (since we don't have the real Z-coordinate available at this point) and then enable the depth test in OpenGL. Not very elegant but it should work. One advantage of this method is that if we decide to implement a real Z-buffer later on (by recovering depth information from the GTE) we might be able to reuse this code directly by plugging the actual depth value instead of a dummy value. It's a bit early to say for sure though.
  • The stencil: probably cleaner, at least from a conceptual standpoint: we enable the stencil test and we set the stencil flag after each draw. The stencil test will reject subsequent draws to the same pixel in the framebuffer. One potential difficulty is that we'll also probably need the stencil test to emulate the mask bit so we'll have to make sure we can make the two modes cohabit. Maybe we can use two stencil buffers? Or a single stencil with two bits per pixels? I'm not sure...

Caveats

There are a few limitations to this optimization however: semi-transparent polys will still have to be drawn back to front after all the opaque primitives for tranparency to work as expected. We'll probably need two passes, one for the opaque polys front-to-back and then one for the semi-transparent polys back-to-front. Maybe we could store the opaque vertex starting from the end of the vertex buffer and then decrementing while transparent primitives would be stored at the beginning and incrementing as it's currently done. Or we could use two buffers, not sure which is best.

Also we can't use this optimization when the "test mask bit" and "set mask bit" options are enabled since the order of the draws become significant for the end result regardless of the "depth" of the polygon. If it's the case we need to render everything in "native" order.

Specification of the OpenGL renderer architecture

Overview of the PlayStation GPU

GPU Rasterizer

The GPU uses 1 megabyte of video RAM organized as a framebuffer of 512 lines of 2048 bytes. The CPU can upload textures to this buffer using GPU commands (it's not directly memory mapped in the CPU address space), it can also read back portions of the framebuffer using other commands.

The GPU also contains a relatively simple 2D rasterizer capable of drawing lines and triangles (and "quads" which are really just two triangles side-by-side). It supports solid colors, textures (truecolor and paletted), several transparency modes and gouraud shading. It can also apply a dithering pattern before outputing the 16bit color. The GPU has a small texture cache to speed up rendering of textured primitives.

The rasterizer always outputs 16bits per pixel (1555 RGB, where the MSB is the "mask" bit) so as far as it's concerned the VRAM is a framebuffer of 1024x512 pixels. Therefore all the draw commands use this system of coordinates.

Note that the GPU is fully 2D, it's not capable of 3D projection and therefore has no notion of depth (so no depth buffer or anything like that). The PlayStation does 3D projection on the CPU using the Geometry Transform Engine (GTE) coprocessor. That means for instance that the GPU cannot do perspective-correct texture mapping which is the source of some easily recognizeable PlayStation graphical artifacts.

The coordinate system used by the GPU is simply the 16bit per pixel coordinate in the video RAM, so (0, 0) is the top-left of the framebuffer while (1023, 511) is the bottom right. You can see a list of the GPU draw commands in the No$ specs.

GPU video output

Once a scene has been rendered/uploaded to the framebuffer it needs to be displayed on the TV through the NTSC/PAL analog video output. In order to do this the GPU video output can be configured to select a rectangle in the framebuffer and stream it to the TV.

The size of this window depends on the video timings used. For NTSC it ranges from roughly 256x240 to 640x480 while for PAL it's from 256x288 to 640x576. I say roughly because since it's an analog output you can tweak the timings in many ways so you can actually "overscan" the output to increase the resolution or crop it furthermore depending on what you're trying to do.

Interestingly even though the rasterizer only outputs 16bits per pixel the video output can be configured to use 24bits per pixel. That's of course mostly useless for graphics generated by the rasterizer but it can be used to display pre-rendered 24bit images, for instance videos decoded with the console's MDEC and uploaded on the GPU VRAM. An other application could be 24bit images dumped directly from the disc and used as static load screens.

Design of the emulated OpenGL renderer

Features

First and foremost I think accuracy should be the main focus. Of course if it was the only objective a software renderer would be better suited but I think with modern OpenGL and its programable pipeline it should be possible to reach a decent level of accuracy (except maybe for the GPU cache, see below).

OpenGL would also make it easier to implement certain enhancements to the game's graphics compared to the original console, for instance increased internal resolution, texture replacement, normal maps etc...

Later on we could even attempt to salvage the raw 3D coordinates from the GTE and use them to render the 3D scene directly with OpenGL. That would allow us to have higher precision for our vertex coordinates, perspective correct mapping and many other things only possible with a fully 3D scene.

I think it's important to keep those features in mind when designed the basic renderer architecture so that we don't end up breaking everything when we try to implement one of them.

Potential difficulties

As you can see from the previous sections the PlayStation GPU is extremely simple compared to a modern graphic card, however it features some quirks and "exotic" modes which don't fit the OpenGL pipeline very well as far as I can tell.

Textures and palettes

At first I thought the most obvious approach to emulate the GPU video memory would be to use a single 1024x512 texture/FBO (or bigger if we increase the internal resolution) and use it as our Video RAM. There are a few potential issues with that approach however.

Upscaling and filtering

When a game wants to upload textures and palettes to the video RAM it must use one of the "Copy Rectangle" commands saying where the data should end up in the framebuffer (always using the same 1024x512 coordinate system) and then send the data 32bits at a time.

At this point there's no easy way to know what the data contains, it can be a 24bit RGB image from a video, it could be a 16bit "truecolor" texture, it can be a paletted texture, it can be a palette or several of those things at once. We'll only know how to interpret the data when the GPU actually uses it, either through a textured draw command or by the video output configuration if it's just meant to be dumped on the TV screen without further processing.

This seriously limits what we can do with the raw framebuffer data if we don't want to break anything.

For instance if we have a single big FBO representing the entire framebuffer at an increased resolution (say, 2048x1024). When the CPU attempts to upload data to the GPU we could upscale and optionally filter it and store it in our big buffer. Easy.

Except of course upscaling and filtering palettes and paletted textures won't work as intended, the intermediate pixel values will be meaningless since we basically have a non-linear color space. We cannot risk destroying palettes, therefore we can't really mess with the uploaded data until we know what it's going to be used for. Or at least whatever we do must be reversible if we need to go back to the original value later on.

I'm not sure what's the best way to deal with this. Maybe we could have two framebuffers instead: one at the native 1024x512 resolution containing the raw framebuffer data with no fancy enhancements that would be used for paletted textures and a bigger framebuffer containing the rasterizer's output at increased resolution. Keeping the two coherent will be a challenge however and I don't know where 24bit images fit in there. Maybe we could use a completely different rendering mode when the video output is set to 24bit mode so that we can ignore it the rest of the time.

If we want to implement texture replacement we also need to figure out when it should take place. That's a complex subject however, maybe we can leave it for later.

OpenGL texture sampling

An other potential issue if we use a single texture/FBO for the entire video RAM is that we need to be able to render into it while we sample a texture in an other location in the same buffer. So we would be rendering to an FBO while it's also bound as a texture.

As far as I know this kind of configuration is not well supported by OpenGL and can quickly lead us into undefined behavirour territory.

I believe that this should be achievable using the GL_ARB_texture_barrier extension which is part of OpenGL 4.5 but maybe we can work around it.

Otherwise we could maybe use two framebuffers and "Ping pong" between the two between each frame instead, this way we would write to the current FBO while we use the previous one for input. That could be innacurate if a game decide to use a polygon rendered during the current frame to texture a subsequent one, I know some games use similar features to create some fancy visual effects.

Semi-transparency

The PlayStation GPU rasterizer has several pixel blending modes used for semi-transparent primitives (copied from the No$ specs):

  B=Back  (the old pixel read from the image in the frame buffer)
  F=Front (the new halftransparent pixel)
  * 0.5 x B + 0.5 x F    ;aka B/2+F/2
  * 1.0 x B + 1.0 x F    ;aka B+F
  * 1.0 x B - 1.0 x F    ;aka B-F
  * 1.0 x B +0.25 x F    ;aka B+F/4

Unfortunately I don't think the OpenGL blending fixed function is flexible enough to accomadate all these modes without a significant number of hacks. Besides for accuracy's sake we might want to handle the blending calculations in our own shader code to make sure we don't have weird rounding and saturation discrepancies (if we want to be bit accurate with the real hardware).

For this reason I think it would be better to handle the blending in the fragment shader. Once again this is not generally how things are done in OpenGL as far as I know, but it should be possible at least using the same OpenGL 4.5 GL_ARB_texture_barrier extension mentioned before or by "ping ponging" the buffers.

Masking

The MSB of the 16bit pixel is used to store a "mask" field. When the GPU renders a primitive it can be configured to set this bit to either zero or one. An other configuration flag can tell the GPU to treat framebuffer pixels with the mask bit set as "read only" and refuse to overwrite them. It effectively works like a simplified stencil test in OpenGL.

The problem if we decide to use a stencil buffer to emulate this masking feature is that we'd potentially need to update the stencil buffer after each primitive, since it's possible for any primitive draw to set the mask bit if the GPU is configured to do so. I don't know if it's possible to meaningfully modify the stencil buffer in an OpenGL fragment shader. Barring that we won't be able to use the stencil test to accurately emulate the masking.

Alternatively we could use the same trick I proposed to handle the semitransparency modes above: we fetch the target FBO pixel in the fragment shader and if the masking is enabled and its MSB is set we don't change its value. If we already handle transparency that way it might be relatively straightforward to add, theoretically.

Video output

So far I only talked about rending things inside the framebuffer, we also have to implement the video output to display the relevant part of the framebuffer on the screen.

The PlayStation video output streams the pixels from the framebuffer directly on the screen without any kind of intermediate buffer. In other words the display is "continuous", if a game wants to implement double buffering it does it by rendering to two different parts of the framebuffer and swapping the video output source position and GPU rasterizer draw offset when it wants to "flip" the buffers.

In order to emulate this properly we'd have to basically render one pixel at a time, keeping track of the output video pixel clock. I don't think that can be done very efficiently in OpenGL, nor does it sound necessary to emulate correctly the vast majority of games.

Instead we could display the entire visible portion of the framebuffer at once at the end of every frame or the beginning of the next one maybe. If the game uses double buffering we want to it right after it swaps its buffers to reduce latency. I'm guessing the swapping would take generally take place during the vertical blanking to reduce tearing artifacts, so maybe we could do the frame rendering at the end of the vertical blanking. I need to do more tests to make sure that's really how it works in practice though.

24bit mode

As explained before while the PlayStation rasterizer can only output 16bit RGB to the framebuffer the video output is capable of treating it as 24bits per pixel to display pre-rendered graphics.

When displaying in 24bit mode we'll have to find a way to take our 16bit RGB framebuffer and display it as a 24bit image. I don't know how difficult that is with OpenGL. I guess at worse we could sample two 16bit pixels in the fragment shader and reconstitute the correct 24bit value there. We could also implement 24bit image upscaling and filtering there to avoid having to handle it in the 16bit code. After all 24bit mode is a very limited special case on the original hardware.

Texture cache

The PlayStation GPU has a small texture cache used to speed up the rendering of textured primitives. Normally it only affects the speed at which a primitive is rendered, however if the cache becomes "dirty" (for instance by overwriting a texture while some of it is in the cache) it could potentially change the aspect of the resulting triangle.

I have no idea how to emulate this particular feature in OpenGL. As far as I can tell the only way to emulate it accurately would be to draw each primitive pixel-by-pixel, updating the cache state in the process but that goes against the spirit of the massively parallel GPUs we have today.

Fortunately I believe that for the vast majority of the games we can completely ignore this cache, so maybe we can ignore it or at least put it very low in our priority list.

Vulkan API support

Hex Editor, ASCII editor, cheats, memory card editor, assembler, disassembler, memory hacks, memory dumping and restoring.

I think these features would be very useful for cheats, translations, game hacking and memory hacks.
Plugin-based PSX emulators have a plugin called PSX Emulation Cheater(PEC) for it. I believe PSX/PSXFin has built-in Hex editor.

http://ngemu.com/threads/playstation-emulator-cheater-setup-guide.101186/
http://pec.duttke.de/
http://www.aldostools.org/pecedit.html
https://github.com/strobejb/HexEdit
https://github.com/evanmiller/hecate
http://www.zophar.net/utilities/hexutil.html
http://homepage.ntlworld.com/simon.mallion/PSXMemTool/

Windows support

Make sure the emulator works on Windows, write a guide on how to build it there.

OpenGL ES 2.0 (and GLSL ES) support

Any plans for Rustation (and rustation-libretro) renderer to support OpenGL ES 2.0 graphics rendering pipeline (and the GLSL ES shading language?)?

OpenGL ES (OpenGL for Embedded System) instead of the full OpenGL architecture is used by the hardware GPU on most embedded computers and mobile devices these days, including popular single-board-computers such as the Raspberry Pi (2) as well as most mobile phones, tablets, and Linux based Android based game consoles or media players / streamers.

http://en.wikipedia.org/wiki/OpenGL_ES

Most comming support version being the OpenGL ES 2.0 specification which was the first to feature GLSL ES 1.0 (GLSL ES shading language) support and is roughly based on OpenGL 2.0, but it eliminates most of the fixed-function rendering pipeline in favor of a programmable one in a move similar to transition from OpenGL 3.0 to 3.1

https://en.wikipedia.org/wiki/OpenGL_ES#OpenGL_ES_2.0

The OpenGL ES 3.0 specification, which was first publicly released in August 2012, is also backwards compatible with OpenGL ES 2.0 which means that any devices which state that their GPU supports the OpenGL ES 3.0 specification should also be compatible with all applications designed for OpenGL ES 2.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.