Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doom - broken enemy behavior #295

Closed
JMendou opened this issue Aug 27, 2021 · 21 comments
Closed

Doom - broken enemy behavior #295

JMendou opened this issue Aug 27, 2021 · 21 comments

Comments

@JMendou
Copy link

JMendou commented Aug 27, 2021

SNES Doom enemy behavior is broken in a number of ways that are significantly inaccurate to how it performs on original hardware as well as emulation through current bsnes.

These are only the most blatant examples that can be easily confirmed with a few minutes of comparison. It is possible that there are more subtle differences as well.
*Enemies with a ranged attack will almost never perform them, but rather simply march toward the player continuously emitting the grunt or roar sound they are meant to make when they are alerted upon seeing the player. They should be shooting constantly.
*Upon reaching close range with the player, enemies will now occasionally attack, though still far less frequently than is accurate.
*Enemies are meant to have injured/staggered animation frames upon being damaged by the player, as well as give sound indicating they are taking damage. Neither of these things occur.
*Taking damage is also supposed to momentarily pause / stagger the movement and actions of most enemy types. This also does not seem to be working most, if not all, of the time.
*Damage calculations to enemies may be functioning incorrectly, or the players attacks are possibly being ignored with significant frequency. This is most noticeable on higher health enemies, who often take exponentially more damage to bring down than is accurate.

Confirmed these issues are occurring in current 20210713 release. (U) and (J) versions were tested and exhibited the same problems.

@lhathcock
Copy link

I can confirm the behavior on enemies at range using your method posted on the message board. Episode 3, from the start on an original model SD2SNES, enemies fire at you quite furiously. On MiSTer, they do virtually nothing.

@birdybro
Copy link
Member

birdybro commented Aug 31, 2021

https://www.youtube.com/watch?v=Tzc3e-W6z78 - video provided by MiSTerFPGA forum user "Stupid Dufus" showing the difference in behavior.

Would you test these roms on your SD2SNES and let us know the results?

mister-fail-bsnes-pass.zip

Thank you! I'm suspecting it has to do with the GSU test failing if that does pass on your OG hardware, because the dev of the sd2snes had to fix that to make super fx 2 games work right.

@lhathcock
Copy link

Sure, I'll try to test this evening.

@lhathcock
Copy link

lhathcock commented Sep 1, 2021

From each of the test ROMs folder - excuse the lousy formatting. Tests are on an SD2SNES Rev. F with 1.10.3-frs-v12 firmware (furious firmware) installed, no in-game hooks enabled. SNES is a somewhat uncommon 2/1/2, unmodified.

blargg_2010-03-14

test_timer_stop2: Failed
timer_at_power_reset: Passed

blobs

irq - red screen
nmi - red screen

multidiv_tests

div_behavior: Passed
div_timing: Passed
mul_behavior: Passed

nmi_irq - red screen

snes_mul_div_timing

div_timing: Passed

KungFuKirby

irq - red screen
nmi - red screen

ladida-lol

white screen with red gradient at bottom
b button makes black box in white area on top, bottom turns solid green

PeterLemon

GSUTest: All passed

@birdybro
Copy link
Member

birdybro commented Sep 1, 2021

Thank you so much for the help. I passed this along with a summary and description into the mister discord as well in the core-bugs channel. This kind of testing is thorough and very helpful! :)

@lhathcock
Copy link

Sure, glad I can help in some small way! It's y'all that are doing the real work.

@birdybro
Copy link
Member

birdybro commented Sep 4, 2021

I don't really do much, just test things and try to gather information. The devs do all the real work.

In summary, the DOOM SNES port's source code shows 143 uses of the Super FX 2's specific instruction "LMULT". The GSUTest rom mentioned above fails the LMULT test. The developer of the SD2SNES had to fix the Super FX and FX 2 implementations to get the GSUTest roms to completely pass in order to fix all known bugs as well, back in 2018 I think it was.

So, it's plausible that the AI is broken for because LMULT is not working "perfectly" yet. Hard to tell though.

@lhathcock
Copy link

Thought I'd seen you commit a few things around here. At any rate, very grateful for your help, and obviously the devs and their hard work.

@birdybro
Copy link
Member

birdybro commented Sep 4, 2021

Thought I'd seen you commit a few things around here. At any rate, very grateful for your help, and obviously the devs and their hard work.

Very little, mostly just readme updates, and any code I have contributed has been me having my hand held by Kitrinx and sorgelig, as I'm a total newbie. Thanks for noticing :)

