PocketStation Homebrew

  • Thread starter Thread starter plasmagrass
  • Start date Start date
  • Views Views 3,127
  • Replies Replies 34
  • Likes Likes 10

plasmagrass

Active Member
Newcomer
Joined
Apr 8, 2025
Messages
42
Reaction score
51
Trophies
0
XP
276
Country
Japan
I managed to get my first animation on PocketStation, and I got to learn Rust to make it along the way.

I'm going to try it on a real device as soon as I implement the exit program functionality, because otherwise I'd need to pop off the coin cell battery which is not fun.


128 kB of programmable goodness, a whopping 1.5kB of usable RAM, and it got PCM speaker! I tried putting some random meme music in it but even at 8000Hz sampling rate, 16 bit PCM, that'd give me like 8 seconds of uninterruptable music. I guess there's really no way other than using sampled instrument and midi-like synthesizer, just like the old days
2025_04_20_23-24__kqr.gif
 
1746268257478.png
I'm currently reversing the kernel rom of pocket station. Turns out getting sound on this thing is a million times harder than getting silly image.

It's just PCM, and making it play square wave or very short sample audio isn't really an issue, but I wanted to know how pocketstation does it, such as the initial boot chime which sounds absolutely gorgeous. I wonder if they used any PCM sample or if it's just generated with code.
 
In the beginning of the project, I thought drawing pixels on the screen is going to be complicated. Nope, just turn the display on and put the rows of 32 bits into the "backplane" registers, preferably at 32Hz but it's up to you. You can not update the screen and it stays whatever you set it too, the LCD driver takes care of that.

Sound, on the other hand, is SUPER hard. On the surface it seemed easy. It's just a 10-bit PCM register. Put numbers there and speaker moves. Easy-peasy.

Except for the fact that it's extremely timing sensitive. You'll want a sample rate of ~20kHz on a CPU that runs from 128kHz to 8Mhz.

During the time you're producing sound, your CPU can't do anything else, otherwise the sound timing will be off. Granted there's a hardware timer that we may be able to use, but testing it with the emulator, the CPU seems to be too busy processing sound at such high frequency, making the whole interrupt thing pointless. I'm not sure how to make a game loop for producing sound.

Do you do this:

while(1) {
takeInput()
draw()
playSound()
waitVblank()
}

If so, how long should I be playing the sound? Maybe 1/32 second worth of sound, and then start the loop all over again. I have no idea if the sound would be choppy that way...

Gave up trying to reverse engineer the kernel for now, I'll just stick to what works.

I know I can send PCM samples (.wav) to it and I was able to get sound, but I can't put entire music into the poor thing, it only got like 128kb of flash memory and 2kb of ram (actually less than that).

I'm shifting my work into just using a Tracker-style music generator (similar to openMPT or MOD or SchismTracker). I roughly know how to play the sound and how to shift the pitch.
Post automatically merged:

I was just about to submit a bug report of the compiler that I used for this project. Turns out the emulator I was using was inaccurate...
Post automatically merged:

I uhh, end up not trusting the existing emulator. I'm currently writing my own. Thankfully the rust community already have decent support for GDB and armv4t. Just need to write the SoC

I already got gdbstub running and I can even do single stepping!
 
Last edited by plasmagrass,
Hiya, sorry for the hiatus, let me try to explain my progress.

Currently, I am focused on getting the emulator working, because I find that there are too many issues with the best pockestation emulator out there (by drhell), and there's no way I could fix it since it wasn't open source.

I'm putting the repo up for people here to see, but it's still very early in development. I also try to summarize the short-term milestone there.

I was able to implement timer interrupt functionality and able to test it. See here. This is my first time writing an emulator, and being able to write assembly, setting up timer, and having my code interrupted by the timer and falls into IRQ is so rewarding!

The goal is to make not only an emulator that plays the game, but also host a GDB server so we can step-through our custom game in a simulated PocketStation! I have basic GDB functionality running, and currently I'm trying to boot into the pocketstation OS.

> I would think you'd just program the tones to generate directly, or be able to utilize some kind of sequencer format for music since it's so small.

