mirror of
https://github.com/afska/gba-link-connection.git
synced 2026-03-21 17:44:21 -05:00
Adding EditorConfig to normalize code style
This commit is contained in:
parent
6516744b6d
commit
91005296d2
23
.editorconfig
Normal file
23
.editorconfig
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
213
docs/gbatek.md
213
docs/gbatek.md
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
The GBAs Serial Port may be used in various different communication modes. Normal mode may exchange data between two GBAs (or to transfer data from master GBA to several slave GBAs in one-way direction).
|
||||
Multi-player mode may exchange data between up to four GBAs. UART mode works much like a RS232 interface. JOY Bus mode uses a standardized Nintendo protocol. And General Purpose mode allows to mis-use the 'serial' port as bi-directional 4bit parallel port.
|
||||
Note: The Nintendo DS does not include a Serial Port.
|
||||
|
||||
Note: The Nintendo DS does not include a Serial Port.
|
||||
|
||||
- SIO Normal Mode
|
||||
- SIO Multi-Player Mode
|
||||
- SIO General-Purpose Mode
|
||||
|
|
@ -13,12 +13,12 @@ Note: The Nintendo DS does not include a Serial Port.
|
|||
- GBA Wireless Adapter
|
||||
|
||||
# SIO Normal Mode
|
||||
|
||||
|
||||
This mode is used to communicate between two units.
|
||||
Transfer rates of 256Kbit/s or 2Mbit/s can be selected, however, the fast 2Mbit/s is intended ONLY for special hardware expansions that are DIRECTLY connected to the GBA link port (ie. without a cable being located between the GBA and expansion hardware). In normal cases, always use 256Kbit/s transfer rate which provides stable results.
|
||||
Transfer lengths of 8bit or 32bit may be used, the 8bit mode is the same as for older DMG/CGB gameboys, however, the voltages for "GBA cartridges in GBAs" are different as for "DMG/CGB cartridges in DMG/CGB/GBAs", ie. it is not possible to communicate between DMG/CGB games and GBA games.
|
||||
Transfer lengths of 8bit or 32bit may be used, the 8bit mode is the same as for older DMG/CGB gameboys, however, the voltages for "GBA cartridges in GBAs" are different as for "DMG/CGB cartridges in DMG/CGB/GBAs", ie. it is not possible to communicate between DMG/CGB games and GBA games.
|
||||
|
||||
**4000134h - RCNT (R) - Mode Selection, in Normal/Multiplayer/UART modes (R/W)**
|
||||
**4000134h - RCNT (R) - Mode Selection, in Normal/Multiplayer/UART modes (R/W)**
|
||||
|
||||
```
|
||||
Bit Expl.
|
||||
|
|
@ -29,7 +29,7 @@ Transfer lengths of 8bit or 32bit may be used, the 8bit mode is the same as for
|
|||
15 Must be zero (0) for Normal/Multiplayer/UART modes
|
||||
```
|
||||
|
||||
**4000128h - SIOCNT - SIO Control, usage in NORMAL Mode (R/W)**
|
||||
**4000128h - SIOCNT - SIO Control, usage in NORMAL Mode (R/W)**
|
||||
|
||||
```
|
||||
Bit Expl.
|
||||
|
|
@ -46,23 +46,23 @@ Transfer lengths of 8bit or 32bit may be used, the 8bit mode is the same as for
|
|||
15 Not used (Read only, always 0)
|
||||
```
|
||||
|
||||
The Start bit is automatically reset when the transfer completes, ie. when all 8 or 32 bits are transferred, at that time an IRQ may be generated.
|
||||
|
||||
**400012Ah - SIODATA8 - SIO Normal Communication 8bit Data (R/W)**
|
||||
The Start bit is automatically reset when the transfer completes, ie. when all 8 or 32 bits are transferred, at that time an IRQ may be generated.
|
||||
|
||||
For 8bit normal mode. Contains 8bit data (only lower 8bit are used). Outgoing data should be written to this register before starting the transfer. During transfer, transmitted bits are shifted-out (MSB first), and received bits are shifted-in simultaneously. Upon transfer completion, the register contains the received 8bit value.
|
||||
|
||||
**4000120h - SIODATA32\_L - SIO Normal Communication lower 16bit data (R/W)**
|
||||
**4000122h - SIODATA32\_H - SIO Normal Communication upper 16bit data (R/W)**
|
||||
**400012Ah - SIODATA8 - SIO Normal Communication 8bit Data (R/W)**
|
||||
|
||||
For 8bit normal mode. Contains 8bit data (only lower 8bit are used). Outgoing data should be written to this register before starting the transfer. During transfer, transmitted bits are shifted-out (MSB first), and received bits are shifted-in simultaneously. Upon transfer completion, the register contains the received 8bit value.
|
||||
|
||||
**4000120h - SIODATA32_L - SIO Normal Communication lower 16bit data (R/W)**
|
||||
**4000122h - SIODATA32_H - SIO Normal Communication upper 16bit data (R/W)**
|
||||
|
||||
Same as above SIODATA8, for 32bit normal transfer mode respectively.
|
||||
SIOCNT/RCNT must be set to 32bit normal mode <before> writing to SIODATA32.
|
||||
|
||||
SIOCNT/RCNT must be set to 32bit normal mode <before> writing to SIODATA32.
|
||||
|
||||
**Initialization**
|
||||
|
||||
First, initialize RCNT register. Second, set mode/clock bits in SIOCNT with startbit cleared. For master: select internal clock, and (in most cases) specify 256KHz as transfer rate. For slave: select external clock, the local transfer rate selection is then ignored, as the transfer rate is supplied by the remote GBA (or other computer, which might supply custom transfer rates).
|
||||
Third, set the startbit in SIOCNT with mode/clock bits unchanged.
|
||||
|
||||
Third, set the startbit in SIOCNT with mode/clock bits unchanged.
|
||||
|
||||
**Recommended Communication Procedure for SLAVE unit (external clock)**
|
||||
\- Initialize data which is to be sent to master.
|
||||
\- Set Start flag.
|
||||
|
|
@ -71,41 +71,41 @@ Third, set the startbit in SIOCNT with mode/clock bits unchanged.
|
|||
\- Set SO to HIGH to indicate that we are not ready.
|
||||
\- Process received data.
|
||||
\- Repeat procedure if more data is to be transferred.
|
||||
(or is so=high done automatically? would be fine - more stable - otherwise master may still need delay)
|
||||
|
||||
(or is so=high done automatically? would be fine - more stable - otherwise master may still need delay)
|
||||
|
||||
**Recommended Communication Procedure for SLAVE unit (external clock)**
|
||||
\- Initialize data which is to be sent to master.
|
||||
\- Set Start=0 and SO=0 (SO=LOW indicates that slave is (almost) ready).
|
||||
\- Set Start=1 and SO=1 (SO=HIGH indicates not ready, applied after transfer).
|
||||
\- Set Start=1 and SO=1 (SO=HIGH indicates not ready, applied after transfer).
|
||||
|
||||
> (Expl. Old SO=LOW kept output until 1st clock bit received).
|
||||
> (Expl. Old SO=LOW kept output until 1st clock bit received).
|
||||
|
||||
> (Expl. New SO=HIGH is automatically output at transfer completion).
|
||||
> (Expl. New SO=HIGH is automatically output at transfer completion).
|
||||
|
||||
\- Set SO to LOW to indicate that master may start now.
|
||||
\- Wait for IRQ (or for Start bit to become zero). (Check timeout here!)
|
||||
\- Process received data.
|
||||
\- Repeat procedure if more data is to be transferred.
|
||||
|
||||
\- Repeat procedure if more data is to be transferred.
|
||||
|
||||
**Recommended Communication Procedure for MASTER unit (internal clock)**
|
||||
\- Initialize data which is to be sent to slave.
|
||||
\- Wait for SI to become LOW (slave ready). (Check timeout here!)
|
||||
\- Set Start flag.
|
||||
\- Wait for IRQ (or for Start bit to become zero).
|
||||
\- Process received data.
|
||||
\- Repeat procedure if more data is to be transferred.
|
||||
|
||||
\- Repeat procedure if more data is to be transferred.
|
||||
|
||||
**Cable Protocol**
|
||||
During inactive transfer, the shift clock (SC) is high. The transmit (SO) and receive (SI) data lines may be manually controlled as described above.
|
||||
When master sends SC=LOW, each master and slave must output the next outgoing data bit to SO. When master sends SC=HIGH, each master and slave must read out the opponents data bit from SI. This is repeated for each of the 8 or 32 bits, and when completed SC will be kept high again.
|
||||
|
||||
When master sends SC=LOW, each master and slave must output the next outgoing data bit to SO. When master sends SC=HIGH, each master and slave must read out the opponents data bit from SI. This is repeated for each of the 8 or 32 bits, and when completed SC will be kept high again.
|
||||
|
||||
**Transfer Rates**
|
||||
Either 256KHz or 2MHz rates can be selected for SC, so max 32KBytes (256Kbit) or 128KBytes (2Mbit) can be transferred per second. However, the software must process each 8bit or 32bit of transmitted data separately, so the actual transfer rate will be reduced by the time spent on handling each data unit.
|
||||
Only 256KHz provides stable results in most cases (such like when linking between two GBAs). The 2MHz rate is intended for special expansion hardware (with very short wires) only.
|
||||
|
||||
Only 256KHz provides stable results in most cases (such like when linking between two GBAs). The 2MHz rate is intended for special expansion hardware (with very short wires) only.
|
||||
|
||||
**Using Normal mode for One-Way Multiplayer communication**
|
||||
When using normal mode with multiplay-cables, data isn't exchanged between first and second GBA as usually. Instead, data is shifted from first to last GBA (the first GBA receives zero, because master SI is shortcut to GND).
|
||||
This behaviour may be used for fast ONE-WAY data transfer from master to all other GBAs. For example (3 GBAs linked):
|
||||
This behaviour may be used for fast ONE-WAY data transfer from master to all other GBAs. For example (3 GBAs linked):
|
||||
|
||||
```
|
||||
Step Sender 1st Recipient 2nd Recipient
|
||||
|
|
@ -116,13 +116,13 @@ This behaviour may be used for fast ONE-WAY data transfer from master to all oth
|
|||
```
|
||||
|
||||
The recipients should not output any own data, instead they should forward the previously received data to the next recipient during next transfer (just keep the incoming data unmodified in the data register).
|
||||
Due to the delayed forwarding, 2nd recipient should ignore the first incoming data. After the last transfer, the sender must send one (or more) dummy data unit(s), so that the last data is forwarded to the 2nd (or further) recipient(s).
|
||||
Due to the delayed forwarding, 2nd recipient should ignore the first incoming data. After the last transfer, the sender must send one (or more) dummy data unit(s), so that the last data is forwarded to the 2nd (or further) recipient(s).
|
||||
|
||||
# SIO Multi-Player Mode
|
||||
|
||||
Multi-Player mode can be used to communicate between up to 4 units.
|
||||
|
||||
**4000134h - RCNT (R) - Mode Selection, in Normal/Multiplayer/UART modes (R/W)**
|
||||
|
||||
Multi-Player mode can be used to communicate between up to 4 units.
|
||||
|
||||
**4000134h - RCNT (R) - Mode Selection, in Normal/Multiplayer/UART modes (R/W)**
|
||||
|
||||
```
|
||||
Bit Expl.
|
||||
|
|
@ -133,9 +133,9 @@ Multi-Player mode can be used to communicate between up to 4 units.
|
|||
15 Must be zero (0) for Normal/Multiplayer/UART modes
|
||||
```
|
||||
|
||||
Note: Even though undocumented, many Nintendo games are using Bit 0 to test current SC state in multiplay mode.
|
||||
|
||||
**4000128h - SIOCNT - SIO Control, usage in MULTI-PLAYER Mode (R/W)**
|
||||
Note: Even though undocumented, many Nintendo games are using Bit 0 to test current SC state in multiplay mode.
|
||||
|
||||
**4000128h - SIOCNT - SIO Control, usage in MULTI-PLAYER Mode (R/W)**
|
||||
|
||||
```
|
||||
Bit Expl.
|
||||
|
|
@ -152,34 +152,34 @@ Note: Even though undocumented, many Nintendo games are using Bit 0 to test curr
|
|||
15 Not used (Read only, always 0)
|
||||
```
|
||||
|
||||
The ID Bits are undefined until the first transfer has completed.
|
||||
|
||||
**400012Ah - SIOMLT\_SEND - Data Send Register (R/W)**
|
||||
Outgoing data (16 bit) which is to be sent to the other GBAs.
|
||||
|
||||
The ID Bits are undefined until the first transfer has completed.
|
||||
|
||||
**400012Ah - SIOMLT_SEND - Data Send Register (R/W)**
|
||||
Outgoing data (16 bit) which is to be sent to the other GBAs.
|
||||
|
||||
**4000120h - SIOMULTI0 - SIO Multi-Player Data 0 (Parent) (R/W)**
|
||||
**4000122h - SIOMULTI1 - SIO Multi-Player Data 1 (1st child) (R/W)**
|
||||
**4000124h - SIOMULTI2 - SIO Multi-Player Data 2 (2nd child) (R/W)**
|
||||
**4000126h - SIOMULTI3 - SIO Multi-Player Data 3 (3rd child) (R/W)**
|
||||
These registers are automatically reset to FFFFh upon transfer start.
|
||||
After transfer, these registers contain incoming data (16bit each) from all remote GBAs (if any / otherwise still FFFFh), as well as the local outgoing SIOMLT\_SEND data.
|
||||
Ie. after the transfer, all connected GBAs will contain the same values in their SIOMULTI0-3 registers.
|
||||
|
||||
After transfer, these registers contain incoming data (16bit each) from all remote GBAs (if any / otherwise still FFFFh), as well as the local outgoing SIOMLT_SEND data.
|
||||
Ie. after the transfer, all connected GBAs will contain the same values in their SIOMULTI0-3 registers.
|
||||
|
||||
**Initialization**
|
||||
\- Initialize RCNT Bit 14-15 and SIOCNT Bit 12-13 to select Multi-Player mode.
|
||||
\- Read SIOCNT Bit 3 to verify that all GBAs are in Multi-Player mode.
|
||||
\- Read SIOCNT Bit 2 to detect whether this is the Parent/Master unit.
|
||||
|
||||
\- Read SIOCNT Bit 2 to detect whether this is the Parent/Master unit.
|
||||
|
||||
**Recommended Transmission Procedure**
|
||||
\- Write outgoing data to SIODATA\_SEND.
|
||||
\- Write outgoing data to SIODATA_SEND.
|
||||
\- Master must set Start bit.
|
||||
\- All units must process received data in SIOMULTI0-3 when transfer completed.
|
||||
\- After the first successful transfer, ID Bits in SIOCNT are valid.
|
||||
\- If more data is to be transferred, repeat procedure.
|
||||
|
||||
The parent unit blindly sends data regardless of whether childs have already processed old data/supplied new data. So, parent unit might be required to insert delays between each transfer, and/or perform error checking.
|
||||
Also, slave units may signalize that they are not ready by temporarily switching into another communication mode (which does not output SD High, as Multi-Player mode does during inactivity).
|
||||
|
||||
Also, slave units may signalize that they are not ready by temporarily switching into another communication mode (which does not output SD High, as Multi-Player mode does during inactivity).
|
||||
|
||||
**Transfer Protocol**
|
||||
Beginning
|
||||
\- The masters SI pin is always LOW.
|
||||
|
|
@ -211,15 +211,15 @@ Step D
|
|||
Transfer end
|
||||
\- Master sets SC=HIGH, all GBAs set SO=HIGH.
|
||||
\- The Start/Busy bits of all GBAs are automatically cleared.
|
||||
\- Interrupts are requested in all GBAs (as far as enabled).
|
||||
|
||||
\- Interrupts are requested in all GBAs (as far as enabled).
|
||||
|
||||
**Error Bit**
|
||||
This bit is set when a slave did not receive SI=LOW even though SC=LOW signalized a transfer (this might happen when connecting more than 4 GBAs, or when the previous child is not connected). Also, the bit is set when a Stopbit wasn't HIGH.
|
||||
The error bit may be undefined during active transfer - read only after transfer completion (the transfer continues and completes as normal even if errors have occurred for some or all GBAs).
|
||||
Don't know: The bit is automatically reset/initialized with each transfer, or must be manually reset?
|
||||
|
||||
Don't know: The bit is automatically reset/initialized with each transfer, or must be manually reset?
|
||||
|
||||
**Transmission Time**
|
||||
The transmission time depends on the selected Baud rate. And on the amount of Bits (16 data bits plus start/stop bits for each GBA), delays between data for each GBA, plus final timeout (if less than 4 GBAs). That is, depending on the number of connected GBAs:
|
||||
The transmission time depends on the selected Baud rate. And on the amount of Bits (16 data bits plus start/stop bits for each GBA), delays between data for each GBA, plus final timeout (if less than 4 GBAs). That is, depending on the number of connected GBAs:
|
||||
|
||||
```
|
||||
GBAs Bits Delays Timeout
|
||||
|
|
@ -230,17 +230,17 @@ The transmission time depends on the selected Baud rate. And on the amount of Bi
|
|||
```
|
||||
|
||||
(The average Delay and Timeout periods are unknown?)
|
||||
Above is not counting the additional CPU time that must be spent on initiating and processing each transfer.
|
||||
|
||||
Above is not counting the additional CPU time that must be spent on initiating and processing each transfer.
|
||||
|
||||
**Fast One-Way Transmission**
|
||||
Beside for the actual SIO Multiplayer mode, you can also use SIO Normal mode for fast one-way data transfer from Master unit to all Child unit(s). See chapter about SIO Normal mode for details.
|
||||
|
||||
Beside for the actual SIO Multiplayer mode, you can also use SIO Normal mode for fast one-way data transfer from Master unit to all Child unit(s). See chapter about SIO Normal mode for details.
|
||||
|
||||
# SIO General-Purpose Mode
|
||||
|
||||
In this mode, the SIO is 'misused' as a 4bit bi-directional parallel port, each of the SI,SO,SC,SD pins may be directly controlled, each can be separately declared as input (with internal pull-up) or as output signal.
|
||||
|
||||
In this mode, the SIO is 'misused' as a 4bit bi-directional parallel port, each of the SI,SO,SC,SD pins may be directly controlled, each can be separately declared as input (with internal pull-up) or as output signal.
|
||||
|
||||
**4000134h - RCNT (R) - SIO Mode, usage in GENERAL-PURPOSE Mode (R/W)**
|
||||
Interrupts can be requested when SI changes from HIGH to LOW, as General Purpose mode does not require a serial shift clock, this interrupt may be produced even when the GBA is in Stop (low power standby) state.
|
||||
Interrupts can be requested when SI changes from HIGH to LOW, as General Purpose mode does not require a serial shift clock, this interrupt may be produced even when the GBA is in Stop (low power standby) state.
|
||||
|
||||
```
|
||||
Bit Expl.
|
||||
|
|
@ -258,14 +258,14 @@ Interrupts can be requested when SI changes from HIGH to LOW, as General Purpose
|
|||
15 Must be "1" for General-Purpose or JOYBUS Mode
|
||||
```
|
||||
|
||||
SI should be always used as Input to avoid problems with other hardware which does not expect data to be output there.
|
||||
|
||||
SI should be always used as Input to avoid problems with other hardware which does not expect data to be output there.
|
||||
|
||||
**4000128h - SIOCNT - SIO Control, not used in GENERAL-PURPOSE Mode**
|
||||
This register is not used in general purpose mode. That is, the separate bits of SIOCNT still exist and are read- and/or write-able in the same manner as for Normal, Multiplay, or UART mode (depending on SIOCNT Bit 12,13), but are having no effect on data being output to the link port.
|
||||
|
||||
This register is not used in general purpose mode. That is, the separate bits of SIOCNT still exist and are read- and/or write-able in the same manner as for Normal, Multiplay, or UART mode (depending on SIOCNT Bit 12,13), but are having no effect on data being output to the link port.
|
||||
|
||||
# SIO Control Registers Summary
|
||||
|
||||
**Mode Selection (by RCNT.15-14 and SIOCNT.13-12)**
|
||||
|
||||
**Mode Selection (by RCNT.15-14 and SIOCNT.13-12)**
|
||||
|
||||
```
|
||||
R.15 R.14 S.13 S.12 Mode
|
||||
|
|
@ -276,8 +276,8 @@ This register is not used in general purpose mode. That is, the separate bits of
|
|||
1 0 x x General Purpose
|
||||
1 1 x x JOY BUS
|
||||
```
|
||||
|
||||
**SIOCNT**
|
||||
|
||||
**SIOCNT**
|
||||
|
||||
```
|
||||
Bit 0 1 2 3 4 5 6 7 8 9 10 11
|
||||
|
|
@ -287,16 +287,17 @@ This register is not used in general purpose mode. That is, the separate bits of
|
|||
```
|
||||
|
||||
# GBA Wireless Adapter
|
||||
|
||||
**GBA Wireless Adapter (AGB-015 or OXY-004)**
|
||||
|
||||
**GBA Wireless Adapter (AGB-015 or OXY-004)**
|
||||
|
||||
- GBA Wireless Adapter Games
|
||||
- GBA Wireless Adapter Login
|
||||
- GBA Wireless Adapter Commands
|
||||
- GBA Wireless Adapter Component Lists
|
||||
|
||||
|
||||
## GBA Wireless Adapter Games
|
||||
|
||||
**GBA Wireless Adapter compatible Games**
|
||||
**GBA Wireless Adapter compatible Games**
|
||||
|
||||
```
|
||||
bit Generations series (Japan only)
|
||||
|
|
@ -341,11 +342,11 @@ This register is not used in general purpose mode. That is, the separate bits of
|
|||
Sennen Kazoku (Japan only)
|
||||
Shrek SuperSlam
|
||||
Sonic Advance 3
|
||||
```
|
||||
```
|
||||
|
||||
## GBA Wireless Adapter Login
|
||||
|
||||
**GBA Wireless Adapter Login**
|
||||
**GBA Wireless Adapter Login**
|
||||
|
||||
```
|
||||
rcnt=8000h ;\\
|
||||
|
|
@ -373,8 +374,8 @@ This register is not used in general purpose mode. That is, the separate bits of
|
|||
ret
|
||||
@@key\_string db 'NINTENDO',01h,80h ;10 bytes (5 halfwords; index=0..4)
|
||||
```
|
||||
|
||||
**Data exchanged during Login**
|
||||
|
||||
**Data exchanged during Login**
|
||||
|
||||
```
|
||||
GBA ADAPTER
|
||||
|
|
@ -394,11 +395,11 @@ This register is not used in general purpose mode. That is, the separate bits of
|
|||
\\ \\
|
||||
MSBs=Inverse of MSBs=Own
|
||||
Prev.Data.From.Adapter Data.From.Adapter
|
||||
```
|
||||
```
|
||||
|
||||
# GBA Wireless Adapter Commands
|
||||
|
||||
**Wireless Command/Parameter Transmission**
|
||||
|
||||
**Wireless Command/Parameter Transmission**
|
||||
|
||||
```
|
||||
GBA Adapter
|
||||
|
|
@ -412,7 +413,7 @@ This register is not used in general purpose mode. That is, the separate bits of
|
|||
... ... ;/
|
||||
```
|
||||
|
||||
Wireless 32bit Transfers
|
||||
Wireless 32bit Transfers
|
||||
|
||||
```
|
||||
wait until \[4000128h\].Bit2=0 ;want SI=0
|
||||
|
|
@ -421,9 +422,9 @@ Wireless 32bit Transfers
|
|||
set \[4000128h\].Bit3=0,Bit7=1 ;set SO=0 and start 32bit transfer
|
||||
```
|
||||
|
||||
All command/param/reply transfers should be done at Internal Clock (except, Response Words for command 25h,27h,35h,37h should use External Clock).
|
||||
|
||||
**Wireless Commands**
|
||||
All command/param/reply transfers should be done at Internal Clock (except, Response Words for command 25h,27h,35h,37h should use External Clock).
|
||||
|
||||
**Wireless Commands**
|
||||
|
||||
```
|
||||
Cmd Para Reply Name
|
||||
|
|
@ -444,7 +445,7 @@ All command/param/reply transfers should be done at Internal Clock (except, Resp
|
|||
1Eh - NN Get Directory? (receive list of game/user names?)
|
||||
1Fh 1 - Select Game for Download (send 16bit Game\_ID)
|
||||
|
||||
|
||||
|
||||
|
||||
20h - 1
|
||||
21h - 1 Good/Bad response to cmd 1Fh ?
|
||||
|
|
@ -463,7 +464,7 @@ All command/param/reply transfers should be done at Internal Clock (except, Resp
|
|||
2Eh
|
||||
2Fh
|
||||
|
||||
|
||||
|
||||
|
||||
30h 1 -
|
||||
31h
|
||||
|
|
@ -483,11 +484,11 @@ All command/param/reply transfers should be done at Internal Clock (except, Resp
|
|||
3Fh
|
||||
```
|
||||
|
||||
Special Response 996601EEh for error or so? (only at software side?)
|
||||
|
||||
Special Response 996601EEh for error or so? (only at software side?)
|
||||
|
||||
## GBA Wireless Adapter Component Lists
|
||||
|
||||
Main Chipset
|
||||
|
||||
Main Chipset
|
||||
|
||||
```
|
||||
U1 32pin Freescale MC13190 (2.4 GHz ISM band transceiver)
|
||||
|
|
@ -497,9 +498,9 @@ Main Chipset
|
|||
|
||||
The MC13190 is a Short-Range, Low-Power 2.4 GHz ISM band transceiver.
|
||||
The processor is Motorola's 32-bit M-Core RISC engine. (?) MCT3000 (?)
|
||||
See also: [http://www.eetimes.com/document.asp?doc\_id=1271943](http://www.eetimes.com/document.asp?doc_id=1271943)
|
||||
|
||||
Version with GERMAN Postal Code on sticker:
|
||||
See also: [http://www.eetimes.com/document.asp?doc_id=1271943](http://www.eetimes.com/document.asp?doc_id=1271943)
|
||||
|
||||
Version with GERMAN Postal Code on sticker:
|
||||
|
||||
```
|
||||
Sticker on Case:
|
||||
|
|
@ -514,7 +515,7 @@ Version with GERMAN Postal Code on sticker:
|
|||
X3 2pin "D959L4I" (9.5MHz) (top side) (ca. 19 clks per 2us)
|
||||
```
|
||||
|
||||
Further components... top side (A-7)
|
||||
Further components... top side (A-7)
|
||||
|
||||
```
|
||||
D1 5pin "D6F, 44" (top side, below X3)
|
||||
|
|
@ -531,7 +532,7 @@ Further components... top side (A-7)
|
|||
CN1 6pin connector to GBA link port (top side)
|
||||
```
|
||||
|
||||
Further components... bottom side (B-7)
|
||||
Further components... bottom side (B-7)
|
||||
|
||||
```
|
||||
U201 5pin "LXVB" (bottom side, near CN1)
|
||||
|
|
@ -540,8 +541,8 @@ Further components... bottom side (B-7)
|
|||
B70 6pin "\[\]" (bottom side, near ANT, small white chip)
|
||||
```
|
||||
|
||||
Plus, resistors and capacitors (without any markings).
|
||||
|
||||
Plus, resistors and capacitors (without any markings).
|
||||
|
||||
Version WITHOUT sticker:
|
||||
|
||||
```
|
||||
|
|
@ -554,7 +555,7 @@ Version WITHOUT sticker:
|
|||
X3 2pin "9.5SKSS4GT" (top side)
|
||||
```
|
||||
|
||||
Further components... top side (A-1)
|
||||
Further components... top side (A-1)
|
||||
|
||||
```
|
||||
D1 5pin "D6F, 31" (top side, below X3)
|
||||
|
|
@ -570,7 +571,7 @@ Further components... top side (A-1)
|
|||
CN1 6pin connector to GBA link port (top side)
|
||||
```
|
||||
|
||||
Further components... bottom side (B-1)
|
||||
Further components... bottom side (B-1)
|
||||
|
||||
```
|
||||
U201 5pin "LXV2" (bottom side, near CN1)
|
||||
|
|
@ -579,9 +580,9 @@ Further components... bottom side (B-1)
|
|||
B70 6pin "\[\]" (bottom side, near ANT, small white chip)
|
||||
```
|
||||
|
||||
Plus, resistors and capacitors (without any markings).
|
||||
|
||||
Major Differences
|
||||
Plus, resistors and capacitors (without any markings).
|
||||
|
||||
Major Differences
|
||||
|
||||
```
|
||||
Sticker "N/A" vs "Grossosteim P/AGB-A-WA-EUR-2 E3"
|
||||
|
|
@ -591,4 +592,4 @@ Major Differences
|
|||
U70/U72 U70 "AAG" (6pin) vs U72 "BMs" (4pin)
|
||||
```
|
||||
|
||||
Purpose of the changes is unknown (either older/newer revisions, or different regions with different FCC regulations).
|
||||
Purpose of the changes is unknown (either older/newer revisions, or different regions with different FCC regulations).
|
||||
|
|
|
|||
|
|
@ -137,4 +137,4 @@
|
|||
<li>MultiPlay mode: <a href="https://www.problemkaputt.de/gbatek.htm#siomultiplayermode" rel="nofollow noreferrer">https://www.problemkaputt.de/gbatek.htm#siomultiplayermode</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,15 @@
|
|||
Game Boy Advance Wireless Adapter
|
||||
-------------------------------------------------
|
||||
## Game Boy Advance Wireless Adapter
|
||||
|
||||
- 🌎 **Original post**: https://blog.kuiper.dev/gba-wireless-adapter 🌎
|
||||
- ✏️ **Updates**: [@davidgfnet](https://github.com/davidgfnet) and I were discovering new things and we added them here!
|
||||
|
||||
> You can learn more details by reading [LinkRawWireless.hpp](../lib/LinkRawWireless.hpp)'s code.
|
||||
|
||||
The Wireless Adapter
|
||||
====================
|
||||
# The Wireless Adapter
|
||||
|
||||
[](img/wirelessadapter.jpg)
|
||||
|
||||
*The Game Boy Advance Wireless Adapter*
|
||||
_The Game Boy Advance Wireless Adapter_
|
||||
|
||||
The wireless adapter is a piece of hardware that connects to the link cable port of a GBA that then communicates wirelessly with other adapters. It also contains a multibootable[1](#fn:multiboot) rom for playing games only one player has a copy of (although I am not aware of many games that use it, some NES classic games use this). However, the most notable games to use it is probably the Pokémon games Fire Red, Leaf Green and Emerald (Sapphire and Ruby do _not_ have wireless adapter support)[2](#fn:list_of_games).
|
||||
|
||||
|
|
@ -19,32 +17,29 @@ The wireless adapter is a piece of hardware that connects to the link cable port
|
|||
multiboot rom from the wireless adapter showing a game title of AGB.RS and a
|
||||
username of CORWIN](img/multiboot.jpg)](img/multiboot.jpg)
|
||||
|
||||
*You can make this screen display any game*
|
||||
_You can make this screen display any game_
|
||||
|
||||
Communicating with the adapter
|
||||
==============================
|
||||
# Communicating with the adapter
|
||||
|
||||
When I started, I used the following resources to start being able to talk with the wireless adapter:
|
||||
|
||||
* [This Gist contains some details](wireless.txt)
|
||||
* [GBATEK has a section on the wireless adapter](gbatek.md)
|
||||
- [This Gist contains some details](wireless.txt)
|
||||
- [GBATEK has a section on the wireless adapter](gbatek.md)
|
||||
|
||||
Pinout
|
||||
------
|
||||
## Pinout
|
||||
|
||||
The wireless adapter connects using the link cable port to the GBA. It uses
|
||||
|
||||
* 3.3V
|
||||
* Serial In
|
||||
* Serial out
|
||||
* SD
|
||||
* Clock
|
||||
* Ground
|
||||
- 3.3V
|
||||
- Serial In
|
||||
- Serial out
|
||||
- SD
|
||||
- Clock
|
||||
- Ground
|
||||
|
||||
which is all 6 of the pins. If you are going to mess with interfacing with the link cable yourself, make sure you know which pin is which. If you just want to use the wireless adapter as part of the GBA this isn’t relevant.
|
||||
|
||||
Serial Peripheral Interface
|
||||
---------------------------
|
||||
## Serial Peripheral Interface
|
||||
|
||||
Broadly speaking the GBA communicates with the wireless adapter using the Serial Peripheral Interface (SPI), however it can be somewhat weird. In the case of the GBA this is a three or four wire protocol depending on how you count. The clock, two data wires, and what is normally chip select but operates more as a reset.
|
||||
|
||||
|
|
@ -54,22 +49,21 @@ Broadly speaking the GBA communicates with the wireless adapter using the Serial
|
|||
analyser displaying an SPI trace from the GBA and wireless adapter
|
||||
communications](img/init.png)](img/init.png)
|
||||
|
||||
*A logic analyser can be used to probe the link cable protocol between the GBA and a Wireless Adapter*
|
||||
_A logic analyser can be used to probe the link cable protocol between the GBA and a Wireless Adapter_
|
||||
|
||||
I will break up the ways in which you communicate into three parts:
|
||||
|
||||
* Initialisation
|
||||
* Commands
|
||||
* Waiting for data
|
||||
- Initialisation
|
||||
- Commands
|
||||
- Waiting for data
|
||||
|
||||
One thing to make note of is that when I have screenshots showing the logic analyser traces, these all come from Pokémon Emerald as it is what I had at the time I did a lot of this.
|
||||
|
||||
Initialisation
|
||||
--------------
|
||||
## Initialisation
|
||||
|
||||
[](img/full_initialisation.png)
|
||||
|
||||
*The initialisation sequence captured using a logic analyser*
|
||||
_The initialisation sequence captured using a logic analyser_
|
||||
|
||||
Before starting sending and receiving commands, a handshake with the adapter needs to be done. During this, the clocks runs at 256 kHz. Real games start this process by resetting the adapter.
|
||||
|
||||
|
|
@ -86,22 +80,22 @@ The GBA and the adapter exchange the word “NINTENDO” with each other in quit
|
|||
[](img/first_single_u32.png)
|
||||
|
||||
*GBA sends `0x7FFF494E` and wireless adapter sends `0x00000000`.*
|
||||
_GBA sends `0x7FFF494E` and wireless adapter sends `0x00000000`._
|
||||
|
||||
The GBA here sends `0x7FFF494E`, of this the relevant part is the `0x494E`. If we look up what the bytes `0x49, 0x4E` are you will find them to be the letters `NI`. As exchanges happen simultaneously, at this point the adapter doesn’t know what to respond with and so responds with all zeros.
|
||||
|
||||
[](img/first_nintendo_32.png)
|
||||
|
||||
*GBA sends `0xFFFF494E` and wireless adapter sends `0x494EB6B1`.*
|
||||
_GBA sends `0xFFFF494E` and wireless adapter sends `0x494EB6B1`._
|
||||
|
||||
Next the GBA sends `0xFFFF494E` and now the wireless adapter does respond and responds with `0x494EB6B1`. I can assure you there is a pattern here:
|
||||
|
||||
* GBA:
|
||||
* Two _most_ significant bytes are the inverse of the adapters previous _most_ significant bytes.
|
||||
* Two _least_ significant bytes are the GBA’s own data.
|
||||
* Adapter:
|
||||
* Two _least_ significant bytes are the inverse of the GBA’s previous _least_ significant bytes.
|
||||
* Two most significant bytes are the adapters own data.
|
||||
- GBA:
|
||||
- Two _most_ significant bytes are the inverse of the adapters previous _most_ significant bytes.
|
||||
- Two _least_ significant bytes are the GBA’s own data.
|
||||
- Adapter:
|
||||
- Two _least_ significant bytes are the inverse of the GBA’s previous _least_ significant bytes.
|
||||
- Two most significant bytes are the adapters own data.
|
||||
|
||||
The “own” data are the bytes of the string “NINTENDO”, and you advance to the next pair when the most significant bytes equal the inverse of the least significant bytes.
|
||||
|
||||
|
|
@ -160,48 +154,48 @@ Following these rules the transfer looks like
|
|||
|
||||
Although note that due to the rules, the first few transfers may contain some junk data and be different to this in practice. And after this, you can start sending commands.
|
||||
|
||||
Commands
|
||||
--------
|
||||
## Commands
|
||||
|
||||
[](img/0x17.png)
|
||||
|
||||
*A command being sent by the GBA and acknowledged by the adapter*
|
||||
_A command being sent by the GBA and acknowledged by the adapter_
|
||||
|
||||
Commands are how you tell the adapter to do things. When in command mode the clock operates at 2 mHz. Some examples of commands include connect to adapter, send message, and receive message. All commands follow the same form:
|
||||
|
||||
* Command
|
||||
|
||||
The command is a 32 bit value of the form `0x9966LLCC`:
|
||||
|
||||
* LL
|
||||
* The length of the data payload in number of 32 bit values. For example here it is `0x01`, so one value is transmitted after this.
|
||||
* CC
|
||||
* The command type, there are a bunch of these! In this case the command type is `0x17`.
|
||||
* Data
|
||||
|
||||
All the data along with the command, must transmit the number given in the command
|
||||
|
||||
* Acknowledge
|
||||
|
||||
The adapter responds with a command, the length is the number of 32 bit values and the command type is always what you send + `0x80`. In this case the length is zero and the command is `0x17` + `0x80` = `0x97`.
|
||||
|
||||
* ⚠️ When you send invalid commands or a one you're not supposed to send in the current state (like sending a `0x1d` before a `0x1c`), the adapter responds `0x996601ee`. If you read the next word (as the response size is `01`), it gives you an error code (`2` when using an invalid command, `1` when using a valid command in an invalid state, or `0`).
|
||||
- Command
|
||||
|
||||
* Response
|
||||
|
||||
The data that the adapter responds with. Equal to the length given in the acknowledgement.
|
||||
The command is a 32 bit value of the form `0x9966LLCC`:
|
||||
|
||||
* Ready
|
||||
|
||||
In the figure, you’ll see that after exchanging any 32 bit value using SPI, some out of clock communication happens. This is the GBA and the Adapter signalling to each other that they are ready to communicate. This happens over the following stages:
|
||||
|
||||
1. The GBA goes low as soon as it can.
|
||||
2. The adapter goes high.
|
||||
3. The GBA goes high.
|
||||
4. The adapter goes low _when it’s ready_.
|
||||
5. The GBA goes low when it’s ready.
|
||||
6. The GBA starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value.
|
||||
- LL
|
||||
- The length of the data payload in number of 32 bit values. For example here it is `0x01`, so one value is transmitted after this.
|
||||
- CC
|
||||
- The command type, there are a bunch of these! In this case the command type is `0x17`.
|
||||
|
||||
- Data
|
||||
|
||||
All the data along with the command, must transmit the number given in the command
|
||||
|
||||
- Acknowledge
|
||||
|
||||
The adapter responds with a command, the length is the number of 32 bit values and the command type is always what you send + `0x80`. In this case the length is zero and the command is `0x17` + `0x80` = `0x97`.
|
||||
|
||||
- ⚠️ When you send invalid commands or a one you're not supposed to send in the current state (like sending a `0x1d` before a `0x1c`), the adapter responds `0x996601ee`. If you read the next word (as the response size is `01`), it gives you an error code (`2` when using an invalid command, `1` when using a valid command in an invalid state, or `0`).
|
||||
|
||||
- Response
|
||||
|
||||
The data that the adapter responds with. Equal to the length given in the acknowledgement.
|
||||
|
||||
- Ready
|
||||
|
||||
In the figure, you’ll see that after exchanging any 32 bit value using SPI, some out of clock communication happens. This is the GBA and the Adapter signalling to each other that they are ready to communicate. This happens over the following stages:
|
||||
|
||||
1. The GBA goes low as soon as it can.
|
||||
2. The adapter goes high.
|
||||
3. The GBA goes high.
|
||||
4. The adapter goes low _when it’s ready_.
|
||||
5. The GBA goes low when it’s ready.
|
||||
6. The GBA starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value.
|
||||
|
||||
⌛ If this acknowledge procedure doesn't complete, the adapter "gives up" after ~800μs and start listening again for commands. That means that if a game doesn't implement this logic, it has to wait almost 1 millisecond between transfers (vs ~40μs in normal scenarios).
|
||||
|
||||
|
|
@ -213,21 +207,20 @@ Whenever either side expects something to be sent from the other (as SPI is alwa
|
|||
|
||||
[](img/0x10.png)
|
||||
|
||||
* Send length: 0, Response length: 0
|
||||
|
||||
* First thing to be called after finishing the initialisation sequence.
|
||||
- Send length: 0, Response length: 0
|
||||
- First thing to be called after finishing the initialisation sequence.
|
||||
|
||||
#### Setup - `0x17`
|
||||
|
||||
[](img/0x17.png)
|
||||
|
||||
* Send length: 1, response length: 0
|
||||
|
||||
* Games set this. It seems to setup the adapter's configuration.
|
||||
- Send length: 1, response length: 0
|
||||
- Games set this. It seems to setup the adapter's configuration.
|
||||
|
||||
Both Pokemon games and the multiboot ROM that the adapter sends when no cartridge is inserted use `0x003C0420`.
|
||||
|
||||
🔝 For a game, the most important bits are bits `16-17` (let's call this `maxPlayers`), which specify the maximum number of allowed players:
|
||||
|
||||
- `00`: 5 players (1 host and 4 clients)
|
||||
- `01`: 4 players
|
||||
- `10`: 3 players
|
||||
|
|
@ -243,9 +236,8 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
[](img/0x16.png)
|
||||
|
||||
* Send length: 6, response length: 0
|
||||
|
||||
* The data to be broadcast out to all adapters. Examples of use include the union room, broadcasting game name and username in download play, and the username in direct multiplayer in Pokémon.
|
||||
- Send length: 6, response length: 0
|
||||
- The data to be broadcast out to all adapters. Examples of use include the union room, broadcasting game name and username in download play, and the username in direct multiplayer in Pokémon.
|
||||
|
||||
💻 This is the first command used to start a server. The 6 parameters are the ASCII characters of the game and user name, plus some bytes indicating whether the server should appear in the Download Play list or not. Here's a byte by byte explanation:
|
||||
|
||||
|
|
@ -259,36 +251,33 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
#### StartHost - `0x19`
|
||||
|
||||
* Send length: 0, response length: 0
|
||||
|
||||
* This uses the broadcast data given by the broadcast command and actually does the broadcasting.
|
||||
- Send length: 0, response length: 0
|
||||
- This uses the broadcast data given by the broadcast command and actually does the broadcasting.
|
||||
|
||||
#### EndHost - `0x1b`
|
||||
|
||||
* Send length: 0, response length: 2+
|
||||
|
||||
* This command stops host broadcast. This allows to "close" the session and stop allowing new clients, but also **keeping the existing connections alive**. Sends and Receives still work, but:
|
||||
- Clients cannot connect, even if they already know the host ID (`FinishConnection` will fail).
|
||||
- Calls to `AcceptConnections` on the host side will fail, unless `StartHost` is called again.
|
||||
|
||||
- Send length: 0, response length: 2+
|
||||
- This command stops host broadcast. This allows to "close" the session and stop allowing new clients, but also **keeping the existing connections alive**. Sends and Receives still work, but:
|
||||
- Clients cannot connect, even if they already know the host ID (`FinishConnection` will fail).
|
||||
- Calls to `AcceptConnections` on the host side will fail, unless `StartHost` is called again.
|
||||
|
||||
#### BroadcastRead - `0x1c`, `0x1d` and `0x1e`
|
||||
|
||||
[](img/0x1d.png)
|
||||
|
||||
* Send length: 0, response length: 7 \* number of broadcasts (maximum: 4)
|
||||
|
||||
* All currently broadcasting devices are returned here along with a word of **metadata** (the metadata word first, then 6 words with broadcast data).
|
||||
* The metadata contains:
|
||||
* First 2 bytes: Server ID. IDs have 16 bits.
|
||||
* 3rd byte: Next available slot. This can be used to check whether a player can join a room or not.
|
||||
* `0b00`: If you join this room, your `clientNumber` will be 0.
|
||||
* `0b01`: If you join this room, your `clientNumber` will be 1.
|
||||
* `0b10`: If you join this room, your `clientNumber` will be 2.
|
||||
* `0b11`: If you join this room, your `clientNumber` will be 3.
|
||||
* `0xff`: The server is full. You cannot join this room.
|
||||
* Although `LinkWireless` uses this to know the number of connected players, that only works because -by design- rooms are closed when a player disconnects. The hardware allows disconnecting specific clients, so if the next available slot is e.g. 2, it can mean that there are 3 connected players (1 host + 2 clients) or that there are more players, but the third client (`clientNumber` = 2) has disconnected and the slot is now free.
|
||||
* The number of available slots depends on `maxPlayers` (see [Setup](#setup---0x17)) and/or [EndHost](#endhost---0x1b).
|
||||
* 4th byte: Zero.
|
||||
- Send length: 0, response length: 7 \* number of broadcasts (maximum: 4)
|
||||
- All currently broadcasting devices are returned here along with a word of **metadata** (the metadata word first, then 6 words with broadcast data).
|
||||
- The metadata contains:
|
||||
- First 2 bytes: Server ID. IDs have 16 bits.
|
||||
- 3rd byte: Next available slot. This can be used to check whether a player can join a room or not.
|
||||
- `0b00`: If you join this room, your `clientNumber` will be 0.
|
||||
- `0b01`: If you join this room, your `clientNumber` will be 1.
|
||||
- `0b10`: If you join this room, your `clientNumber` will be 2.
|
||||
- `0b11`: If you join this room, your `clientNumber` will be 3.
|
||||
- `0xff`: The server is full. You cannot join this room.
|
||||
- Although `LinkWireless` uses this to know the number of connected players, that only works because -by design- rooms are closed when a player disconnects. The hardware allows disconnecting specific clients, so if the next available slot is e.g. 2, it can mean that there are 3 connected players (1 host + 2 clients) or that there are more players, but the third client (`clientNumber` = 2) has disconnected and the slot is now free.
|
||||
- The number of available slots depends on `maxPlayers` (see [Setup](#setup---0x17)) and/or [EndHost](#endhost---0x1b).
|
||||
- 4th byte: Zero.
|
||||
|
||||
🆔 IDs are randomly generated. Each time you broadcast or connect, the adapter assigns you a new ID.
|
||||
|
||||
|
|
@ -300,10 +289,9 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
#### AcceptConnections - `0x1a`
|
||||
|
||||
* Send length: 0, response length: 0+
|
||||
|
||||
* Accepts new connections and returns a list with the connected adapters. The length of the response is zero if there are no connected adapters.
|
||||
* It includes one value per connected client, in which the most significant byte is the `clientNumber` (see [IsFinishedConnect](#isfinishedconnect---0x20)) and the least significant byte is the ID.
|
||||
- Send length: 0, response length: 0+
|
||||
- Accepts new connections and returns a list with the connected adapters. The length of the response is zero if there are no connected adapters.
|
||||
- It includes one value per connected client, in which the most significant byte is the `clientNumber` (see [IsFinishedConnect](#isfinishedconnect---0x20)) and the least significant byte is the ID.
|
||||
|
||||
🔗 If this command reports 3 connected consoles, after turning off one of them, it will still report 3 consoles. Servers need to detect timeouts in another way.
|
||||
|
||||
|
|
@ -311,18 +299,16 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
[](img/0x1f.png)
|
||||
|
||||
* Send length: 1, response length: 0
|
||||
|
||||
* Send the ID of the adapter you want to connect to from [BroadcastRead](#broadcastread---0x1c-0x1d-and-0x1e).
|
||||
- Send length: 1, response length: 0
|
||||
- Send the ID of the adapter you want to connect to from [BroadcastRead](#broadcastread---0x1c-0x1d-and-0x1e).
|
||||
|
||||
#### IsFinishedConnect - `0x20`
|
||||
|
||||
[](img/0x20.png)
|
||||
|
||||
* Send length: 0, response length: 1
|
||||
|
||||
* Responds with a 16 bit ID as lower 16 bits if finished, otherwise responds with `0x01000000`.
|
||||
|
||||
- Send length: 0, response length: 1
|
||||
- Responds with a 16 bit ID as lower 16 bits if finished, otherwise responds with `0x01000000`.
|
||||
|
||||
👆 It also responds in its bits 16 and 17 a number that represents the `clientNumber` (0 to 3). Lets say our ID is `abcd`, it will respond `0x0000abcd` if we are the first client that connects to that server, `0x0001abcd` if we are the second one, `0x0002abcd` third, and `0x0003abcd` fourth. Rooms allow 5 simultaneous adapters at max.
|
||||
|
||||
💥 If the connection failed, the `clientNumber` will be a number higher than `3`.
|
||||
|
|
@ -331,54 +317,55 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
[](img/0x21.png)
|
||||
|
||||
* Send length: 0, response length: 1
|
||||
|
||||
* Called after [IsFinishedConnect](#isfinishedconnect---0x20), responds with the final device ID (which tends to be equal to the ID from the previous command), the `clientNumber` in bits 16 and 17, and if all went well, zeros in its remaining bits.
|
||||
- Send length: 0, response length: 1
|
||||
- Called after [IsFinishedConnect](#isfinishedconnect---0x20), responds with the final device ID (which tends to be equal to the ID from the previous command), the `clientNumber` in bits 16 and 17, and if all went well, zeros in its remaining bits.
|
||||
|
||||
#### SendData - `0x24`
|
||||
|
||||
* Send length: N, response length: 0
|
||||
|
||||
* Send N 32 bit values to connected adapter.
|
||||
|
||||
- Send length: N, response length: 0
|
||||
- Send N 32 bit values to connected adapter.
|
||||
|
||||
⚠️ The first value **is a header**, and has to be correct. Otherwise, the adapter will ignore the command and won't send any data. The header is as follows:
|
||||
|
||||
- For hosts: the number of `bytes` that come next. For example, if we want to send `0xaabbccdd` and `0x12345678` in the same command, we need to send:
|
||||
* `0x00000008`, `0xaabbccdd`, `0x12345678`.
|
||||
- `0x00000008`, `0xaabbccdd`, `0x12345678`.
|
||||
- For clients: `(bytes << (3 + (1+clientNumber) * 5))`. The `clientNumber` is what I described in [IsFinishedConnect](#isfinishedconnect---0x20). For example, if we want to send a single 4-byte value (`0xaabbccdd`):
|
||||
* The first client should send: `0x400`, `0xaabbccdd`
|
||||
* The second client should send: `0x8000`, `0xaabbccdd`
|
||||
* The third client should send: `0x100000`, `0xaabbccdd`
|
||||
* The fourth client should send: `0x2000000`, `0xaabbccdd`
|
||||
- The first client should send: `0x400`, `0xaabbccdd`
|
||||
- The second client should send: `0x8000`, `0xaabbccdd`
|
||||
- The third client should send: `0x100000`, `0xaabbccdd`
|
||||
- The fourth client should send: `0x2000000`, `0xaabbccdd`
|
||||
|
||||
🔝 Each `SendData` can send up to:
|
||||
|
||||
- **Host:** 87 bytes (or 21.75 values)
|
||||
- **Clients:** 16 bytes (or 4 values)
|
||||
- *(the header doesn't count)*
|
||||
- _(the header doesn't count)_
|
||||
|
||||
🗂️ Any non-multiple of 4 byte count will send LSB bytes first. For example, a host sending `0x00000003`, `0xaabbccdd` will result in bytes `0xbb`, `0xcc` and `0xdd` being received by clients (the clients will receive `0x00bbccdd`).
|
||||
|
||||
🤝 Note that when having more than 2 connected adapters, data is not transferred between different clients. If a client wants to tell something to another client, it has to talk first with the host with `SendData`, and then the host needs to relay that information to the other client.
|
||||
|
||||
👑 Internally, data is only sent when **the host** calls `SendData`:
|
||||
|
||||
- The send/receive buffer size is 1 packet, so calling `SendData` multiple times on either side (before the other side calls `ReceiveData`) will result in data loss.
|
||||
- Clients only **schedule** the data transfer, but they don't do it until the host sends something. This is problematic because the command overrides previously scheduled transfers, so calling `SendData` multiple times on the client side before the host calls `SendData` would also result in data loss. I believe this is why most games use `SendDataWait` on the client side.
|
||||
- Here's an example of this behavior:
|
||||
- **Client**: `SendData` `{sndHeader}`, `10`
|
||||
- **Host**: `SendData` `{sndHeader}`, `1` **(\*)**
|
||||
- *(here, the adapter internally receives the 10 from the client)*
|
||||
- **Host**: `SendData` `{sndHeader}`, `2`
|
||||
- *(here, the previous packet with 1 is lost since nobody received it yet)*
|
||||
- **Client**: `ReceiveData`
|
||||
- Receives `{rcvHeader}`, `2`
|
||||
- **Client**: `SendData` `{sndHeader}`, `20`
|
||||
- **Host**: `ReceiveData`
|
||||
- Receives `{rcvHeader}`, `10` *(pending from **(\*)**)*
|
||||
- **Host**: `ReceiveData`
|
||||
- Receives nothing
|
||||
- **Host**: `SendData` `{sndHeader}`, `3`
|
||||
- *(here, the adapter internally receives the 20 from the client)*
|
||||
- **Host**: `ReceiveData`
|
||||
- Receives `{rcvHeader}`, 20
|
||||
- **Client**: `SendData` `{sndHeader}`, `10`
|
||||
- **Host**: `SendData` `{sndHeader}`, `1` **(\*)**
|
||||
- _(here, the adapter internally receives the 10 from the client)_
|
||||
- **Host**: `SendData` `{sndHeader}`, `2`
|
||||
- _(here, the previous packet with 1 is lost since nobody received it yet)_
|
||||
- **Client**: `ReceiveData`
|
||||
- Receives `{rcvHeader}`, `2`
|
||||
- **Client**: `SendData` `{sndHeader}`, `20`
|
||||
- **Host**: `ReceiveData`
|
||||
- Receives `{rcvHeader}`, `10` _(pending from **(\*)**)_
|
||||
- **Host**: `ReceiveData`
|
||||
- Receives nothing
|
||||
- **Host**: `SendData` `{sndHeader}`, `3`
|
||||
- _(here, the adapter internally receives the 20 from the client)_
|
||||
- **Host**: `ReceiveData`
|
||||
- Receives `{rcvHeader}`, 20
|
||||
|
||||
🔁 This command can also be used with one header and **no data**. In this case, it will resend the last N bytes (based on the header) of the last packet. Until we have a better name, we'll call this **ghost sends**.
|
||||
|
||||
|
|
@ -386,19 +373,17 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
[](img/0x25.png)
|
||||
|
||||
* Send length: N, response length: 0
|
||||
|
||||
* The same as [SendData](#senddata---0x24) but with the additional effect of [Wait](#wait---0x27)
|
||||
* See [Waiting](#waiting) for more details on this.
|
||||
- Send length: N, response length: 0
|
||||
- The same as [SendData](#senddata---0x24) but with the additional effect of [Wait](#wait---0x27)
|
||||
- See [Waiting](#waiting) for more details on this.
|
||||
|
||||
#### ReceiveData - `0x26`
|
||||
|
||||
[](img/0x26.png)
|
||||
|
||||
* Send length: 0, response length: N
|
||||
|
||||
* Responds with all the data from all adapters. No IDs are included, this is just what was sent concatenated together.
|
||||
* Once data has been pulled out, it clears the data buffer, so calling this again can only get new data.
|
||||
- Send length: 0, response length: N
|
||||
- Responds with all the data from all adapters. No IDs are included, this is just what was sent concatenated together.
|
||||
- Once data has been pulled out, it clears the data buffer, so calling this again can only get new data.
|
||||
|
||||
🧩 The data is only concatenated on the host side, and its order is based on the `clientNumber`. It doesn't matter who called `SendData` first.
|
||||
|
||||
|
|
@ -412,32 +397,31 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
- The rest of the bits are `0`.
|
||||
|
||||
🧱 Concatenation is done at **byte** level. So, for example, if client 3 sends 3 bytes (`0xAABBCC`) and client 1 sends 2 bytes (`0xDDEE`), the host would receive 3 words:
|
||||
- (header) `0b0000_00011_00000_00000_00010_0_0000000`, `0xEEAABBCC`, `0x000000DD`
|
||||
|
||||
- (header) `0b0000_00011_00000_00000_00010_0_0000000`, `0xEEAABBCC`, `0x000000DD`
|
||||
|
||||
#### Wait - `0x27`
|
||||
|
||||
[](img/0x27.png)
|
||||
|
||||
* Send length: 0, response length: 0
|
||||
|
||||
* See [Waiting](#waiting) for more details on this.
|
||||
|
||||
- Send length: 0, response length: 0
|
||||
- See [Waiting](#waiting) for more details on this.
|
||||
|
||||
#### DisconnectClient - `0x30`
|
||||
|
||||
[](img/0x30.png)
|
||||
|
||||
* Send length 1, reponse length: 0
|
||||
|
||||
* This command disconnects clients. The argument is a bitmask of the client ID to disconnect. Sending `0x1` means "disconnect client number 0", sending `0x2` means "disconnect client number 1", and sending `0xF` would disconnect all the clients. After disconnecting a client, its ID won't appear on `AcceptConnection` calls and its `clientNumber` will be liberated, so other peers can connect.
|
||||
- Send length 1, reponse length: 0
|
||||
- This command disconnects clients. The argument is a bitmask of the client ID to disconnect. Sending `0x1` means "disconnect client number 0", sending `0x2` means "disconnect client number 1", and sending `0xF` would disconnect all the clients. After disconnecting a client, its ID won't appear on `AcceptConnection` calls and its `clientNumber` will be liberated, so other peers can connect.
|
||||
|
||||
⚡ The clients also are able to disconnect themselves using this command, but they can only send its corresponding bit or `0xF`, other bits are ignored (they cannot disconnect other clients). Also, the host won't know if a client disconnects itself, so this feature is not very useful:
|
||||
* The host still needs to monitor clients to ensure they are still alive (ie. through some PING like mechanism) and disconnect them if they are not, to allow new clients to connect. [4](#pokered)
|
||||
|
||||
- The host still needs to monitor clients to ensure they are still alive (ie. through some PING like mechanism) and disconnect them if they are not, to allow new clients to connect. [4](#pokered)
|
||||
|
||||
#### Bye - `0x3d`
|
||||
|
||||
* Send length: 0, Response length: 0
|
||||
|
||||
* This sets the adapter in a low power consumption mode. Games use it when the player exits the multiplayer mode. To use the adapter again after this command, a new reset/initialization is needed.
|
||||
- Send length: 0, Response length: 0
|
||||
- This sets the adapter in a low power consumption mode. Games use it when the player exits the multiplayer mode. To use the adapter again after this command, a new reset/initialization is needed.
|
||||
|
||||
### Other commands
|
||||
|
||||
|
|
@ -445,58 +429,59 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg
|
|||
|
||||
[](img/0x11.png)
|
||||
|
||||
* Send length: 0, response length: 1
|
||||
|
||||
* This returns the signal level of the other adapters from `0` to `0xFF` (`0` means disconnected).
|
||||
* The levels are returned in a single value, the first byte being the signal level of client 0, and the last byte being the signal level of client 3.
|
||||
* When called from a client, it only returns the signal level of that client in its corresponding byte. The rest of the bytes will be `0`.
|
||||
- Send length: 0, response length: 1
|
||||
- This returns the signal level of the other adapters from `0` to `0xFF` (`0` means disconnected).
|
||||
- The levels are returned in a single value, the first byte being the signal level of client 0, and the last byte being the signal level of client 3.
|
||||
- When called from a client, it only returns the signal level of that client in its corresponding byte. The rest of the bytes will be `0`.
|
||||
|
||||
#### VersionStatus - `0x12`
|
||||
|
||||
* Send length: 0, Response length: 1
|
||||
- Send length: 0, Response length: 1
|
||||
|
||||
* In my adapter, it always returns `8585495` (decimal). It contains the hardware and firmware version of the adapter.
|
||||
- In my adapter, it always returns `8585495` (decimal). It contains the hardware and firmware version of the adapter.
|
||||
|
||||
#### SystemStatus - `0x13`
|
||||
|
||||
* Send length: 0, Response length: 1
|
||||
- Send length: 0, Response length: 1
|
||||
|
||||
* Returns some information about the current connection and device state. The returned word contains:
|
||||
- Bits `0-15`: The device ID (or zero if the device is not connected nor hosting).
|
||||
- Bits `16-23`: A 4-bit array with slots. If the console is a client, it'll have a 1 in the position assigned to that slot (e.g. the one with `clientNumber` 3 will have `0100`). The host will always have `0000` here.
|
||||
- Bits `24-31`: A number indicating the state of the adapter
|
||||
- `0` = idle
|
||||
- `1`/`2` = serving (host)
|
||||
- `3` = searching
|
||||
- `4` = connecting
|
||||
- `5` = connected (client)
|
||||
- Returns some information about the current connection and device state. The returned word contains:
|
||||
|
||||
* Bits `0-15`: The device ID (or zero if the device is not connected nor hosting).
|
||||
* Bits `16-23`: A 4-bit array with slots. If the console is a client, it'll have a 1 in the position assigned to that slot (e.g. the one with `clientNumber` 3 will have `0100`). The host will always have `0000` here.
|
||||
* Bits `24-31`: A number indicating the state of the adapter
|
||||
- `0` = idle
|
||||
- `1`/`2` = serving (host)
|
||||
- `3` = searching
|
||||
- `4` = connecting
|
||||
- `5` = connected (client)
|
||||
|
||||
#### SlotStatus - `0x14`
|
||||
|
||||
* Send length: 0, Response length: 1+
|
||||
- Send length: 0, Response length: 1+
|
||||
|
||||
* It's returns a list of the connected adapters, similar to what `AcceptConnections` responds, but also:
|
||||
- It's returns a list of the connected adapters, similar to what `AcceptConnections` responds, but also:
|
||||
|
||||
- `SlotStatus` has an extra word at the start of the response, indicating the `clientNumber` that the next connection will have (or `0xFF` if the room is not accepting new clients).
|
||||
- `SlotStatus` can be called after `EndHost`, while `AcceptConnections` fails.
|
||||
- `SlotStatus` has an extra word at the start of the response, indicating the `clientNumber` that the next connection will have (or `0xFF` if the room is not accepting new clients).
|
||||
- `SlotStatus` can be called after `EndHost`, while `AcceptConnections` fails.
|
||||
|
||||
#### ConfigStatus - `0x15`
|
||||
|
||||
* Send length: 0, Response length: 7 (as client), or 8 (as host)
|
||||
* Returns the adapter configuration.
|
||||
- Send length: 0, Response length: 7 (as client), or 8 (as host)
|
||||
- Returns the adapter configuration.
|
||||
|
||||
🤔 In my tests...
|
||||
|
||||
- As client, it returned: `0, 0, 0, 0, 0, 0, 257`.
|
||||
- As host, it returned: `1, 2, 3, 4, 5, 6, 3933216, 257`.
|
||||
* `1, 2, 3, 4, 5, 6` would be the broadcast data.
|
||||
* `3933216` is the value used in the [Setup](#setup---0x17) command (`0x003C0420`).
|
||||
* No idea what `257` this means.
|
||||
- `1, 2, 3, 4, 5, 6` would be the broadcast data.
|
||||
- `3933216` is the value used in the [Setup](#setup---0x17) command (`0x003C0420`).
|
||||
- No idea what `257` this means.
|
||||
|
||||
#### RetransmitAndWait - `0x37`
|
||||
|
||||
* Send length: 0, Response length: 0
|
||||
* Retransmits the last data from a host to all clients, with the additional effect of [Wait](#wait---0x27)
|
||||
* See [Waiting](#waiting) for more details on this.
|
||||
- Send length: 0, Response length: 0
|
||||
- Retransmits the last data from a host to all clients, with the additional effect of [Wait](#wait---0x27)
|
||||
- See [Waiting](#waiting) for more details on this.
|
||||
|
||||
### Unknown commands
|
||||
|
||||
|
|
@ -510,16 +495,15 @@ If we analyze whether a command ID throws an 'invalid command' error (`0x996601e
|
|||
- `0x38`
|
||||
- `0x39`
|
||||
|
||||
Waiting
|
||||
-------
|
||||
## Waiting
|
||||
|
||||
[](img/wake-up.png)
|
||||
|
||||
* After either [SendDataWait](#senddatawait---0x25) or [Wait](#wait---0x27), clock control switches to the wireless adapter.
|
||||
* Once the adapter has something to tell the GBA about, the _adapter_ sends a command to the GBA (usually `0x99660028`).
|
||||
* These transfers are dealt with in much the same way as before but with the roles of the GBA and the adapter reversed, see the figure!
|
||||
* The GBA then sends the response back (e.g. `0x996600A8` as `0x28` + `0x80` = `0xA8`).
|
||||
* After this, control of the clock returns to the GBA, and it can start sending commands back again. For example this might be receiving the command sent by the other device using [ReceiveData](#receivedata---0x26).
|
||||
- After either [SendDataWait](#senddatawait---0x25) or [Wait](#wait---0x27), clock control switches to the wireless adapter.
|
||||
- Once the adapter has something to tell the GBA about, the _adapter_ sends a command to the GBA (usually `0x99660028`).
|
||||
- These transfers are dealt with in much the same way as before but with the roles of the GBA and the adapter reversed, see the figure!
|
||||
- The GBA then sends the response back (e.g. `0x996600A8` as `0x28` + `0x80` = `0xA8`).
|
||||
- After this, control of the clock returns to the GBA, and it can start sending commands back again. For example this might be receiving the command sent by the other device using [ReceiveData](#receivedata---0x26).
|
||||
|
||||
⌚ This timeouts after 500ms of the adapter not having anything to tell the GBA about. In this case, the adapter sends `0x99660027`. **This is only true if the console has used the [Setup](#setup---0x17) command before**. The value that most games use (`0x003C0420`) contains this timeout value, but the default is zero (no timeout).
|
||||
|
||||
|
|
@ -528,14 +512,16 @@ Waiting
|
|||
💨 Clients receive the `0x28` when new data from the host is available, but the host receives it immediately (well, after the transfer completes), as it can be used to know which clients received data or are disconnected.
|
||||
|
||||
⚠️ If some children didn't receive the data, the adapter sends to the host GBA a `0x99660128`.
|
||||
- The extra parameter has two bitarrays:
|
||||
* Bits `0-4`: The clients that _received_ data.
|
||||
* Bits `8-11`: The clients marked as _inactive_. This depends on the # of maximum transmissions configured with the [Setup](#setup---0x17) command.
|
||||
|
||||
- The extra parameter has two bitarrays:
|
||||
- Bits `0-4`: The clients that _received_ data.
|
||||
- Bits `8-11`: The clients marked as _inactive_. This depends on the # of maximum transmissions configured with the [Setup](#setup---0x17) command.
|
||||
|
||||
🔗 When the adapter is disconnected from the host, it sends a `0x99660029`.
|
||||
- Bit 8 of the response indicates the reason:
|
||||
* `0` = manual disconnect (aka the host used [DisconnectClient](#disconnectclient---0x30))
|
||||
* `1` = the connection was lost
|
||||
|
||||
- Bit 8 of the response indicates the reason:
|
||||
- `0` = manual disconnect (aka the host used [DisconnectClient](#disconnectclient---0x30))
|
||||
- `1` = the connection was lost
|
||||
|
||||
◀ **Inverted ACKs**
|
||||
|
||||
|
|
@ -548,20 +534,19 @@ While the clock is inverted, the acknowledge procedure is 'standard' but with th
|
|||
5. The adapter goes low when it's ready.
|
||||
6. The adapter starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value.
|
||||
|
||||
Wireless Multiboot
|
||||
------------------
|
||||
## Wireless Multiboot
|
||||
|
||||
> You can learn more details by reading [LinkWirelessMultiboot.hpp](../lib/LinkWirelessMultiboot.hpp)'s code.
|
||||
|
||||
To host a 'multiboot' room, a host sets the **multiboot flag** (bit 15) in its game ID (inside broadcast data) and starts serving.
|
||||
|
||||
- 1) For each new client that connects, it runs a small handshake where the client sends their 'game name' and 'player name'. The bootloader always sends `RFU-MB-DL` as game name and `PLAYER A` (or `B`, `C`, `D`) as player name.
|
||||
- 1. For each new client that connects, it runs a small handshake where the client sends their 'game name' and 'player name'. The bootloader always sends `RFU-MB-DL` as game name and `PLAYER A` (or `B`, `C`, `D`) as player name.
|
||||
|
||||
- 2) When the host player confirms that all players are ready, it sends a 'rom start' command.
|
||||
- 2. When the host player confirms that all players are ready, it sends a 'rom start' command.
|
||||
|
||||
- 3) The host sends the rom bytes in 84-byte chunks.
|
||||
- 3. The host sends the rom bytes in 84-byte chunks.
|
||||
|
||||
- 4) The host sends a 'rom end' command and the games boot.
|
||||
- 4. The host sends a 'rom end' command and the games boot.
|
||||
|
||||
### Valid header
|
||||
|
||||
|
|
@ -637,37 +622,37 @@ enum CommState : unsigned int {
|
|||
- Payload: `0x01`, `0x06`, `0x00`, `0x1A`, `0x00`, `0x00`
|
||||
- Server: ACKs the packet (`size=0, n=1, ph=0, ack=1, commState=1`)
|
||||
- Client: sends `0x00000501`
|
||||
- Header: `0x0501` (`size=1, n=2, ph=0, ack=0, commState=1`)
|
||||
- Payload: `0x00`
|
||||
- Header: `0x0501` (`size=1, n=2, ph=0, ack=0, commState=1`)
|
||||
- Payload: `0x00`
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x00000886`, `0x2D554652`
|
||||
- Header: `0x0886` (`size=6, n=1, ph=0, ack=0, commState=2`) (`2 = COMMUNICATING`)
|
||||
- Payload: `0x00`, `0x00`, `0x52`, `0x46`, `0x55`, `0x2D`
|
||||
- => `RFU-`
|
||||
- Header: `0x0886` (`size=6, n=1, ph=0, ack=0, commState=2`) (`2 = COMMUNICATING`)
|
||||
- Payload: `0x00`, `0x00`, `0x52`, `0x46`, `0x55`, `0x2D`
|
||||
- => `RFU-`
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x424D08A6`, `0x004C442D`
|
||||
- Header: `0x08A6` (`size=6, n=1, ph=1, ack=0, commState=2`)
|
||||
- Payload: `MB-DL`
|
||||
- Header: `0x08A6` (`size=6, n=1, ph=1, ack=0, commState=2`)
|
||||
- Payload: `MB-DL`
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x000008C6`, `0x50000000`
|
||||
- Header: `0x08C6` (`size=6, n=1, ph=2, ack=0, commState=2`)
|
||||
- Payload: `P`
|
||||
- Header: `0x08C6` (`size=6, n=1, ph=2, ack=0, commState=2`)
|
||||
- Payload: `P`
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x414C08E6`, `0x20524559`
|
||||
- Header: `0x08E6` (`size=6, n=1, ph=3, ack=0, commState=2`)
|
||||
- Payload: `LAYER`
|
||||
- Header: `0x08E6` (`size=6, n=1, ph=3, ack=0, commState=2`)
|
||||
- Payload: `LAYER`
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x00410902`
|
||||
- Header: `0x0902` (`size=2, n=2, ph=0, ack=0, commState=2`)
|
||||
- Payload: `A`
|
||||
- Header: `0x0902` (`size=2, n=2, ph=0, ack=0, commState=2`)
|
||||
- Payload: `A`
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x00000C00`
|
||||
- Header: `0x0C00` (`size=0, n=0, ph=0, ack=0, commState=3`) (`3 = ENDING`)
|
||||
- No payload
|
||||
- Header: `0x0C00` (`size=0, n=0, ph=0, ack=0, commState=3`) (`3 = ENDING`)
|
||||
- No payload
|
||||
- Server: ACKs the packet
|
||||
- Client: sends `0x00000080`
|
||||
- Header: `0x0080` (`size=0, n=1, ph=0, ack=0, commState=0`) (`0 = OFF`)
|
||||
- No payload
|
||||
- Header: `0x0080` (`size=0, n=1, ph=0, ack=0, commState=0`) (`0 = OFF`)
|
||||
- No payload
|
||||
- Server: ACKs the packet
|
||||
|
||||
## (2) ROM start command
|
||||
|
|
@ -691,22 +676,18 @@ After all ROM chunks are ACK'd, the last transfers are:
|
|||
- `size=0, n=0, ph=0, ack=0, commState=3` (`3 = ENDING`)
|
||||
- `size=0, n=1, ph=0, ack=0, commState=0` (`0 = OFF`)
|
||||
|
||||
SPI config
|
||||
----------
|
||||
## SPI config
|
||||
|
||||
Here's how SPI works on the GBA:
|
||||
|
||||
[](img/logic2.png)
|
||||
|
||||
I know more!
|
||||
============
|
||||
# I know more!
|
||||
|
||||
If you know any extra details about the wireless adapter, get in touch!. For specific details I’ve left footnotes around if you happen to know that piece of information.
|
||||
|
||||
1. Multiboot is what we call a rom that can be booted over link cable. This can be used for something akin to download play software for the DS. [↩︎](#fnref:multiboot)
|
||||
|
||||
2. [Games compatible with the wireless adapter](https://en.wikipedia.org/wiki/Game_Boy_Advance_Wireless_Adapter#Compatible_games) [↩︎](#fnref:list_of_games)
|
||||
|
||||
3. [Send me an email if you know more about this](https://blog.kuiper.dev/contact)
|
||||
|
||||
4. Some interesting data about the RFU adapter can be found in Pokemon Games, see the [FireRed Decompilation](https://github.com/pret/pokefirered/blob/49ea462d7f421e75a76b25d7e85c92494c0a9798/include/librfu.h#L44) for more information.
|
||||
|
|
|
|||
|
|
@ -133,4 +133,4 @@ void log(std::string text) {
|
|||
tte_erase_screen();
|
||||
tte_write("#{P:0,0}");
|
||||
tte_write(text.c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,4 +77,4 @@ void log(std::string text) {
|
|||
tte_erase_screen();
|
||||
tte_write("#{P:0,0}");
|
||||
tte_write(text.c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,4 @@ class InputHandler {
|
|||
bool isWaiting = false;
|
||||
};
|
||||
|
||||
#endif // INPUT_HANDLER_H
|
||||
#endif // INPUT_HANDLER_H
|
||||
|
|
|
|||
|
|
@ -367,4 +367,4 @@ u32 toMs(u32 cycles) {
|
|||
// CPU Frequency * time per frame = cycles per frame
|
||||
// 16780000 * (1/60) ~= 279666
|
||||
return (cycles * 1000) / (279666 * 60);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,4 +104,4 @@ std::string value(std::string name, LinkGPIO::Pin pin, bool isHigh) {
|
|||
return linkGPIO->getMode(pin) == LinkGPIO::Direction::INPUT
|
||||
? "< " + title + std::to_string(linkGPIO->readPin(pin)) + "\n"
|
||||
: "> " + title + std::to_string(isHigh) + "\n";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export TONCLIB := ${DEVKITPRO}/libtonc
|
|||
|
||||
# === TONC RULES ======================================================
|
||||
#
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# DKP's base_rules and gba_rules
|
||||
#
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
|
||||
%.elf :
|
||||
@echo Linking cartridge
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(NM) -Sn $@ > $(basename $(notdir $@)).map
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
|
@ -69,7 +69,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
%.iwram.o : %.iwram.cpp
|
||||
@echo $(notdir $<)
|
||||
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
%.iwram.o : %.iwram.c
|
||||
@echo $(notdir $<)
|
||||
|
|
@ -148,8 +148,8 @@ bDEBUG2 := 0 # Generate debug info (bDEBUG2? Not a full DEBUG flag. Yet)
|
|||
|
||||
# === BUILD FLAGS =====================================================
|
||||
# This is probably where you can stop editing
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# outside them for example). Removing them sometimes helps
|
||||
|
||||
# --- Architecture ---
|
||||
|
|
@ -203,7 +203,7 @@ endif
|
|||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
|
||||
# Still in main dir:
|
||||
# Still in main dir:
|
||||
# * Define/export some extra variables
|
||||
# * Invoke this file again from the build dir
|
||||
# PONDER: what happens if BUILD == "" ?
|
||||
|
|
@ -238,7 +238,7 @@ export OFILES := $(addsuffix .o, $(BINFILES)) \
|
|||
export INCLUDE := $(foreach dir,$(INCDIRS),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
|
||||
export LIBPATHS := -L$(CURDIR) $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
# --- Create BUILD if necessary, and run this makefile from there ---
|
||||
|
|
|
|||
|
|
@ -131,4 +131,4 @@ void wait(u32 verticalLines) {
|
|||
vCount = REG_VCOUNT;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,4 +36,4 @@ class InputHandler {
|
|||
bool isWaiting = false;
|
||||
};
|
||||
|
||||
#endif // INPUT_HANDLER_H
|
||||
#endif // INPUT_HANDLER_H
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export TONCLIB := ${DEVKITPRO}/libtonc
|
|||
|
||||
# === TONC RULES ======================================================
|
||||
#
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# DKP's base_rules and gba_rules
|
||||
#
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
|
||||
%.elf :
|
||||
@echo Linking cartridge
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(NM) -Sn $@ > $(basename $(notdir $@)).map
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
|
@ -69,7 +69,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
%.iwram.o : %.iwram.cpp
|
||||
@echo $(notdir $<)
|
||||
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
%.iwram.o : %.iwram.c
|
||||
@echo $(notdir $<)
|
||||
|
|
@ -148,8 +148,8 @@ bDEBUG2 := 0 # Generate debug info (bDEBUG2? Not a full DEBUG flag. Yet)
|
|||
|
||||
# === BUILD FLAGS =====================================================
|
||||
# This is probably where you can stop editing
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# outside them for example). Removing them sometimes helps
|
||||
|
||||
# --- Architecture ---
|
||||
|
|
@ -203,7 +203,7 @@ endif
|
|||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
|
||||
# Still in main dir:
|
||||
# Still in main dir:
|
||||
# * Define/export some extra variables
|
||||
# * Invoke this file again from the build dir
|
||||
# PONDER: what happens if BUILD == "" ?
|
||||
|
|
@ -238,7 +238,7 @@ export OFILES := $(addsuffix .o, $(BINFILES)) \
|
|||
export INCLUDE := $(foreach dir,$(INCDIRS),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
|
||||
export LIBPATHS := -L$(CURDIR) $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
# --- Create BUILD if necessary, and run this makefile from there ---
|
||||
|
|
|
|||
|
|
@ -123,4 +123,4 @@ void wait(u32 verticalLines) {
|
|||
vCount = REG_VCOUNT;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export TONCLIB := ${DEVKITPRO}/libtonc
|
|||
|
||||
# === TONC RULES ======================================================
|
||||
#
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# DKP's base_rules and gba_rules
|
||||
#
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
|
||||
%.elf :
|
||||
@echo Linking cartridge
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(NM) -Sn $@ > $(basename $(notdir $@)).map
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
|
@ -69,7 +69,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
%.iwram.o : %.iwram.cpp
|
||||
@echo $(notdir $<)
|
||||
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
%.iwram.o : %.iwram.c
|
||||
@echo $(notdir $<)
|
||||
|
|
@ -148,8 +148,8 @@ bDEBUG2 := 0 # Generate debug info (bDEBUG2? Not a full DEBUG flag. Yet)
|
|||
|
||||
# === BUILD FLAGS =====================================================
|
||||
# This is probably where you can stop editing
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# outside them for example). Removing them sometimes helps
|
||||
|
||||
# --- Architecture ---
|
||||
|
|
@ -203,7 +203,7 @@ endif
|
|||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
|
||||
# Still in main dir:
|
||||
# Still in main dir:
|
||||
# * Define/export some extra variables
|
||||
# * Invoke this file again from the build dir
|
||||
# PONDER: what happens if BUILD == "" ?
|
||||
|
|
@ -238,7 +238,7 @@ export OFILES := $(addsuffix .o, $(BINFILES)) \
|
|||
export INCLUDE := $(foreach dir,$(INCDIRS),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
|
||||
export LIBPATHS := -L$(CURDIR) $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
# --- Create BUILD if necessary, and run this makefile from there ---
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
const { SerialPort, ReadlineParser } = require('serialport') // "^12.0.0"
|
||||
const { SerialPort, ReadlineParser } = require("serialport"); // "^12.0.0"
|
||||
|
||||
var serialPort = new SerialPort({
|
||||
path: "COM9", // (*nix: /dev/ttyACMX, Windows: COMX)
|
||||
baudRate: 9600
|
||||
baudRate: 9600,
|
||||
});
|
||||
const parser = serialPort.pipe(new ReadlineParser());
|
||||
|
||||
parser.on('data', (it) => console.log(it))
|
||||
parser.on("data", (it) => console.log(it));
|
||||
|
||||
setInterval(() => {
|
||||
serialPort.write('<< node\n')
|
||||
}, 1000);
|
||||
serialPort.write("<< node\n");
|
||||
}, 1000);
|
||||
|
|
|
|||
|
|
@ -134,4 +134,4 @@ void waitFor(u16 key) {
|
|||
do {
|
||||
keys = ~REG_KEYS & KEY_ANY;
|
||||
} while (!(keys & key));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
- Reuse code from `examples/LinkCable_full`
|
||||
- Uncomment `USE_LINK_UNIVERSAL` in `src/main.h`
|
||||
- Compile!
|
||||
- Compile!
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
- Reuse code from `examples/LinkCable_stress`
|
||||
- Uncomment `USE_LINK_UNIVERSAL` in `src/main.h`
|
||||
- Compile!
|
||||
- Compile!
|
||||
|
|
|
|||
|
|
@ -36,4 +36,4 @@ class InputHandler {
|
|||
bool isWaiting = false;
|
||||
};
|
||||
|
||||
#endif // INPUT_HANDLER_H
|
||||
#endif // INPUT_HANDLER_H
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export TONCLIB := ${DEVKITPRO}/libtonc
|
|||
|
||||
# === TONC RULES ======================================================
|
||||
#
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# Yes, this is almost, but not quite, completely like to
|
||||
# DKP's base_rules and gba_rules
|
||||
#
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
|
||||
%.elf :
|
||||
@echo Linking cartridge
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(NM) -Sn $@ > $(basename $(notdir $@)).map
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
|
@ -69,7 +69,7 @@ export OBJCOPY := $(PREFIX)objcopy
|
|||
%.iwram.o : %.iwram.cpp
|
||||
@echo $(notdir $<)
|
||||
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
%.iwram.o : %.iwram.c
|
||||
@echo $(notdir $<)
|
||||
|
|
@ -148,8 +148,8 @@ bDEBUG2 := 0 # Generate debug info (bDEBUG2? Not a full DEBUG flag. Yet)
|
|||
|
||||
# === BUILD FLAGS =====================================================
|
||||
# This is probably where you can stop editing
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
|
||||
# up things (gcse seems fond of building masks inside a loop instead of
|
||||
# outside them for example). Removing them sometimes helps
|
||||
|
||||
# --- Architecture ---
|
||||
|
|
@ -203,7 +203,7 @@ endif
|
|||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
|
||||
# Still in main dir:
|
||||
# Still in main dir:
|
||||
# * Define/export some extra variables
|
||||
# * Invoke this file again from the build dir
|
||||
# PONDER: what happens if BUILD == "" ?
|
||||
|
|
@ -238,7 +238,7 @@ export OFILES := $(addsuffix .o, $(BINFILES)) \
|
|||
export INCLUDE := $(foreach dir,$(INCDIRS),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
|
||||
export LIBPATHS := -L$(CURDIR) $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
# --- Create BUILD if necessary, and run this makefile from there ---
|
||||
|
|
|
|||
|
|
@ -470,4 +470,4 @@ void wait(u32 verticalLines) {
|
|||
|
||||
void hang() {
|
||||
waitFor(KEY_DOWN);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,4 +19,4 @@ void interrupt_disable(interrupt_index index) {
|
|||
|
||||
void interrupt_set_reference_vcount(unsigned long y) {
|
||||
IRQ_SetReferenceVCOUNT(y);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,4 @@ void interrupt_enable(interrupt_index index);
|
|||
void interrupt_disable(interrupt_index index);
|
||||
void interrupt_set_reference_vcount(unsigned long y);
|
||||
|
||||
#endif // INTERRUPT_H
|
||||
#endif // INTERRUPT_H
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user