I CAN SEE EVERYTHING

I CAN SEE EVERYTHING

by ferris

This week I did a bunch of stuff :)

For one thing, I spent some more time in VICE reverse-engineering that EoD plasma again, and with great success. I figured out what I believe is the selection of hardware tricks/graphics mem layouts to make the thing tick. The effect has two main bits, the plasma and the sprite zoomer on top; I've ignored the sprite zoomer, and just focused on the plasma.

The short version is something like this:

  • The plasma is displayed using char FPP, where one of 8 different 144x1 lines of graphics (making up a 144x8 "base picture") is chosen and displayed for each of the 200 lines in the visible screen.
  • The left/right borders around the plasma are simply displayed using hardcoded chars; there are no sprites visible over the plasma (except for the zoomer, where a separate FPP routine is used and what appears to be a constant number of sprites, which I don't plan on digging into). The top/bottom borders appear to be sprite-based, but may actually be done by changing background colors; this is not very important other than they're not part of the char area.
  • The base 144x8 plasma picture that's stretched using FPP changes each frame; this is done by updating the 38 bytes of screen mem (char indexes). It doesn't look like any updates are done on a per-pixel level.
  • The screen mem used (only the first line is used) starts at 0x3c00. The hardware reads this line, along with all of the color data, at the top of the screen. Before the FPP starts doing its thing, the bank is switched. The FPP repeats the last line of each character, and the character mem is changed per-line. Since the bank is switched between reading screen/color mem and reading char mem, these regions don't actually overlap. This also means that the screen/color mem can't change throughout the screen, but it's sufficient to simply change char graphics. This FPP technique is explained here.

While there are still some details left, I believe this is what's going on based on what I've seen and the techniques I'm aware of. I discovered most of this through a painstaking process of trial-and-error poking at things in the monitor. A lot of the code that makes this effect work is in the form of large, unrolled loops which can be a bit hard to sift through, but the answers are all in there!

A couple things that fooled me for a long time were 1. that the left/right borders were not drawn using sprites (which explains a lot of the raster timing) and 2. that there are only 8 lines of graphics in the "base picture" that's stretched by the FPP. This simplifies the base picture update immensely and allows for an FPP routine that leaves a lot of extra raster time available (which is why it can work with the sprite zoomer on top), as long as you can make only 8 lines/frame look good, which is another challenge :)

The real breakthrough for figuring this out was to watch the video bank and screen mem registers (bits of $dd00 and $d018), and how they change per scanline. Once I did that, I was able to figure out where different parts of graphics data should be. From there I started clearing large chunks this memory, and lines started disappearing :) Once I got down to 4 lines, it became obvious that there were only 4 left (as you can see from the screenshot). You can see the rest of the detailed notes I took You can see my full notes here, along with some helpful emulator save states with the plasma effect isolated etc. As I said before, the number of lines really threw me for a loop. This may seem insignificant, but the line limit really ties a lot of the other tricks/mem layout things together, and it took a bit of poking around to confirm what was really going on and have it all "click" in my head. It really looks like there are a lot more, so I'll have to dig into the tables it's using to select lines a bit more, but I'll hold off for a bit and work on other things I think :)

Another thing I started was a Virtual Boy emulator in Rust. This is a system I've had my eye on for awhile and bought somewhat recently, and some of my colleagues have also recently been bitten by the emulator bug, so I thought I'd dive in a bit too after not touching rustendo64 for so long. This was a pretty fun thing; after doing so much of the reverse-engineering stuff in VICE's monitor, I decided I'd change up my approach a bit and start there for the emulator rather than diving straight into CPU execution. This turned out to be a great idea, as I was able to actually look at code before executing, and design the emulator architecture to naturally support breakpoints and introspection (among some other helpful things like labels). I haven't gotten to any hw other than the ROM/RAM/CPU yet and a few hw reg's, but that comes next :) For now, you can see this beautiful monitor session dump:

Loading ROM file ../vb-rs-corpus/Teleroboxer (Japan, USA).vb
ROM size: 1MB
Header info:
 name: "TELEROBOXER         "
 maker code: "01"
 game code: "VT"
 game version: 1.00
