mamedev / mame

MAME
https://www.mamedev.org/
Other
7.92k stars 1.98k forks source link

Taito F3 unemulated video effects #10033

Closed FredYeye closed 4 months ago

FredYeye commented 2 years ago

Elevator Action Returns has a visual effect that currently isn't emulated in mame: upon shooting the lamps hanging from the ceiling, the lights on that floor are supposed to go out.

I disassembled some of the code for the lamps and figured out that it's writing to 0x626200: line ram, offset 0x6200. _taito_f3v.cpp labels this area as "Alpha blending control", but it seems to go unused (to me, who is unfamiliar with the code, anyway). I don't know what that is supposed to mean, but it appears to be 256 16-bit values (one u16 per line).

I looked at the effect in the saturn port to have some kind of reference as to what it's supposed to look like. Not the best as it could differ, but it seems identical. These are the values that gets written to line ram -

hex  | binary              | effect
-----------------------------------
BBBB | 1011 1011 1011 1011 | default brightness

BBFB | 1011 1011 1111 1011 | dark flashing
7B7B | 0111 1011 0111 1011 | bright flashing

7B8B | 0111 1011 1000 1011 | fade bright
7BAB | 0111 1011 1010 1011 | fade |
7BCB | 0111 1011 1100 1011 | fade |
7BEB | 0111 1011 1110 1011 | fade |
8BFB | 1000 1011 1111 1011 | fade v
ABFB | 1010 1011 1111 1011 | fade dark

CBFB | 1100 1011 1111 1011 | lights off
-----------------------------------

When a lamp hits the ground:
2f dark flashing
2f bright flashing
2f dark flashing
2f bright flashing
1f dark flashing
fade to dark from bright
lights off

Looking at the values makes me think that each nibble is some kind of control per layer. For now I'll just call it brightness control, as that is the effect they achieve. Two columns stay the same throughout this effect and two change. Layers 1 & 4 are not supposed to be affected. Layer 3 should change brightness. I don't know what layer 2 is, it's not used in stage 1. Might be something that changes brightness in later stages.

I guess my question here is, should I keep going with this? For now I've at least documented my findings, but I obviously can't know what the real PCB does with these values. I'm tempted to play around with the source code and attempt to apply the brightness control, but I'm at a loss as where to start. _taito_f3v.cpp is not a crazy long file but I'm having a hard time following the code, never having looked at mame's codebase before.

Any help or comments are appreciated! If this data isn't enough try anything yet, let me know too.

-Fred

einstein95 commented 2 years ago

This footage confirms the effect is in the arcade version https://youtu.be/ZkoQj_f0ato

ghost commented 2 years ago

The F3 video is very complex (and probably needs a rewrite at some point) so I don't really blame you for having a hard time with it; Taito really pushed the boat out with the design.

Almost everything on the system can be controlled per-line, there are often other registers which control the behavior of the registers you're looking at etc.

Of course highlighting specific cases like this, with video evidence, and notes on the register use is a good way of working out how things should really work.

FredYeye commented 2 years ago

Gekirindan appears to use this effect as well.

Glowing logo: https://youtu.be/1HOTZ9BAjiA?t=22 The orange ship gets brighter as it fades away: https://youtu.be/1HOTZ9BAjiA?t=65

Edit: Puchi carat also uses this. At the very least it's used to brighten the intro text. There is a dimmed area during gameplay that could be a candidate as well, but not 100% about that one, as the entire line array is the same value at that time. Haven't really looked closer there.

FredYeye commented 2 years ago

I've slowly been looking at this issue with one of the FB Neo devs. FB Neo has a hack that makes the lights almost work, which gave me some clues about where in the code I should look.

I can now see that the 0x6200 area values are in fact being read, but are not being used in some cases in MAME. 0xB000 (labelled Playfield priority) has a blend mode field, and no blending is being done (afaict) if it's set to 0x3000 = Normal blending mode.

Clearly blending is still supposed to happen though, so maybe there's some misunderstanding about how these blend modes are supposed to work. This is where we're currently at.

