Everything You Never Wanted to Know about the WonderSwan RTC
Only two games seem to use a RTC. These two games are:
* Dokodemo Hamster 3
* Inuyasha - Kagome no Sengoku Nikki
The WonderWitch BIOS ("FreyaBIOS") does read from the RTC regularly. However, the two winning games from the WonderWitch competition don't appear to use the output.
The other ROMs known for the WonderSwan that have headers that claim an RTC – namely:
* Digimon Tamers - Battle Spirit
* From TV Animation One Piece - Treasure Wars 2 - Buggy Land e Youkoso
* Robot Works
neither have the RTC IC on their game cards nor have code to access the RTC.
Everything You Never Wanted to Know about the WonderSwan 2003 mapper IC's RTC Interface
The interface is custom-made for Seiko's S-3511A RTC IC.
Code: Select all
Write to port 0CAh: [...Z FFFR] |||| |||| |||| |||+- 1:read 0:write |||+-++++- Function number (0x10-0x1B) +++------- Don't care
Invalid values (i.e. 0x00 through 0x0F, 0x1C through 0x1F) do immediately deassert +CS, but cannot safely be used as an "abort": RTC CK continues to relay the 384kHz clock from the Wonderswan. This can cause a bit insertion, because for a subsequent function +CS will rise simultaneously to SCK rising)
Functions 0x10 through 0x18 exactly correspond to on-the-wire commands 0x60 through 0x68. The 2003 knows the number of bytes to transfer for each function:
0x10, 0x11: 0 bytes of data (reset)
0x12, 0x13: 1 byte of data (status / 12-vs-24 hour / IRQ enables)
0x14, 0x15: 7 bytes of data (RTCC, YMDWHMS)
0x16, 0x17: 3 bytes of data (same RTC, only HMS)
0x18: 2 bytes of data (Alarm cfg)
0x19, 0x1A, 0x1B: 2 bytes of data (invalid)
0x19, which would be "read from Alarm configuration", isn't supported by the S-3511. Both bytes will read back as 0xFF, because the S-3511 serial output is open-drain. HOWEVER, the S-3511 thinks it's being written to, and this will write 0xFFFF to the register.
0x1A and 0x1B are invalid and ignored by the S-3511. 0x1A has no effect, but expects to write two bytes. 0x1B reads two bytes, both of which will be 0xFF.
Commands 0x6C through 0x6F are documented to exist by the S-3511, but are not supported by the 2003.
The WonderWitch and its associated games (FreyaBIOS) only provides an abstraction for functions 0x10, 0x12, 0x13, 0x14, 0x15, and 0x18.
The 2003 assumes that the data in port 0CBh is already valid when a write function is started. (The game need not have already written the payload: it just needs to be loaded before the interface clocks out the first 8 bits)
Code: Select all
Read from port 0CAh: [X00B FFFR] |||| |||| |||| |||+- 1:read 0:write |||| ++++- lower four bits of function number |||+------ 1: more SPI pending 0:finished |++------- always zero +--------- 1: 2003 needs read/write by Wonderswan 0: 2003 ignores reads/writes by Wonderswan
read/write from 0CBh: payload data
Most data in the S-3511 are encoded as packed BCD. Consult Seiko's datasheet for more details. Other than knowing how many reads or writes are needed for each function, the 2003 is otherwise naive.
Existing games assume that the data isn't valid until the "X" bit is set in 0CAh.
Everything You Never Wanted to Know about the S-3511A on the WonderSwan
The WS+2003+S3511 combine together to make a truly bad system for interrupts.
The S3511 datasheet says
"Alarm interrupt output pin [...] is disabled by rewriting the status register."
Unfortunately, this is not true.
The S3511 can only acknowledge interrupts by disabling interrupts.
There are four (five if you count "none") "interrupt" modes the the S3511 can operate in:
"Frequency" (INTFE) mode-
* This mode cannot be ACKed, and the output is just a function of the current prescaler count.
If I very slowly manually clock in bits into the 2003 when writing to 0x18, I can watch the contents of the ALARM register rotate through and the resulting frequency change. This means that interrupts probably need to be disabled before setting a time-of-day interrupt.
"Per-minute edge" (INTME) mode-
* Continuously asserts /IRQ throughout first 10ms of second=0
* This mode can be ACKed, BUT...
Note that 10ms is most of an entire vertical redraw. In experiments, I got an assert about 6ms before vsync, and as a result got double-firing IRQs. I don't think there's a good way to handle this without another timer.
"Per-minute steady" (INTME and INTFE) mode-
Same as "per-minute edge" but:
* Additionally deasserts /IRQ at second 30.
"Alarm" (INTAE and neither other INTxE bit) mode-
* Continuously asserts /IRQ throughout hour and minute as programmed.
* Does not de-assert automatically.
* Cannot be ACKed during the scheduled minute.
"No interrupts" mode (all bits clear)
* Continuously de-asserts /IRQ
The Wonderswan treats external interrupts as level-triggered, not edge-triggered, so we'd have to stay in the handler until /IRQ was deasserted, or else we'd re-enter.
But: the 2003 provides no good way to abort an ongoing transaction, so we may not be able to immediately acknowledge the IRQ anyway.
All of these together mean that the only thing we can do in the interrupt handler is mask out the interrupt.
A Piece of Homebrew to Test Out the WonderSwan RTC
A very minimal piece of software I wrote that can run on real hardware, demonstrating where existing emulators are inaccurate and how to use the various interrupt modes.
On hardware, it should look like this: Keys are as follows:
X1/X3 (up/down in landscape) - move right-pointing triangle up/down
X2/X4 (left/right in landscape) - move down-pointing triangle left/right
Y1-Y4 - toggle 1/2/4/8s bit in hexadecimal digit pointed to by down-pointing triangle
A,B,Start - send command specified by right-pointing triangle with payload in bottom row
Up-pointing triangle indicates how many bytes were requested by 2003.
Hexadecimal number in top right corner indicates how many IRQs have fired since power-on, modulo 256.
How does Mednafen get this wrong?
* "B" bit isn't implemented, returns always 1, so reports infinite bytes available.
* "Month-of-year" field is 0 indexed, but should be 1-indexed (i.e. 1=January, 0x12=December)
* Doesn't even try to implement interrupts
How does Higan get this wrong?
* "B" bit isn't implemented, returns always 0, so only one byte available, and only one byte gets written
* Only implements the "time-of-day" interrupt, not the other three