diff options
author | HombreLaser <buran@silosneeded.com> | 2025-07-26 22:25:28 -0600 |
---|---|---|
committer | HombreLaser <buran@silosneeded.com> | 2025-07-26 22:25:28 -0600 |
commit | e288e3ad95874b6f38aff782195eaa2b0543cb4e (patch) | |
tree | 16bd575386763243b401cf99603a129f206e036b | |
parent | a4bc34f3977980009e7675369e7dad22118ced61 (diff) |
Add picoshock devlog #3
-rw-r--r-- | _posts/en/2025-07-26-picoshock_devlog_3.md | 164 | ||||
-rw-r--r-- | assets/images/2025/pulseview.jpg | bin | 0 -> 185366 bytes | |||
-rw-r--r-- | assets/images/2025/second_cycle.jpg | bin | 0 -> 134008 bytes | |||
-rw-r--r-- | assets/images/2025/successful_output.jpg | bin | 0 -> 201287 bytes |
4 files changed, 164 insertions, 0 deletions
diff --git a/_posts/en/2025-07-26-picoshock_devlog_3.md b/_posts/en/2025-07-26-picoshock_devlog_3.md new file mode 100644 index 0000000..205bca4 --- /dev/null +++ b/_posts/en/2025-07-26-picoshock_devlog_3.md @@ -0,0 +1,164 @@ +--- +layout: post +title: "PicoShock Devlog #3" +lang: en +date: 2025-07-26 +tag: ["Programming", "PicoShock"] +--- + +Well, it's been a while. Since [last time](https://silosneeded.com/2025/04/picoshock_devlog_2) I had success reading +the data the Playstation 2 was sending me. After that, I took the logical next step: sending data back. That was my first +block. + +After a couple of days of banging my head against the wall I took a rest that prolonged itself more than it should have. +Just today I got back at it. And I think I've made a respectable advancement, but with a twist. Because nothing can +be as simple as I'd like things to be. + +# First off: debugging + +So the problem was knowing what in God's name I was actually putting on the line. That's when I got into the rabbit hole of logic analyzers. +I came across a couple of pretty interesting projects: [sigrok](https://sigrok.org), its GUI, [pulseview](http://sigrok.org/wiki/PulseView) and +a driver for using a raspberry pi pico as the analyzer: [sigrok-pico](http://sigrok.org/wiki/PulseView). + +Installing these tools is outside of the scope of this article, but the only thing worth mentioning here is that compiling sigrok and pulseview +oneself is necessary if you pretend to use a raspberry pi pico as your logic analyzer. I remember struggling with that but in the end it was +possible. + + + +# Taking a peek at the output line + +With sigrok I could actually see the output I was sending. And it was nonsense, for the most part. But something functional reared its head from time +to time. Let's remember the table of bytes I was receiving, and the ones I'd like to send in response: + +|Byte Number|Received|Sent| +|-----------|--------|----| +|1 |0x01 |0xff| +|2 |0x42 |0x41| +|3 |0x00 |0x5a| +|4 |0x00 |0xff| +|5 |0x00 |0x40| + +Bytes 4 and 5 are entirely mine, so to speak: the playstation 2 drives its line low and expects two bytes from my end. In these bytes is where the magic happens: +mode setting, button presses... In this example I want to send a button press, the cross button if I remember correctly. This button corresponds to the seventh +bit of the second byte: in binary, 0100 0000. In hex, 0x40. If there's nothing pressed then I must send all 1s (0xff). + +What was strange about the data I was sending is, well, first, that it was wrong, but not every time. Sometimes, a correct byte was sent, but as the third or +fourth one for example, instead of its intended order. This also meant that the preceding and following bytes were gibberish, well, not TOTAL gibberish, they +sometimes differed from the actual byte I wanted to send for just a bit. For example, I wanted to send 0x5a (0101 1010 in binary) but the byte that was being sent +was 0xda (1101 1010). Pretty mystical isn't it? + +# First steps towards actually fixing this: the C portion + +After some time suffering with this and posting a help thread on the forums I decided to revisit my bible on this project, yes, the +[curious inventor's guide](https://store.curiousinventor.com/guides/PS2). + +The paragraph that caught my eye was this one: + +> The play station sends a byte at the same time as it receives one (full duplex) via serial communication. + +I already knew this, but I'm such a newbie on this that I actually pondered on this. This means that the TX FIFO must have the byte I want to send BEFORE +I consume the RX FIFO byte, i.e. I must send my 0xff byte before I consume the 0x01 the console sends me. The same for every byte. I do have to pattern match +for the second byte, as that is the command (poll, configure...) but MY second byte is independent from the one from the console. The next bytes will be the ones +that will act as the response to the console's command. + +So I decided to change my main loop, from pattern matching the bytes I was receiving to jamming the output FIFO with my data. When I start writing the library +proper I will have to add logic checking for the data I'm receiving, but for now I want to emulate the base case exposed in the guide. + +```c +while (true) { + // The TX FIFO will have data ready to simultaneously send while + // the RX FIFO is being emptied. + pio_sm_put_blocking(data_pio, data_sm, (0xff ^ 0xff)); + data = pio_sm_get_blocking(pio, sm) >> 24; + pio_sm_put_blocking(data_pio, data_sm, (0xff ^ 0x41)); + data = pio_sm_get_blocking(pio, sm) >> 24; + + // Receiving all zeros. Send button presses. + pio_sm_put_blocking(data_pio, data_sm, (0xff ^ 0x5a)); + uint32_t buf = pio_sm_get_blocking(pio, sm) >> 24; + + if (buf != 0x00) + continue; + + pio_sm_put_blocking(data_pio, data_sm, (0xff ^ 0xff)); + buf = pio_sm_get_blocking(pio, sm) >> 24; + + if (buf != 0x00) + continue; + + pio_sm_put_blocking(data_pio, data_sm, (0xff ^ 0x40)); + + printf("%x\n", data); +} +``` + +# A small change in the PIO program + +In my previous version of my PIO program, after sending my byte, the program looped to the `set pindirs 0` instruction: + +```asm +.program dualshock_data + + .wrap_target + set pindirs 0 +wait_for_output: + jmp !osre wait_for_att ;; Stall while we don't have data to output + jmp wait_for_output + +wait_for_att: + set x, 7 ;; Set bit counter: we want to send a byte. + wait 0 pin 0 ;; Wait for ATT. + +out_loop: + wait 0 pin 1 ;; When clock goes from low to high, + wait 1 pin 1 ;; send our data. + out pindirs, 1 ;; Send a bit to the PS2. + jmp x-- out_loop ;; Decrement bit counter. +.wrap +``` + +I told myself, well, isn't it more appropriate to jump to the label that waits for more data to be present in the TX FIFO than to let the program wrap? +I wasn't thinking about the importance of the `set pindirs 0` (for those not in the loop, this instruction drives the data pin high, as it is an open collector +output... now that I'm reading my program's config... does having the data pin as a SET pin makes sense? Will check in the future) so I decided to jump to +`wait_for_output` instead after every byte: + +```asm +out_loop: + wait 0 pin 1 ;; When clock goes from low to high, + wait 1 pin 1 ;; send our data. + out pindirs, 1 ;; Send a bit to the PS2. + jmp x-- out_loop ;; Decrement bit counter. + jmp wait_for_output ;; We sent the byte; let's check if there's another one to send. +``` + +What came after was a pretty big surprise + +# Now (it seems) I'm sending what I actually want to send + +After these changes, it seems I've made some progress. I say that it seems because I have no way of actually verifying it as for now I'm not capable of seeing what's +going on the Playstation 2, I gave the monitor I was using to test this to a friend and started using another one I had on a corner. But it doesn't like +to receive a 1080p resolution image. The other one wasn't compatible either but at least showed something, so I'm pretty much blind: I can only peek at my logic +analyzer. + +And this is what the logic analyzer shows me: + + + +If we see the data line, and count the bits (on the rising edge of the clock, that's the moment when the Playstation 2 reads our bit), those are the actual bytes +I want to send. But... there's a caveat. The data line is an open collector output, this means that it's normally held high as it is the console that actually puts +the voltage on the line, not us. I'm not entirely sure of the consequences of the line being by default low, as 0 is the last bit of our last byte. And I can't +see the playstation's screen so I'm not sure if it's actually listening to me and registering the button press I want it to register. + +So, for the next time around... + +# What comes next + +The reason for the data pin to be a set pin (and also an output pin) is that that was the recommendation I was given on the forums. When it is a set pin, I can +drive it high or low at convenience. + +So, what I must do is look for a compatible monitor or small CRT TV to do my testing and know if, once and for all, I'm doing something correctly for a change, +and the console is registering my button press. If it's not doing that, maybe the fact that the line is pulled low and stays low after every packet has +something to do with it. + +Well, see you next time! diff --git a/assets/images/2025/pulseview.jpg b/assets/images/2025/pulseview.jpg Binary files differnew file mode 100644 index 0000000..8145626 --- /dev/null +++ b/assets/images/2025/pulseview.jpg diff --git a/assets/images/2025/second_cycle.jpg b/assets/images/2025/second_cycle.jpg Binary files differnew file mode 100644 index 0000000..4ed30e6 --- /dev/null +++ b/assets/images/2025/second_cycle.jpg diff --git a/assets/images/2025/successful_output.jpg b/assets/images/2025/successful_output.jpg Binary files differnew file mode 100644 index 0000000..1bdfb84 --- /dev/null +++ b/assets/images/2025/successful_output.jpg |