@birdybro
Copy link
Member

birdybro commented Sep 4, 2021

Some notes I took to try and help if anyone wants to tackle this one day:

GSUTEST LMULT ROM

Here is the result on the MiSTer:
20210904_135037-GSULMULT

For the Rn/Op = R4/$9F with a VSCZ of 0110 the result should be FFFF, but instead it arrives at F800. These are signed 32-bit integers according to known SNES documentation so 0x0000FFFF = 65535 and 0x0000F800 = 63488. There is a difference of 2047 in decimal. I might be getting that number wrong somewhere.

Source code for the LMULT test rom and the rom itself here --> LMULT.zip

https://github.com/PeterLemon/SNES/blob/master/CHIP/GSU/GSUTest/LMULT/GSULMULT_gsu.asm

stop // Stop GSU
nop // Delay Slot
iwt r4, #$FFFF // R4 = $FFFF
iwt r6, #$0000 // R6 = $0000
with r4 ; lmult // R4 *= R6

stop // Stop GSU
nop // Delay Slot

iwt r4, #$FFFF // R4 = $FFFF
iwt r6, #$0800 // R6 = $0800
with r4 ; lmult // R4 *= R6

Doom-FX Source Code from @RandalLinden

153 uses of the LMULT instruction in the source code in total. LMULT's use is described in the sfx.txt file:

https://github.com/RandalLinden/DOOM-FX/blob/master/docs/sfx.txt

f/lmult src are register 6 apparently.

lmult = {notR4} Rd:R4 = Rs * R7 and has 5 (Fast) Cache Cycles with the V S C Z equal to _ * * *, Opcode is 3D 9F, and C = sign of lo word

Likely related source code files where LMULT is used

Reality Engine (RandalLinden made his own game engine for the DOOM port) files where LMULT is used:

There's much more which I'm assuming aren't as relevant, but hopefully some of this points someone in the right direction eventually. From what I am ignorantly assuming LMULT is used frequently in the x and y deltas for movement and for enemy targeting in general. Given the bug shows that on certain difficulty settings the monsters just can't seem to target the player and their movement gets stuck, it seems likely the math is just incorrect on this. It seems to be a bug that only works when SkillLevel=3.

SNES_MiSTer code snippets

https://github.com/MiSTer-devel/SNES_MiSTer/blob/master/rtl/chip/GSU/GSU_PKG.vhd

--line351
((OP_FMULT,  5), (OP_LMULT,   5), (OP_FMULT,   5), (OP_FMULT,   5)), --FMULT / LMULT
--

https://github.com/MiSTer-devel/SNES_MiSTer/blob/master/rtl/chip/GSU/GSU.vhd

--line 424
OP_CYCLES <= "000" when TURBO = '1' else
				 not (MS0 and not SPEED) & "10" when OP.OP = OP_FMULT or OP.OP = OP_LMULT else
				 "00" & not (MS0 and not SPEED) when OP.OP = OP_MULT or OP.OP = OP_UMULT else
				 "000";
--line 870
begin
    A := unsigned(R(to_integer(SREG)));
    if FLAG_ALT2 = '1' and (OP.OP = OP_ADD or OP.OP = OP_SUB or OP.OP = OP_AND or OP.OP = OP_MULT or OP.OP = OP_UMULT or OP.OP = OP_OR or OP.OP = OP_XOR) then
        B := x"000" & OP_N;
    else
	B := unsigned(R(to_integer(OP_N)));
    end if;	
    ALUR <= (others => '0');
    MULR <= (others => '0');
    ALUOV <= FLAG_OV;
    ALUCY <= FLAG_CY;
--skipping to line 931
    elsif OP.OP = OP_FMULT or OP.OP = OP_LMULT then
	MUL_TEMP := signed(A) * signed(R(6));
	ALUR <= std_logic_vector(MUL_TEMP(31 downto 16));
	MULR <= std_logic_vector(MUL_TEMP(15 downto 0));
        ALUCY <= MUL_TEMP(15);
--skipping to line 967
    end if;
end process; 

SD2SNES source code snippets

The behavior is correct on the SD2SNES and the LMULT test passes as well, so it's relevant to show SD2SNES source code. The GSU source code is in verilog instead of vhdl but they are both HDL so probably more simple to compare than software source code.

https://github.com/mrehkopf/sd2snes/blob/develop/verilog/sd2snes_gsu/gsu.v