Given the small memory, I'd expect to program tones directly or use a sequencer for music. The limited memory is a real challenge. I might try to reverse-engineer how others achieved it someday. I found asset dumps, but they only had basic square waves, no sample music, even though the existing games themselves have great chiptune. I'd definitely be interested if you have any idea how someone might have done it.
Post automatically merged:

I managed to make an SVD file

https://gitlab.com/plasmagrass/plasma_pocket/-/blob/master/refs/PocketStation.svd

Basically this is the register definition of pocketstation. Think of it like the hardware address of all the peripherals like timers, lcd, sound system, etc. Totally vibecoded and probably incorrect but it's good enough to get me started. 99% vibecoded based on available info
 
Last edited by plasmagrass,
A bit of rant.

My pocketstation emulator project uses gdb for communication. The idea is that we can use other programs that are compatible with gdb.

However...

I tried Binary Ninja, radare, vscode, even IDA, and all of them have different kind of bugs with the gdb remote debugging integration. (I didn't bother trying ghidra). Binary Ninja's gdb integration sucks for now and will probably stay bad until they moved to gdb/mi integration

Apparently the BEST program for using gdb remote protocol is gdb itself. but there's NO good gui frontend for gdb that is a) works on windows and b) released in the last decade or so.

So I'm in the progress of making my own gui for gdb using the gdb/mi interface. right now it just have the bare essentials: step, continue, interrupt, breakpoint, register, and stack frame. Of course there's way more basic features to be added soon.




1753710606161.png
 
  • Like
Reactions: hippy dave
By any chance, have you ever worked on making a minigame for a Dreamcast VMU? I've always wanted to make a small portable game on an LCD screen because I have a soft spot for "BrickGame-Looking Games." But I have no idea if it's easier to program on the PocketStation or the VMU.
 
By any chance, have you ever worked on making a minigame for a Dreamcast VMU? I've always wanted to make a small portable game on an LCD screen because I have a soft spot for "BrickGame-Looking Games." But I have no idea if it's easier to program on the PocketStation or the VMU.
Same here, but I am opting to go for Pocketstation instead of Dreamcast VMU. I'm currently making the tooling because information was pretty sparse. Apparently original Pocketstation game developers were told to just use a generic arm emulator and run a program to convert the memory from a hex editor to an image, one frame at a time.

1. It uses ARM (same kind as GBA) so tooling is way simpler, rather than dreamcast vmu that uses 8-bit SHARP processor. The first post in this thread was made using Rust code that can be ran on drhell's pocketstation emulator. VMU only has 512B of RAM, meanwhile pocketstation has a whopping 2kB worth of RAM.
2. I wasn't able to find a good way of uploading things to VMU, seems like all options require both the Dreamcast and controller, whereas I was able to upload to pocketstation with just an arduino uno, resistors, and some bent cables
3. in Japan, I was able to get pocketstation from 500JPY~2000JPY


I guess the biggest issue with pocketstation is

1) the lack of development tool (which is what I'm trying to do here)

2) not sure how easy it is to get your hands on pocketstation if you live out of japan.
 
Rant ahead

I would like to update on this project. I think not many people making emulation talks about how difficult and frustrating things can get.

I started making my own emulator for pocketstation on May 2025. It is Aug now and FINALLY I can show this mundane looking logs of text.