angelosa commented 1 year ago

Potentially a duplicate of MT bug reports, pick your poison:

Additional acknowledged stuff here:

Last but not least, this:

TL;DR: you are trying to describe TC0650FDA (the scanline priority & palette mixer) from the wrong perspective here.

FredYeye commented 1 year ago

Not entirely sure what you're trying to say but I can see that this driver needs some love, haha.

angelosa commented 1 year ago

I'm saying that there must be a reason about each one of these bugs happening, trying to analyze from the perspective of one game only isn't the right approach. List of things that comes on top of my head atm, without even digging the reports:

Note: I've already tried a couple times to come up with something for a couple of these bugs above to no avail, I find the code to be kinda hard to follow in places and I've converted the whole thing to 16-bit accessors some years ago.

y-ack commented 1 year ago

brightness: puchi carat was a good one to find, shows that 7400 ("sprite clipping") high bits are related during gameplay, sets 6200 brightness/alpha A7FF over all lines, then sets 7400: 8310 on lines with the darkened sprite panels (0310 otherwise)

possibly a/b select by priority(? like alpha control). something like

0x7400: Sprite clipping
<...
    0x0100 = Affects interpretation of inverse mode bits. If on, 1 = invert. if off, 0 = invert.
    0x0200 = unknown - maybe analogous to xb000 0x2000 ("0x3000")?
    0x0c00 = seems analogous to xb000 0xc000 blend mode select?
    0x1000 = brightness mode for sprites with pri value 0x00 ?
    0x2000 = brightness mode for sprites with pri value 0x40 ?
    0x4000 = brightness mode for sprites with pri value 0x80 ?
    0x8000 = brightness mode for sprites with pri value 0xc0 ?

gekirindan

title logo (attract) 6200 values pf1 bg, pf2 black logo <- affected by brightness, pf3 logo fill, pf4 earth bg pf pri [3002] [9005] [1007] [3003] (9=[100_]) 77FF <- logo not shown yet pf pri [3002] [B005] [1007] [3003] (B=[101_]) 78FE <- logo opacity fade in starts, stays for 8 frames 79FD 7AFC 7BFB <- half transparency 7CFA 7DF9 7EF8 7FF7 <- fully opaque in mame? 8FE7 <- brightness flash starts? 9FD7 AFC7 CFA7 DF97 EF87 <- final value pfs now [3002] [3005] [3007] [3003] and pf3 orange animation happens ---
ship (stage 1 intro) 6200 values tiles have 'alpha blend mode' bit set. 0BFB 77FF <- pf2 `900A` now ([100_]) 87EF <- pf2 `B00A` now ([101_]) 97DF A7CF B7BF C7AF D79F E78F F77F <- fully transparent in mame, too early according to ref? F87E <- would expect this to be a switch to alpha fade out but the ref makes it seem like those should overlap (?) F97D FA7C FB7B FC7A FD79 FE78 ~20+ frames later, reset to 0BFB, pri 3009 [001_] ---

elevator action returns this time all playfields' pri values are 300x [001_] playfield 3, which is the only one that should be affected, has the alpha blend mode set on tiles here too. original post has better notes about the value transition, but copying a few here for relevant reference material

value output
BB[B]B image
BB[F]B image
7B[7]B image

