The Let's Play Archive

EXAPUNKS

by Carbon dioxide

Part 25: Sawayama WonderDisc

Part 25 - Sawayama WonderDisc

=== Trash World Inbox ===

silentsnack posted:

code:
LINK 800
COPY -3 X

MARK INPUT
LINK X
LINK X
COPY #NERV T
MULI X -1 X
LINK X
LINK X
MARK OUTPUT
LINK X
REPL INPUT
LINK X
REPL OUTPUT
COPY T #NERV
JUMP INPUT
274/16/548. Each input/output pair is separated by 4, 6, or 8 jumps (sending output at 2 doesn't work because the P-HND signal also hits M-HND) and to make the cycle hit all 3 inputs without saturating we have to rely on the 5 jumps between M-CNS and H-HND, then it can also go one further to P-HND
Wow. That's very clever but also hard to follow code. I don't even know how to simply explain it in my own words. Basically, the same EXAs are doing double duty and the different REPLs make sure at least one ends up in the right place in the right time. The wrong ones happen to be harmless.

As for speed:

silentsnack posted:

Here we can do some shenanigans that rely on something from the zine: "nerve voltages oscillate around -70mV but can spike up to 50 or down to -120" but here if we look at the testdata M shows all the characteristics but I think this is the first time we've seen sensory nerves like H and P that always stay near -70. Why is that useful? It means they're all 2-digit numbers and always have the same sign, but EXAs can handle 4-digit variables, at which point you can probably guess where this is going...
code:
;XA
LINK 800
LINK -3
LINK -3

MARK M_LOOP
REPL NERV
JUMP M_LOOP

MARK NERV
COPY #NERV X
COPY #NERV T

@REP 4
LINK 3
@END
COPY X #NERV
COPY T #NERV

;XB
;NOOP
LINK 800
LINK 3
LINK 3
LINK 3
REPL HND_LOOP
LINK 3

MARK HND_LOOP
REPL NERV
JUMP HND_LOOP

MARK NERV
MULI #NERV 100 X
ADDI X #NERV M

;XC
;NOOP
;NOOP
LINK 800
LINK -3
LINK -3
LINK -3
REPL CNS_LOOP
LINK -3

MARK CNS_LOOP
NOOP
REPL CNS_LOOP

COPY M X
SWIZ X 43 #NERV;DIV  100
SWIZ X 21 #NERV;MOD -100
25/39/47 if we comment out the NOOPs and just assume the EXAs start moving in order of XA,XB,XC
This looks quite similar to my fastest solution. There's two differences: the first is that XA (the one moving M-CNS data) has REPLs running back and forth instead of my file solution. By storing signals in both X and T and using REPLs this is much faster, and puts the bottleneck on the M solution. The second, as silentsnack hinted, is by compressing two nerve signals into one M signal. Since the compression/decompression arithmetic takes slightly longer, it uses REPLs to make sure a signal goes through every cycle, doubling the signal speed.

About my 37 cycle solution:

GuavaMoment posted:

Very easy to tweak this to save two cycles - have XA do a file copy of 7 length instead of 14, replicate an exa to do it again, and transfer 7 digits over at a time. It's a little faster because now you have two exas who can read and copy values at the same time instead of one doing everything. 35/56/21
I noticed when I tried a hybrid of my solution and silentsnack's, that once you hit 35 the bottleneck shifts from the M-CNS to the other EXAs. So, your simple change basically optimizes XA as much as possible in my solution.


There was also an interesting discussion in the thread about learning from each other's solutions. Yes, I've been doing that during this LP as well, many of your ideas went into later puzzle solutions. I don't remember all your ideas nor do I know how to apply some of them in specific situations, but it has helped me come up with better solutions overall.


=== Sawayama WonderDisc - Drive Controller ===

Last time, I fixed my hand.

Do you ever feel like it's a losing battle?
This constant effort to maintain your physical body.




One vote for "stay in the moment", and if I interpreted the votes correctly, three for "everyone eventually loses".

Everyone eventually loses.

I wonder if I'm the same way.
Only time will tell.




Ah, yes, definitely the music industry. I had nothing to do with this.



So, to play that game Isadora got me, I'll need to disable the region lock on the Sawayama.

WonderDisc games are restricted by region locks.
Why?




Two votes for "business reasons" and also two for "big corporations". Time to get out the two-sided die again.

Business reasons, I guess.

Artificial scarcity?
You wouldn't want gamers importing titles too easily.
The less effort it takes, the less special it is.
Hmm.



OST: Getting Started

Let's see what I have to do.
- Modify your WonderDisc, which normally only plays SSEA region games, to play games from any region.
- The SSEA region code is available in file 300.
- It is not necessary to leave no trace. Your EXAs should be written to operate indefinitely.
- For more information see "Hardware Hacks: Sawayama WonderDisc" in the second issue of the zine.




Okay, so make a copy of whatever it needs from the disc but overwrite its region code. But first I need to unlock the system using that key in the zine.

Let's start with that then, so I can see what I'm dealing with.

code:
LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH
This is going to be the fastest way to unlock it. No intermediate files or anything, just hardcode the key.

The key gets filled in on that display in the DISC host, and... I'm in.



A whole lot of track files. Remember, the little lock icon after the file id means you can't move or change them (which makes sense for read-only game discs). The files contain four-digit numbers, with the region code interspersed. I have to find the right track, make a copy in the buffer host with the updated region code, and repeat.

I am going to need the M register no matter what, because copying a file with a single EXA would require dropping it and picking up the copy repeatedly. So let's just get the data to the BUFFER host directly.

Replacing the region code is a bit tricky because it requires fiddling with the limited set of EXA registers. I'll first get the file copy code itself working. After unlocking the disc:

code:
MARK NEXT
REPL WRITER

COPY #TRAK X
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP

MARK WRITER
LINK 800
MAKE
MARK WRLOOP
COPY M F
NOOP
TEST MRD
TJMP WRLOOP
MARK END
DROP
LINK -1
JUMP NEXT
Send a writer to the BUFFER. Then the main EXA reads the track number, goes to the disc, and starts copying. It'll die when it reaches EOF. The writer needs to know when it's done (otherwise it'll get stuck waiting for an M signal that never comes), and uses TEST MRD for that. Since it takes a cycle between M signals an extra NOOP is required, making that loop a bit slow, but it works. When it's done, it drops the file, goes back to the central debug host, and starts over again.





As soon as a file is dropped in the BUFFER, this unusual black EXA comes out of the Reality Processor, grabs the file, and takes it in. The processor notices the file has the wrong region and it'll throw an error.

But... that makes me wonder, can I mess with that black EXA and just take over entirely?



Turns out you can kill it, but this fails the assignment.
Doing so nets us the DISC_READ_ERROR steam achievement, with description "It was just doing its job...". Now I'm feeling bad.

Anyway, the final thing I need to do is overwrite the region code. Now, I considered doing that while copying the file. But that's hard. You can't check directly against M because that will "use up" M. Using X as an intermediate won't work either, because I need a place to store the new region code. And I can't use T because I need to test if a value needs replacing. Let's instead go through the file a second time to change the code.



Ignore that useless HOST command, I just wanted to know what that host was called.

To save a cycle, XB reads the region code into M and XA stores that into X once the disc is unlocked. Once the writer is done, it will REPL a new reader/writer, while it holds on to the file and overwrites the region code with the new one in X. Note that TEST F > -9999 can be used to test if something is text. This test will be true for any valid number (except -9999 but that doesn't occur in these files), but comparisons between text and numbers always return false.

Let's run it.



Hm. Looks like when a small file follows a big file, the small file is sometimes processed before the big one, and the Sawayama doesn't like getting tracks out of order.

An easy fix would be to only handle one file at one but I don't want to slow the code down that much. You know, I'm basically using F as an intermediate register now, can't I inline the region update anyway?

code:
;XA

LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH
COPY M X

LINK 800

MARK NEXT
LINK -1
REPL WRITER

COPY #TRAK X
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP

MARK WRITER
LINK 800
MAKE

MARK WRLOOP
COPY M F
SEEK -1
TEST F > -9999
FJMP OVERWRITE
TEST MRD
TJMP WRLOOP
DROP
JUMP NEXT

MARK OVERWRITE
SEEK -1
COPY X F
TEST MRD
TJMP WRLOOP
DROP
JUMP NEXT

;XB

GRAB 300
COPY F M
This is my first working solution. After every write to F, it jumps back one in the file and tests if it's the region code. If so, it'll overwrite it. Either way, it tests if there's more data waiting, if so it goes back to the loop, otherwise it drops the file and starts processing the next.



At 6487/48/94 the solution is rather slow. The top percentiles are 3293, 44 and 3. What's interesting is how far the tenth percentiles are from those numbers: 5584 for cycles and 63 for activity. This shows less people got an actual top score, so optimizing is getting much more difficult.

Anyway, I'm not that far from the low size top score.

To get to 45 I can replace the duplicate TEST MRD code with a jump, and then move the OVERWRITE elsewhere so the EXA dies by reaching the end of the code. If I do that, I can replace the DROP/JUMP with a REPL.

code:
;XA 
LINK 800
; AUTH CODE

COPY M X

LINK 800

MARK NEXT
LINK -1
REPL WRITER

COPY #TRAK X
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP

MARK OVERWRITE
SEEK -1
COPY X F
JUMP BACK

MARK WRITER
LINK 800
MAKE

MARK WRLOOP
COPY M F
SEEK -1
TEST F > -9999
FJMP OVERWRITE
MARK BACK
TEST MRD
TJMP WRLOOP
REPL NEXT
To save that last line, I had to stop thinking from the middle host and start thinking from the buffer. Move the LINK -1 from after the MARK NEXT to below the REPL WRITER. By doing so, the LINK 800 after MARK WRITER can be deleted. 6584/44/64

Wait a second, now that the OVERWRITE is a jump and then a jump back, can't I inline that too and just skip if no overwrite is necessary?

code:
MARK WRLOOP
COPY M F
SEEK -1
TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F

MARK SKIPOVERWRITE
TEST MRD
TJMP WRLOOP
REPL NEXT
6459/42/64, not only under the top percentile for size but also the lowest cycle count I got so far.

I don't think I can lower the size much more, so let's focus on the cycle count. Unrolls!

There's no point unrolling the reader by itself because the writer needs to do all the checks and is slower anyway. The files seem to be between 6 and 36 entries in size, always a multiple of 6. I can use that but unrolls in combination with jumps (for the overwrite) are always complex.



This solution uses the advanced REP syntax. The @{0,1} means "fill in zero for the first repetition, then 1 for the next, and keep incrementing by one." So I have a MARK SKIPOVERWRITE0, MARK SKIPOVERWRITE1 and so on in the unrolled result. This way, with the jumps it doesn't lose its place in the unroll, so no matter what, it checks for MRD every 6 repetitions. 4799/77/64.

For my next improvement I have a completely different idea. Can I parallellize the file copying? That requires the M register which is quite occupied... except we also have LOCAL mode.

You might think that won't fit in the DISC host but it does. One EXA in global mode can grab a file. Then a second EXA in local mode can grab a second file, and then a third one can use the one empty spot in the DISC to make a new file. I had some code that seemed like it would work but it got stuck when the Sawayama requested the same track twice in a row and the slowest EXA couldn't find it because the fastest was holding it.

I went through several complete rewrites before ending up with something that actually works.

It still isn't close to the top percentile but honestly, after being at it for several hours I consider this Good Enough. I'll start with XB, which has two purposes. The top half:

code:
;XB LOCAL

GRAB 300
COPY F X
DROP
LINK 800

REPL WRITER

MAKE
COPY #TRAK F

MARK ROUND
COPY #TRAK F

SEEK -2
TEST F = F

SEEK -2
COPY F M

TJMP SKIPLOCAL
COPY F M
COPY X M
COPY #TRAK F

JUMP ROUND

MARK SKIPLOCAL
COPY 0 M
SEEK 1
JUMP ROUND
First it writes the region code to X, then it goes to the DEBUG (center) host and starts reading the #TRAK register two values at a time. If they are not equal, it sends both to local M, followed by a reminder of the region code. If they are equal it sends only one, followed by a zero. In that case it does a SEEK to get to the end of the file and only reads ONE value from #TRAK. You'll see how the other EXA deals with this. First, the second part of XB:

code:
;XB cont'd

MARK WRITER
MODE
LINK 800

MARK WRITEFILE
MAKE

MARK WRLOOP

COPY M F
SEEK -1
TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F
MARK SKIPOVERWRITE

TEST MRD
TJMP WRLOOP
DROP
MODE
VOID M
MODE
JUMP WRITEFILE
Thw WRITER REPL works the same as before. This EXA creates it because it has time to spare while the disc is being unlocked. It listens to M in GLOBAL mode, and once it's done it waits for a LOCAL M message before starting the next file.

code:
;XA LOCAL

LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH

MARK ROUND
COPY M X
REPL GLOBALREADER
COPY M T
TJMP LOCALRW
LINK 800
COPY 0 M
LINK -1
JUMP ROUND
The first part of XA handles the unlock, as before. Then it reads two values from LOCAL M. The first is used by the GLOBALREADER which I'll show in a bit. The second causes a jump to LOCALRW but only if it's not zero. If it is zero (XB detected a duplicate request), instead if skips that step, and goes to the buffer to send the local message to the WRITER. This is just to sync them up again. It won't do anything until the first half of the duplicate request is completed and WRITER waits for new input.

code:
;XA cont'd

MARK GLOBALREADER
MODE
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP
The GLOBALREADER is nothing new. It switches to GLOBAL mode, grabs its file, sends the data to the WRITER and dies automatically at the EOF.

code:
;XA cont'd 

MARK LOCALRW
COPY M X
LINK 801
GRAB T
REPL LOCALWRI
JUMP RDLOOP

MARK LOCALWRI
MAKE

MARK LOCALWRLOOP
COPY M F
SEEK -1
TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F
MARK SKIPOVERWRITE

TEST MRD
TJMP LOCALWRLOOP
LOCALRW stays in local mode. It first gets the region code again from M (which is why XB sends it again), because the earlier stuff overwrote it. It then jumps to the DISC (importantly, after the GLOBAL one did and grabbed its file, otherwise there wouldn't be enough space), grabs its file, opening up a single space for the LOCALWRIter which is created. It then jumps into the standard read loop. The local writer works the same as the global writer, except it runs in the DISC host. Once its file is written, it runs the last bit of code:

code:
;XA cont'd

LINK -1
LINK 800
COPY 0 M
DROP
LINK -1
JUMP ROUND
Walk the file to the buffer, wait for the GLOBAL one to be done (so the tracks are delivered in the correct order), DROP its file, LINK back to the DEBUG host and start the next round of files.

So, to summarize:
- XB checks if two files can be handled at once (different IDs). If so, XA puts them both to work. Once one of them is done, it has to wait for the other so that everything stays in sync.
- If the Sawayama wants the same track twice, only the GLOBAL reader runs, slowing down the process but making sure it stays in sync.

The result is 3830/98/81 and I'm just glad I got a sub-4000 cycle count. There's no space left for unrolls since the max allowed size is 100.


Finally, the 3-activity solution is tricky, but doable.

The issue is that since you can't move EXAs around, the M register has to do a lot of duties at the same time. So I have to be smart about it.

I wrote a solution but didn't bother it optimizing for anything but activity so forgive me for my ugly code.
code:
GRAB 300
COPY F X
DROP

LINK 800

REPL WRITER

COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH

REPL READER

MARK TRAK
COPY #TRAK M
COPY 190 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
JUMP TRAK


MARK READER
LINK 801

MARK READNEXT
GRAB M


MARK RDLOOP
COPY F M
TEST EOF
FJMP RDLOOP
DROP
JUMP READNEXT


MARK WRITER
LINK 800

REPL MCONTROLLER

MODE
MARK WRITEFILE
MAKE

MARK WRLOOP


COPY M F
SEEK -1
TEST F = 0
TJMP DONE
SEEK -1

TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F
MARK SKIPOVERWRITE
JUMP WRLOOP

MARK DONE
SEEK -1
VOID F
DROP
JUMP WRITEFILE


MARK MCONTROLLER
COPY M X
TEST X < 300
TJMP GLOBAL
MODE
COPY X M
MODE
TEST MRD
FJMP NEXTFILE
JUMP MCONTROLLER

MARK GLOBAL
COPY X M
TEST MRD
FJMP NEXTFILE
JUMP MCONTROLLER

MARK NEXTFILE
MODE
COPY 0 M
MODE
JUMP MCONTROLLER
It runs at 11542/81/3.

It starts with unlocking the disc like normal, and REPLing a single writer and reader. The reader is still quite simple: grab the file id which is received through M, then send all data through M, but specifically check for EOF and read the next file when it's done.

The tricky part is in the WRITER. It runs in LOCAL mode. There's an M-CONTROLLER EXA that receives all GLOBAL M messages. If it's a number under 300 (a file ID), it sends it on global M again because it was actually intended for the reader but the controller happened to intercept it. If it's anything else, the WRITER needs it, so the MCONTROLLER forwards it in LOCAL mode. Finally, if nobody is sending, the controller sends a 0 to the WRITER to let it know the file is done.

So, how do you tell the EXA reading from #TRAK that it should send a new value? That's the neat part, you don't. If you try it with M, all EXAs will intercept messages from each other and you'll end up in unpredictable inescapable loops. Instead, that EXA just has a very long countdown (190 two-cycle iterations) so it waits long enough for even the biggest file to be done.

The countdown and all M messages having to go through the controller make this solution slow.

That was a lot of work just to play one game.
Think it will be worth it?




And that brings us to our first vote.

Once again, we unlocked a special minigame. This time it isn't Solitaire. It is called HACK*MATCH.

Think the console version will live up to the arcade classic?



Here's the second vote. We'll check out the minigame next time.