Code:
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes: LCDBackplanes { backplanes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes w32 0xd000128 0
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes: LCDBackplanes { backplanes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes w32 0xd00012c 0
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes: LCDBackplanes { backplanes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes w32 0xd000130 110
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes: LCDBackplanes { backplanes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes w32 0xd000134 110
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes: LCDBackplanes { backplanes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes w32 0xd000138 110
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes: LCDBackplanes { backplanes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 272, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
2025-08-07T02:49:01.784Z INFO  [plasma_pocket::emu::peripherals::display] LCD backplanes w32 0xd00013c 1f0

These are the lines that writes to the LCD. Every number on the backplane is a single row of 32 bit pixels (black and white), and there are 32 rows. So when you see the number 272, you can convert it to binary `0b100010000` (leading zeroes implied) and you can kind of see that the `1` are black pixel, while the `0` is white.

I started this project knowing very little about ARM, let alone on embedded space. I was fortunate that there was already an `armv4t_emu` rust crate that I can use, because this small thing runs the same CPU architecture as GBA.

What took so long was figuring how the interrupts work. Here's how it works:
  • There are two types of interrupts: IRQ (Interrupt Request?) and FIQ (Fast Interrupt). Details not important, they are pretty standard for arm CPU. The CPU has 2 flags that can disable them
  • There's an Interrupt Controller, which is a separate device that interfaces with the peripherals (buttons, timers, etc)
  • Interrupt controller has two registers: latch and mask. A latch register is a register that if you write '1' into it, it will stay as '1'; writing '0' afterwards would have no effect. There's a separate address you can write into which would clear the latch. A mask is a register that programs can use to communicate which register it wanted to listen to. For example, when you're displaying an animation, you need to display a frame, setup a timer for 1 second, make sure that you'll get woken up after 1 second. You don't want to be woken up by other events such as button press, so you set your mask for only timer.
I was dealing with a bug where I thought the interrupt was firing, but there's a section in the kernel code that just loops, waiting for a flag variable to be set but never happened. I didn't know if I had a bug with the peripherals or the interrupt. Turns out the way I handled interrupt was wrong. There's this crucial detail that I missed:
  • When an interrupt source (button pressed, timer times out, etc) happened, the latch register should be set (regardless of it was enabled or not).
  • We MUST raise an interrupt when BOTH latch and enabled are ON, regardless of WHEN either of them were set. My previous code was buggy because we only check for interrupt when we are about to set the latch.
So yeah, this little mishap took me months to figure out. I suppose we're all learning here. I was so happy I finally got those LCD prints to happen, even if I haven't hooked up a display yet.

I guess the takeaway here: Emulator development are tricky, even something that seemed minor can send you back for quite a while.
 
  • Like
Reactions: hippy dave
Great to see a new PocketStation emulator in the works! It's always been kind of a blind spot in emulation, and the few options out there are broken in varying ways. Hell, out of PK201, no$GBA, and MAME, only PK201 actually saves progress properly.
 
Okay, I know it's been forever, but I manage to get the timing and input working. I'm using a CPU governor and that seems to be flexible enough to handle the 4MHz timing. The idea is we run the instructions in batch of 100,000 instruction, and then we sleep for around 7ms (actual number varies), and that should be enough to switch to other thread and what not.

animation is still broken, RTC is good for reading but not for writing yet. The way we set RTC is by sending "1" value to some RTC increment register, and if we set it twice, we increment _once_, probably because we're crossing clock domain. The RTC has a constant 1Hz interrupt (unless we're in configuration), irrespective of the CPU clock.

The button works and what you're seeing is the different screen that are available. I haven't implemented the "memory card" yet (should be easy). They actually have animation and the scrolling date effect works, but the : separator between the clock and the heart symbol doesn't animate, and I'm still figuring out why.

I've been dreading to do the sound. Maybe after I figure out why the animation failed.



1760721827421.png
1760721867675.png
1760721895456.png
 
  • Like
Reactions: hippy dave
Okay, I know it's been forever, but I manage to get the timing and input working. I'm using a CPU governor and that seems to be flexible enough to handle the 4MHz timing. The idea is we run the instructions in batch of 100,000 instruction, and then we sleep for around 7ms (actual number varies), and that should be enough to switch to other thread and what not.

animation is still broken, RTC is good for reading but not for writing yet. The way we set RTC is by sending "1" value to some RTC increment register, and if we set it twice, we increment _once_, probably because we're crossing clock domain. The RTC has a constant 1Hz interrupt (unless we're in configuration), irrespective of the CPU clock.

The button works and what you're seeing is the different screen that are available. I haven't implemented the "memory card" yet (should be easy). They actually have animation and the scrolling date effect works, but the : separator between the clock and the heart symbol doesn't animate, and I'm still figuring out why.

I've been dreading to do the sound. Maybe after I figure out why the animation failed.



View attachment 533859View attachment 533860View attachment 533861
Yeah, I'm trying to see if I can use the pocketstation emulator to see if I can get 110% on Crash 3. The Japanese version is the only version that does this, and it requires a PocketStation.
 

Site & Scene News

Popular threads in this forum