(from https://youtu.be/7WD-ftK-Dgk?t=6728)

most likely: add and clamp, steps of 32 -128 to 128 linear, steps of 32 789A B FEDC color + (256/8)*(4−(n-7))

from these cases, my guess would be that each word in 6200 (currently called alpha_level) is split like [B][A][B][A] B = brightness, 7 (lighter) to F (darker) A = alpha, 7 (transparent) to F (opaque)

but scanline_draw reads them like this: [a][b][c][d] and seems to consider the b,d set alpha 'a' and c,a 'b'? so there might be a misunderstanding there

more trying to read source (for self use)current alpha code takes `sprite_alpha` from 6000, then `sprite_alpha_mode = (sprite_alpha >> (i * 2)) & 3`, two bits alpha mode per, i guess "priority block" [pri alpha bits] = alpha_mode low bits [xx0] = 0 [001] = 1 [011] = 2 [101] = 3 alpha_type << 4 is 0x10 if vram and alpha_mode > 1 [get_vram_info] 0x10 if [1x_] or [_1_] set [get_vram_info] 0x10 if tile alpha mode bit NOT set and alpha_mode > 1 [visible_tile_check] 0x20 if tile alpha mode bit set and alpha_mode > 1 [visible_tile_check] 0x30 if both set and unset tiles in a line?? [visible_tile_check] becomes an offset of +2 (+4 if both) in m_dpix_n lookup alpha_type comes from the high bits in the alpha_mode depicted here: /* alpha_mode ---- --xx 0:disable 1:nomal 2:alpha 7000 3:alpha b000 ---1 ---- alpha level a --1- ---- alpha level b 1-------- opaque line */ `m_dpix_n[alpha_mode[pos] + (alpha level - 1)*2];` dpix = destination pixel contribution ? fallback cases for alpha modes??
if (alpha_mode[i] == 2)
{
    if (alpha_type == 1)
    {
        /* if ([b] == 0   && [d] == 255)
         *     alpha_mode[i]=3; alpha_mode_flag[i] |= 0x80; }
         * will display continue screen in gseeker (mt 00026) */
        if      ([b] == 0   && [d] == 255) alpha_mode[i] = 0;
        else if ([b] == 255 && [d] ==   0) alpha_mode[i] = 1;
    }
    else if (alpha_type == 2)
    {
        if      ([a] == 0   && [c] == 255) alpha_mode[i] = 0;
        else if ([b] == 255 && [d] ==   0 &&
             [a] == 255 && [c] ==   0) alpha_mode[i] = 1;
    }
    else if (alpha_type == 3)
    {
        if      ([b] == 0   && [d] == 255 &&
             [a] == 0   && [c] == 255) alpha_mode[i] = 0;
        else if ([b] == 255 && [d] ==   0 &&
             [a] == 255 && [c] ==   0) alpha_mode[i] = 1;
    }
}
else if (alpha_mode[i] == 3)
{
    if (alpha_type == 1)
    {
        if      ([d] == 0   && [b] == 255) alpha_mode[i] = 0;
        else if ([d] == 255 && [b] ==   0) alpha_mode[i] = 1;
    }
    else if (alpha_type == 2)
    {
        if      ([c] == 0   && [a] == 255) alpha_mode[i] = 0;
        else if ([d] == 255 && [b] ==   0 &&
             [c] == 255 && [a] ==   0) alpha_mode[i] = 1;
    }
    else if (alpha_type == 3)
    {
        if      ([d] == 0   && [b] == 255 &&
             [c] == 0   && [a] == 255) alpha_mode[i] = 0;
        else if ([d] == 255 && [b] ==   0 &&
             [c] == 255 && [a] ==   0) alpha_mode[i] = 1;
    }
}
y still has no idea what `::dpix_*_*` are trying to do at this point... one other thing worth mentioning, arabianm, all pfs `7xxx` [011_] except misbehaving text layer (vram) `b00f` [101_] so alpha mode 3 and alpha type 1, i think? and then since 6200 is `000F`, `else if ([d] == 255 && [b] == 0) alpha_mode[i] = 1;` triggers? so i have a kind of suspicion that the alpha blending code might be responsible for that ---
FredYeye commented 1 year ago

Wow, good stuff! It's been a while since I looked at this, so I don't have any new insights for you... busy with life stuff, heh.

from these cases, my guess would be that each word in 6200 (currently called alpha_level) is split like [B][A][B][A] B = brightness, 7 (lighter) to F (darker) A = alpha, 7 (transparent) to F (opaque)

This sounds like a good theory to me. It does seem to be grouped into 4-bit values at least.

I don't know if i ever posted this anywhere (on fb-neo, maybe), but I made a similar data table for gekirindan. I guess you already have the values but I'll post it here anyway.

gekirindan

hex  | binary              | effect / time
------------------------------------------
0BFB | 0000 1011 1111 1011 | default

77FF | 0111 0111 1111 1111 | 3f ship fade effect
87EF | 1000 0111 1110 1111 | 4f
97DF | 1001 0111 1101 1111 | 4f
A7CF | 1010 0111 1100 1111 | 4f
B7BF | 1011 0111 1011 1111 | 4f
C7AF | 1100 0111 1010 1111 | 4f
D79F | 1101 0111 1001 1111 | 4f
E78F | 1110 0111 1000 1111 | 4f
F77F | 1111 0111 0111 1111 | 3f
F87E | 1111 1000 0111 1110 | 4f
F97D | 1111 1001 0111 1101 | 4f
FA7C | 1111 1010 0111 1100 | 3f
FB7B | 1111 1011 0111 1011 | 4f
FC7A | 1111 1100 0111 1010 | 4f
FD79 | 1111 1101 0111 1001 | 4f
FE78 | 1111 1110 0111 1000 | 24f ship fade effect end (goes back to default after this)

same data but different view (easier to see ramping effect)
column 1: 0 | 789ABCDEFFFFFFFF
column 2: B | 77777777789ABCDE
column 3: F | FEDCBA9877777777
column 4: B | FFFFFFFFFEDCBA98

layer 1: ?
layer 2: orange ship (inside)
layer 3: orange ship (outside)
layer 4: star field?
y-ack commented 1 year ago

hm, starting to reconsider these values being cleanly split into brightness and alpha after reading more.

more disorganized trying to read driver source (for self use) where 6200 words are taken as [a][b][c][d] read the alpha levels [a'] [b'] [c'] and [d'] as adjusted to 0-256 here `[scanline_draw]` ``` /* name indicates alpha A 0x7000, could be meaningless ! */ // al = alpha level, s = src, d = dst (?) al_s = (15 - [d]) * 32 al_d = (15 - [b]) * 32 m_alpha_level_3as = al_s [d'] m_alpha_level_3ad = al_d [b'] m_alpha_level_2as = al_d [b'] m_alpha_level_2ad = al_s [d'] /* name indicates alpha B 0xb000 */ al_s = (15 - [c]) * 32 al_d = (15 - [a]) * 32 m_alpha_level_3bs = al_s [c'] m_alpha_level_3bd = al_d [a'] m_alpha_level_2bs = al_d [a'] m_alpha_level_2bd = al_s [c'] ``` // why do the `m_alpha_level` s and d have nothing to do with the `al_` s and d... // q: what do s and d even mean here, then? `[alpha_set_level()]` (some details omitted, like +1 accounting for [x'] 256) ``` m_alpha_s_1_1 = 255 - [b'] m_alpha_s_1_2 = 255 - [a'] m_alpha_s_1_4 = [b'] m_alpha_s_1_5 = [b'] * (255 - [b']) / 255) m_alpha_s_1_6 = [b'] * (255 - [a']) / 255) m_alpha_s_1_8 = [a'] m_alpha_s_1_9 = [a'] * (255 - [b']) / 255) m_alpha_s_1_a = [a'] * (255 - [a']) / 255 m_alpha_s_2a_0 = [b'] m_alpha_s_2a_4 = [b'] * [b'] / 255 m_alpha_s_2a_8 = [b'] * [a'] / 255 m_alpha_s_2b_0 = [a'] m_alpha_s_2b_4 = [a'] * [b'] / 255 m_alpha_s_2b_8 = [a'] * [a'] / 255 m_alpha_s_3a_0 = [d'] m_alpha_s_3a_1 = [d'] * [d'] / 255 m_alpha_s_3a_2 = [d'] * [c'] / 255 m_alpha_s_3b_0 = [c'] m_alpha_s_3b_1 = [c'] * [d'] / 255 m_alpha_s_3b_2 = [c'] * [c'] / 255 ``` // a: it's flipped because it's used as the second value here.. (for the following routines, read m_dval as an output value) `[alpha_blend32_s]` only use source pix, param1 alpha `[alpha_blend32_d]` uses dest pix to blend into `m_add_sat` is a saturating addition table (clamped to 255) i.e. `dest color = min(dest color + ((alpha * src color) div 256), 255)` (src color arg comes from clut in UPDATE_PIXMAP_LP and UPDATE_PIXMAP_SP) `[alpha_blend_*]` these functions wrap the main blend functions with the m_alpha_s_ source levels. noteworthy: alpha_blend_2a_0 alpha_blend_2b_0 alpha_blend_3a_0 alpha_blend_3b_0 do not blend with a destination pixel, probably correspond to a case where both alpha type bits are off. `[dpix_*]` mode 1 wraps alpha blend, mode 2a, 3a, 2b, 3b has an extra check for respective m_pdest_* `m_pdest_2a`: 0x10 if [d'] is nonzero (if [d] < 0xF) `m_pdest_2b`: 0x20 if [c'] is nonzero `m_pdest_3a`: 0x40 if [b'] is nonzero `m_pdest_3b`: 0x80 if [a'] is nonzero and sets the corresponding bit on m_pval (another output value) m_pval is used as the second input to the m_dpix_n tables ..... dpix_2_* has to check both 2a and 2b dpix_3_* has to check both 3a and 3b // so probably saying one or both values not required or invalid in the other cases tr comes from tile flags map?

i'm not entirely sure, but the specific way m_alpha_s combines the values seems like (if correct) a way to trade more line-level alpha channels for increased resolution, which doesn't sound implausible... the implication being that the 6200 'channels' are also shared, sort of generic parameters? ...regardless, i'm starting to think that considering brightness as completely separate from alpha might be the wrong way to approach this...

probably have to look at some alpha blending examples/value transitions the same way to re-verify everything in here, at this point... gseeker continue screen is another good case to check.

ICEknigh7 commented 1 year ago

Bubble Memories may be also a good test case:

y-ack commented 1 year ago

Bubble Memories may be also a good test case:

that effect seems to use 6a00 (not 6200), possibly multiply/addition blend modes...? not described in mame.

setup triggered by $e3208, call $e3a28 to enqueue set of copies to line set ram: ``` 6000 (alpha/x zoom/bg) control 620400-62043C = 180F->183F 620440-6205FC = 180D->183D [0000][1101]->[0011][1101] 7000 (sprite priority) control 620600-62063C = 1C0F->1C2F 620640-6207FC = 1C02->1C22 [0000][0010]->[0010][0010] b000 (pf priority) control 620E00-620E3C = 2C0F->2CFF 620E40-620FFC = 2C07->2CF3 [0000][0111]->[1111][0011] ``` line data (transfer in $e3ac6): | group|before|during| |------|-----:|-----:| | 6200 | BBBB | BBBB | | **6800** | A0FF | **A0D5** | | **6a00** | BBBB | **5555** | | 7200 | 3003 | 3003 | | **7a00** | 3003 | **7003** | | b000 | 3001 | 3001 | | b200 | B00B | B00B | | b400 | 300C | 300C | | b600 | 310D | 300D | | **b800** | 3001 | **7001** | | **ba00** | B00B | B00B | | **bc00** | 300C | **7202** | | **be00** | 300D | **B00E** | ---

maybe not a good test case so much as the only test case for an entirely different set of blend modes.

edit: oh hey, and kaiserkn uses 5800 during the clipped intro animation. also, i think all of the "Where bit 0 of x enables effect on playfield 1" etc. documentation is misleading, because they're probably subsection enables.

FredYeye commented 9 months ago

I don't really have anything substantial to add right now, but I guess this is the best place to dump various F3 things for now?

At the end of elvactr stage 5, the brightness should increase before the screen goes completely white. These are the 0x62xxxx writes that happens in the setup function for this effect:

addr             | old  -> new  | PC
626030           | 00FF -> 00FF | 1436AA
626230           | BBBB -> 77FF | 1436DE // increase brightness
626430           | 7000 -> 7000 | 143712
620432 .. 6205FE | 180F -> 1800 | 143746 // 620430 remains 180F
627230           | 300F -> 300F | 14377A
627430           | 0300 -> F300 | 1437AE
627630           | 26AE -> 16AE | 1437E2
620632 .. 6207FE | 1C0F -> 1C00 | 143816 // 620630 remains 1C0F

// increase brightness over the next frames
77EE to 626230           (PC=14386A)
77DD to 626230
77CC to 626230
77BB to 626230
77AA to 626230
7799 to 626230
7788 to 626230
7777 to 626230

Another thing. Many F3 games have a ram/rom check function. These are the names that function uses for the 0x62xxxx area:

620000 - 620FFF: "line set ram"
621000 - 621FFF: "pivot port"
622000 - 62FFFF: "line data ram"

The pivot port name would suggest it's related to pivot ram (0x630000). The pivot port & ram checks are right next to each other as well. taito_f3_v has this to say about 0x621000 currently:

0x1000: Unknown control word?
        (usually 0x00f0; gseeker, spcinvdj, twinqix, puchicar set 0x0000)
angelosa commented 9 months ago

I don't really have anything substantial to add right now, but I guess this is the best place to dump various F3 things for now?

It is yeah, also the OP can be edited to tasks given this is a pretty big user story.

y-ack commented 9 months ago

621000 - 621FFF: "pivot port"

note that this area is still rw, see e.g. tcobra2 POST @ 0x000b48. i wouldn't be surprised if this only omits 'ram' for brevity, or something.

622000 - 62FFFF: "line data ram"

regarding line data ram starting at 622000 (as opposed to the known start in 624000), one hypothesis is that the line data sections can be entirely remapped:

of course, it doesn't really affect existing emulation cases whether or not line ram can be remapped, and it sure doesn't seem like a very useful feature to have. but it would be a good test to have in a hw test regardless.

i hesitate to write down assumptions like this because there's already so much incorrect guessing about this machine out there, some of which i've already contributed to this very thread, but this one at least adds meaning to previously meaningless bits and gaps ...


i'm still around, just busy.
after my previous work i've [also] come to the conclusion that the future of taito f3 emulation in mame requires a proper rewrite of at least the drawing routines.
maybe there's low-hanging fruit still like what i did before but the current code is not maintainable... some things are just bad and some things don't map conceptually and some things are so convoluted in the name of optimization that correctness is unobtainable.
i intend to begin to address this in a draft pr this weekend, but will probably be late december to conclude it.

FredYeye commented 9 months ago

the future of taito f3 emulation in mame requires a proper rewrite of at least the drawing routines.

Sounds about right! I look forward to your draft. 👀

I think updating the "line ram memory map" documentation would do a lot as well. Making it easier to see where each region starts and ends, and what each group of bits in each region do would be nice.

Edit: I think something in this style could look good for documentation:

0400 - 05FF: Line control for 6000 section (256 * 16-bit values)
  ???? ???? ???? ??ts
  s: latch sprite alpha value
  t: latch tilemap alpha value

0600 - 07FF: Sprite control (256 * 16-bit values)
  ???? ???? ???? ????

0800 - 09FF: Zoom line control (256 * 16-bit values)
  ???? ???? ???? pppp
  p: effect on/off toggles for playfields 1-4

Though preferably this would also be done by someone with a better understanding than me! Haha.


one hypothesis is that the line data sections can be entirely remapped

Right, i see what you mean by this. If none of the known games use this feature we'd have to run tests on real hw to verify. Arabianm actually writes 0x040F to line set ram on boot... but immediately clears it. 😂


Regarding bubble memories... the data in 6800-6FFF looks suspiciously similar to what's in 6000-67FF, or is it just me? Is it really just a second set of data...? Not sure how probable or useful that would be... I can't tell if that effect could be done with just 6000-67FF.

FredYeye commented 4 months ago

Amazing! Many people I know like the F3 games and they'd sometimes mention the video bugs, so it's cool to get to see these changes actually happen.

Fantastic work, ywy & 12Me21!!

While they have more changes in the works, this issue is essentially resolved (or at the very least outdated), so I'll go ahead and close it now.