(vb-rs 0xfffffff0) > d
  0xfffffff0  20bcf6ff    movhi 0xfff6, r0, r1
  0xfffffff4  21a00a7f    movea 0x7f0a, r1, r1
  0xfffffff8  0118        jmp [r1]
  0xfffffffa  ffffffff    out.w -1[r31], r31
(vb-rs 0xfffffffe) > ab fff67f0a
(vb-rs 0xfffffffe) > c
* 0xfff67f0a  0570        ldsr r0, psw
(vb-rs 0xfff67f0a) > d 30
* 0xfff67f0a  0570        ldsr r0, psw
  0xfff67f0c  0078        sei
  0xfff67f0e  20bc0105    movhi 0x501, r0, r1
  0xfff67f12  61a0fcff    movea 0xfffc, r1, r3
  0xfff67f16  20bc0105    movhi 0x501, r0, r1
  0xfff67f1a  81a00080    movea 0x8000, r1, r4
  0xfff67f1e  20bc0100    movhi 0x1, r0, r1
  0xfff67f22  41a1ffff    movea 0xffff, r1, r10
  0xfff67f26  6141        mov 1, r11
  0xfff67f28  4b09        sub r11, r10
  0xfff67f2a  fe95        bnz 0x1fe (0xfff67f28)
  0xfff67f2c  40bd0005    movhi 0x500, r0, r10
  0xfff67f30  6acd0000    ld.w 0[r10], r11
  0xfff67f34  6acd0000    ld.w 0[r10], r11
  0xfff67f38  6acd0000    ld.w 0[r10], r11
  0xfff67f3c  6acd0000    ld.w 0[r10], r11
  0xfff67f40  6acd0000    ld.w 0[r10], r11
  0xfff67f44  6acd0000    ld.w 0[r10], r11
  0xfff67f48  6acd0000    ld.w 0[r10], r11
  0xfff67f4c  6acd0000    ld.w 0[r10], r11
  0xfff67f50  60bd0100    movhi 0x1, r0, r11
  0xfff67f54  8441        mov 4, r12
  0xfff67f56  0adc0000    st.w 0[r10], r0
  0xfff67f5a  4c05        add r12, r10
  0xfff67f5c  6c09        sub r12, r11
  0xfff67f5e  f895        bnz 0x1f8 (0xfff67f56)
  0xfff67f60  40bd0002    movhi 0x200, r0, r10
  0xfff67f64  6041        mov 0, r11
  0xfff67f66  6ad10000    st.b 0[r10], r11
  0xfff67f6a  20bc0002    movhi 0x200, r0, r1
(vb-rs 0xfff67f6e) > ab fff67f2c
(vb-rs 0xfff67f6e) > c
* 0xfff67f2c  40bd0005    movhi 0x500, r0, r10
(vb-rs 0xfff67f2c) > d 20
* 0xfff67f2c  40bd0005    movhi 0x500, r0, r10
  0xfff67f30  6acd0000    ld.w 0[r10], r11
  0xfff67f34  6acd0000    ld.w 0[r10], r11
  0xfff67f38  6acd0000    ld.w 0[r10], r11
  0xfff67f3c  6acd0000    ld.w 0[r10], r11
  0xfff67f40  6acd0000    ld.w 0[r10], r11
  0xfff67f44  6acd0000    ld.w 0[r10], r11
  0xfff67f48  6acd0000    ld.w 0[r10], r11
  0xfff67f4c  6acd0000    ld.w 0[r10], r11
  0xfff67f50  60bd0100    movhi 0x1, r0, r11
  0xfff67f54  8441        mov 4, r12
  0xfff67f56  0adc0000    st.w 0[r10], r0
  0xfff67f5a  4c05        add r12, r10
  0xfff67f5c  6c09        sub r12, r11
  0xfff67f5e  f895        bnz 0x1f8 (0xfff67f56)
  0xfff67f60  40bd0002    movhi 0x200, r0, r10
  0xfff67f64  6041        mov 0, r11
  0xfff67f66  6ad10000    st.b 0[r10], r11
  0xfff67f6a  20bc0002    movhi 0x200, r0, r1
  0xfff67f6e  41a10400    movea 0x4, r1, r10
(vb-rs 0xfff67f72) > c

So that's rad :) Until next time!

Last Edited on Tue Dec 13 2016 07:22:17 GMT-0500 (EST)