// line 2214
// MULTIPLY
`OP_FMULT_LMULT   : begin
  exe_fmult_srca_r <= exe_src_r;
  exe_fmult_srcb_r <= exe_r6_r;

  e2c_waitcnt_val_r <= 1;
  e2c_waitcnt_r     <= lat_fmult_r;
end
// line 2325
`OP_FMULT_LMULT   : begin
              e2c_waitcnt_val_r <= 0;
              exe_mult_delay_r <= 2;
              EXE_STATE <= ST_EXE_MEMORY_WAIT;
            end
// line 2499
case (exe_opcode_r)
  `OP_FMULT_LMULT   : begin
    if (|exe_mult_delay_r) begin
      exe_mult_delay_r <= exe_mult_delay_r - 1;
    end
    else begin
      e2r_val_r      <= 1;

      e2r_data_r     <= exe_fmult_out[31:16];
      e2r_r4_r       <= exe_fmult_out[15:0];
      e2r_lmult_r    <= exe_alt1_r;

      e2r_z_r        <= ~|exe_fmult_out[31:16];
      e2r_s_r        <= exe_fmult_out[31];
      e2r_cy_r       <= exe_fmult_out[15];

      EXE_STATE <= ST_EXE_WAIT;
    end
// line 2568
if (gsu_clock_en) e2r_lmult_r <= 0;

bsnes source code snippets

bsnes does pass the LMULT test and does show normal enemy behavior in DOOM, therefore it's probably a useful reference for this specific game. It is also 100% compatible with all of the commercial SNES library and passes every one of the test roms out there. Finally, the SNES_MiSTer used bsnes as the primary reference so it's relevant to mention the source code here.

https://github.com/bsnes-emu/bsnes/blob/master/bsnes/processor/gsu/instructions.cpp

//$9f(alt0) fmult
//$9f(alt1) lmult
auto GSU::instructionFMULT_LMULT() -> void {
  uint32 result = (int16)regs.sr() * (int16)regs.r[6];
  if(regs.sfr.alt1) regs.r[4] = result;
  regs.dr() = result >> 16;
  regs.sfr.s  = (regs.dr() & 0x8000);
  regs.sfr.cy = (result & 0x8000);
  regs.sfr.z  = (regs.dr() == 0);
  regs.reset();
  step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2));
}

@paulb-nl
Copy link
Contributor

paulb-nl commented Sep 4, 2021

Separated the LMULT test:
GSUTest_LMULT.zip

20210904_192835-GSULMULT

@birdybro
Copy link
Member

birdybro commented Sep 4, 2021

Thank you! I'm updating the above comment a couple more times I think as I keep looking into it.

@birdybro
Copy link
Member

birdybro commented Sep 5, 2021

be0cfc8 - LMULT now passes in this test rom, so that part is fixed, but this didn't fix the issue with DOOM-FX yet.

I tested for regressions with PeterLemon's GSUTEST roms, there were none. Enemy behavior remains the same. @JMendou and @lhathcock can you confirm same behavior as before? Such a shame, I was hoping we had figured it out :P

The following test roms are the only ones left that fail on the MiSTer but pass on original hardware and pass on bsnes. We are like 99.7% of the way there! :)

mister-fail-bsnes-pass-oghw-pass.zip

jonasquinn-test-roms

blargg_2010-03-14

timer_at_power_reset.smc
notes.txt:

0A 2731                                                         ~  3
1A 1639 = 0.600146466 = 0.600146466 : 1.666259915 = 1.666259915 ~  5
2A  910 = 0.333211278 = 0.333211278 : 3.001098901 = 3.001098901 ~  9
3A  482 = 0.176492127 = 0.176492127 : 5.665975104 = 5.665975104 ~ 17

4A 2049               = 0.750274625               = 1.33284529  ~  4 + 1
5A 1366 = 0.666666667 = 0.500183083 : 1.5         = 1.999267936 ~  6 + 1
6A  819 = 0.399707174 = 0.29989015  : 2.501831502 = 3.334554335 ~ 10 + 1
7A  455 = 0.222059541 = 0.166605639 : 4.503296703 = 6.002197802 ~ 12 + 1

CA  819               = 0.29989015                = 3.334554335 ~ 10 + 6
DA  683 = 0.833943834 = 0.250091542 : 1.199121523 = 3.998535871 ~ 12 + 6
EA  512 = 0.625152625 = 0.187477115 : 1.599609375 = 5.333984375 ~ 16 + 6
FA  342 = 0.417582418 = 0.125228854 : 2.394736842 = 7.985380117 ~ 24 +12

 3+2= 5+4= 9+8=17
 4+2= 6+4=10+8=18
 6+2= 8+4=12+8=20
10+2=12+4=16+8=24

step = 1 << clock_speed + 2 << timer_speed;

base = (1 << clock_speed) >> 1;  //0, 1, 2, 4

{ 3, 5, 9, 17 }
{ 1, 3, 7, 15 }

0001
0011
0111
1111

2, 3, 5, 9

15 >> (3 - timer_speed);


timer_speed << 2
{ 0, 4, 8, 16 }



base = 1 + (1 << clock_speed);  //2,3,5,9
clocks = base + 15 >> (3 - timer_speed);  //add 1,3,7,15

{ 3, 5, 9, 17; 4, 6, 10, 18; 6, 8, 12, 20, 10, 12, 16, 24 }

3*4*5*6*8*9*10*12*16*17*18*20*24=7309688832000
7309688832000/24576000=297432

{ 3, 5, 9, 11; 4, 6, 10, 12; ?, ?, ??, ??; 10, 12, 16, 24 }

/3

multidiv_tests

div_behavior.smc
div_timing.smc
mul_behavior.smc
notes:

mul
---
WRMPYB write starts multiply if one isn't running.
WRMPYB write clears RDMPY even if multiply is in progress (value written is ignored).
WRMPYA write doesn't affect current multiply.

Start multiply:
	Copy WRMPYA to RDDIVL.
	Copy WRMPYB to RDDIVH.
	Copy WRMPYB to internal left shifter.

Each step of multiply:
	If low bit of RDDIVL is set, add internal shifter to RDMPY.
	Internal shifter left by one bit.

in DOOM-FX source code:

rllines.a (reality engine lines module)

;line 70
ldy	_RDMPYL

Probably not related, but worth mentioning as maybe the targeting relies upon lines?

div
---
WRDIVL write during divide has no effect on result.
WRDIVH write during divide has no effect on result.
WRDIVB write during divide reloads RDMPY with last written value to WRDIV (value written is ignored).

Start divide:
	Copy WRDIV to RDMPY.
	Copy WRDIVB to internal right shifter.

In DOOM-FX source code:

snes.i (under cpu equates section)

; line 179
WRDIVL		equ	$4204
WRDIVH		equ	$4205
WRDIVB		equ	$4206
; line 193
_RDDIVL		equ	$4214
_RDDIVH		equ	$4215

These only seem to be used for scores (kills, etc...) and status (health, armor, etc...) though.

snes_mul_div_timing

div_timing.smc

ladida-lol

ladida-lol.sfc
Expected result should be that once you press the b button the bottom of the screen changes from red to green and a black box appears, but instead a black box appears but the bottom of the screen stays red. This test rom is an iffy one on whether or not it is necessary to pass, but byuu/Near thought it was significant and bsnes and OG hardware pass.

@lhathcock
Copy link

Assuming the unstable build I grabbed here is the correct one, same behavior as before.

https://github.com/MiSTer-unstable-nightlies/SNES_MiSTer/releases/download/unstable-builds/SNES_unstable_20210905_8be2.rbf

@Kopert
Copy link

Kopert commented Dec 11, 2021

I did a quick test with SNES_unstable_20211211_aab7.rbf which includes fcd808a. Ranged enemies are shooting as expected and reacting properly to getting shot, so it's an improvement.

Test rom results:

timer_at_power_reset.smc fail
div_behavior.smc pass
div_timing.smc pass
mul_behavior.smc pass
ladida-lol.sfc fail

@birdybro
Copy link
Member

Oh that's exciting!!

@birdybro
Copy link
Member

@JMendou Can you confirm resolution? Thanks! :)

@sdufus
Copy link

sdufus commented Dec 21, 2021

I just did a test with SNES_unstable_20211214_5a9e.rbf and seems to work on my end. Only tested the part from my video.

@birdybro
Copy link
Member

I think this issue is now resolved and can probably be closed, right @JMendou ?

@JMendou
Copy link
Author

JMendou commented May 29, 2022

Sorry for disappearing.
After reading through the comments and doing some testing on the 20220519 release, I’m closing the issue.
Amazing work!

@JMendou JMendou closed this as completed May 29, 2022
@RandalLinden
Copy link

Great work, guys!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants