That is about the same kind of RTS as Napalm, and almost on the same level. RTG, AHI, 68k, music/sounds/cd-audio and co.
It has some issues (like always freezing preference program on exit), DSI in a game itself a few places, DSI on exit from the game, non-stop-able CD-Audio on exit, and all that sort of minor things which nice to deal with.
So, I start, of course, with the latest CD release (i took it for 5 EUR now in downloadable form from amiga future, it comes with .cue and all cd-audio tracks working via our diskimage.device/cdplayer.library/cddpatch), and study/apply all necessary/latest patches.
At the moment I already have worked and reassembled two binaries of a game: the main binary, and setup program binary. I reassembled them from disassembled code, and they work now just like originals, so patching/fixing/making_new_bugs is possible.
But before, we need to sort one thing out:
On Radeon RX and Radeon HD drivers (be it x5000 or sam460 used) that game does not have correct rendering in 640x480xCLUT but has correct rendering on Pegaos2 with old Radeon9250, and on WinUAE with os3.9 and E-UAE with OS3.2.
Hope some of you will help me to understand wtf and where and how we can deal with it in a fast manner.
So. If we run a game in 640x480x8 bit (CLUT) (the mode for which that game was designed to work on GFX cards) on X5000 or on SAM460 (i.e. anywhere where we use RadeonHD or Radeon RX), instead of a clean picture we have that:
All images are clickable for full-size!
Intro:
Menu:
Now if we run on Pegasos2 with Radeon9250(!), or on WinUAE with OS3, or on E-UAE with OS3.2, then we have that:
Intro:
Menu:
I point out that Pegasos2 with OS4 and the same up2date OS4 with all the latest stuff as on X5000 and Sam460 is all fine too. The only exception that this is RadeonRX+RadeonHD cards/drivers versus old Radeon9250 card/drivers.
For sake of fun I run 800x600 and 1024x768 modes on all the machines, and it has everywhere the same kind of trash as we have in 640x480 but on RadeonRx/RadeonHD, see:
800x600 pegasos2/winuae/euae:
1024x768 pegasos2/winuae/euae:
So far, that is what it means:
1). working resolution of game only 640x480x8. Ok.
2). by some reason it didn't work well on Radeon RX/RadeonHD, but works on older Radeon, and WinUae, and our e-UAE. It also works not only on Pegasos , but also on A1/Sams where older Radeons can be/still used
3). I checked via Ranger params of Screen and Window after creation, and on Radeon RX/RadeonHD and on Radeon9250, everything the same:
Box: Left: 0 Top: 0 Width: 640 Height: 480 Shape: Regison : 0x00000000 Hook: 0x00000000 Alpha: ClipRect: 0x000000 Hook: 0x000000 Opaqueness: 255 Override: False Fade Time: 0 (ms) No Hit Threshold: 16 Drop Shadows: -1 User Data 0x00000000
4). I do compare all the images, and we do "miss" on RadeonHD/Radeon RX exactly 80 lines at the bottom (anyone can recheck it from the screenshots above).
5). created screen/window is indeed 640x480 everywhere. On RadeonRX/RadeonHD I can move via mouse cursor in all areas of the screen (also over that "black" area too). It's just rendering on Radeon RX/HD happens to be smaller on 80 lines and not in full 480 lines, but just 400 instead.
Now the main question: WTF. If it works on pegasos2 then it is not the game's code probably, right? Seeing the same kind of "garbage" when we use bigger modes on "working" hardware, point out that something is wrong exactly with our 640x480 mode. But what?
Is it the game's bug, or driver's bug? From where we can start to deal with it?
Thanks a bunch to anyone who digs in !:)
Edited by kas1e on 2022/1/11 21:58:58 Edited by kas1e on 2022/1/19 8:01:08
I guess the game expects a width to be what it had allocated, not what it got. While in AmigaOS4.1 it was introduced actual width, that includes padded width sizes.
Edited by LiveForIt on 2022/1/11 22:33:27
(NutsAboutAmiga)
Basilisk II for AmigaOS4 AmigaInputAnywhere Excalibur and other tools and apps.
@All I got a mail from Hans, he says that it looks like the game isn't using the right bytes-per-row.
Radeon 9xxx cards have images padded to a multiple of 128 while RadeonHD/RX-ATI/AMD cards padded that out to a multiple of 256.
Assuming that Exodus uses 8-bit bitmaps:
640/128 = 5, so Radeon 9000 cards can handle 640 wide bitmaps with no padding
640/256 = 2.5, so Radeon HD and newer cards pad 640 out to 768
So, 640*480 = 307.200 And 768*400 = 307.200
But as we have with no padding 768, then game fill 768x400 => mess on screen and 80 lines missed.
So it looks like the game fails to read the actual bitmap width and adapt to it (or mean don't do it at all, and only read heigh and hope for a no-padding-all-works-fine)
Now, the question: how we can deal with that.
I already got binary reassembled, so I can in any place put calling to my own functions written on C, bypass any code in original assembler parts, and return back when need it with taking all necessary pointers/data/etc from C to ASM and from ASM to C.
So what do we need to do next? Is there maybe some easy way so we can deal with less blood (like, changing some TAGS somewhere, or find out where data is allocated and replace some FLAG in which will force making padding to necessary limits?)
As this game works width out hardware blitter, there has to be copy loop somewhere. 16bit alignment for bitmaps is common, so I'm guessing.
Move.w (a0)+,(a1)+
or it can be 32bit copy routine, less likely.
Move.l (a0)+,(a1)+
or something like that.
it can be just loaded in and trys to display it
Maybe add lots of print’s and see what part is executed, and narrow it down.
Graphic library WritePixelArray function is maybe not available for Assembler or 68k programs, (as that included later), maybe game is using cybergraphics or picasso96 then there is a WritePixelArray function that the game should have used.
(NutsAboutAmiga)
Basilisk II for AmigaOS4 AmigaInputAnywhere Excalibur and other tools and apps.
The one which we need to look at for now is ExodusTLW_V21.asm.
The game is from 2000-2001, so cyber graphics were used for sure as well (and in the readme for game and patches they say that CyberGraphics need it)
Anyway ... I do patch graphics and cybergraphics libraries, so when a call to any of their function happens, it will warn me.
And as result, there is _NO_ any WritePixelArray, or any kind of "Write" call is used.
At first, i think that something goes wrong with my patching of libs, but no, i can see clearly when for example "fadein/fadeout" effects happen, there are loads of LoadRGB32/WaitTOF calls (fade in / out effects of "palette" images back in times done exactly via LoadRGB32).
I also do check some of my stuff written for old days which use WPA (more precise WritePixelArray8) - and when i trace this one, calls are found. So it is safe to say that patching of libs works, just game for real doesn't use any kind of WPA.
I also can see that from CGX, only 2 functions are used: LockBitMapTagList() and UnLockBitMap().
There is full trace once first image loaded and i click on it, so it start "fade out" to show me menu screen, then i wait a bit and hit close (so also fade out and exit):
So, before image are "fade in" on screen, that what we have by graphics/cybergraphics functions calls (see for "gfx:" and "cgx:" marks at the begining):
I think that game may work this way: As there are no AllocBitMap or so calls, they just do load data to the memory and allocate just by exec's AllocVec() or even malloc().
They lock it as bitmap by Cgx's LockBitMapTagList and then work further with it.
And whole copy routines seems not WPA based of any form, but instead, they use their own (probably because back in the times WPA was considered as very slow, and people often made replacements for it)
Now... We need to find out where the copy routine in the disassembled code happens.
I may try to find out where LoadRGB32() call happens, maybe somewhere near of that function copy ones will be .. dunno.
Quote:
As this game works width out hardware blitter, there has to be copy loop somewhere. 16bit alignment for bitmaps is common, so I'm guessing.
Move.w (a0)+,(a1)+
or it can be 32bit copy routine, less likely.
Move.l (a0)+,(a1)+
In the whole source there are 7 places where "MOVE.L (A0)+,(A1)+" are used and in 3 functions they are used in a row 4 times one after another.
There is also 10 times "MOVE.B (A0)+,(A1)+" per one time in a function
As for "Move.w (a0)+,(a1)+" there none.
Edited by kas1e on 2022/1/12 22:51:22 Edited by kas1e on 2022/1/13 5:53:06 Edited by kas1e on 2022/1/13 8:40:56 Edited by kas1e on 2022/1/13 8:42:04 Edited by kas1e on 2022/1/13 8:42:46
Daniel a little bit help me with understanding, and the snipped we need are the first one: LockBitMapTagList() there just with a null-pointer check and then UnLockBitMap().
There we can see that after LockBitMapTagList() we do "move.l d0,lab_01ad", which mean we store our pointer to bitmap (lab_01ad are just 0x00000000, so probably place for storing pointer).
Then, before calling UnLockBitMap() we again move that pointer to D0/A0 meaning that this seems indeed a pointer to bitmap.
Now, i do check from where we call LockBitMapTagList(), and they're are just in 2 places, and the first one :
See "BSR.W LAB_01A7" its our call to LockBitMapTagsLIst. But then after you can see 4 "MOVE.L (A0)+,(A1)+".
And i just tried to comment them out one by one, and, seems that it! Each call is handle 25% of the data which draws on the screen.
That means, we now know probably where the actual copy to screen happens.
Now, how we can fix the no-padding issues, so it will work on Radeon RX/HD?
As the game uses hardcore 640x480, we don't need any "good" code which will cover anything else, just we need to shift data in some hardcore way so it will work as expected.
need to find destBytesPerRow some how, just test, type some value there. #82
maybe D4 needs to be put on stack... (to restore it, or maybe we can use different register)
Edited by LiveForIt on 2022/1/13 18:00:14 Edited by LiveForIt on 2022/1/13 18:33:34 Edited by LiveForIt on 2022/1/13 18:50:24 Edited by LiveForIt on 2022/1/13 18:52:24 Edited by LiveForIt on 2022/1/13 19:41:07 Edited by LiveForIt on 2022/1/13 19:41:50
(NutsAboutAmiga)
Basilisk II for AmigaOS4 AmigaInputAnywhere Excalibur and other tools and apps.
move.w d7,-(sp) ; I need another register, dunno if d7 is free, I'll store it on the stack
move.w #(480-1),D7 ; 480 rows
LAB_OUTER:
moveq #((640/4/4)-1),d5 ; we need 40*4 LONG-writes for one line, unrolled to 40 loops of 4 writes
LAB_019F:
MOVE.L (A0)+,(A1)+
MOVE.L (A0)+,(A1)+
MOVE.L (A0)+,(A1)+
MOVE.L (A0)+,(A1)+
DBF D5,LAB_019F
; here one line is done, now add our row padding
adda.w #128,A1
; next line
DBF D7, LAB_OUTER
; restore D7 from stack
move.w (sp)+,d7
But thanks for your time and help anyway too, without you i didn't think about those cybergraphics functions which lead us to find where actual copy blocks are :)
Now, we need to deal with skippable DSI in the menu. As it is skippable, I hope it will be not _that_ hard as this padding crap. That is what it gives to crash log:
As I see "DAR" is 0x00000000 are null, so that is a null-pointer crash. (If course, having 0x00000000 in the DAR from Petunia mean the same as we have when crashing on native PPC binaries.)
Now as we have disassembled in the crash log, i find out the place of the code where it crashes. There are:
The crash happens on RTS. I am not an expert so do not know why it may crash on RTS exactly (when it is just about to return to another function). Trashed stack ?
It looks like maybe the first two instructions were meant to be a NULL pointer check but the programmer did not take into account that moves with an address register as destination do not set condition flags.
On 68020+ I think you can do: MOVEA.L 14(A3),A2 TST.L A2 BEQ.W LAB_0758
But on 68000 CPU you will have to find a free data register and do something like: MOVE.L 14(A3),D0 MOVEA.L D0,A2 BEQ.W LAB_0758
but the programmer did not take into account that moves with an address register as destination do not set condition flags
We can't be sure about that without digging deeper. Maybe he did it on purpose and the BEQ tests the condition before the movea and he wanted to happen the move in any case. Maybe he just forgot the nullptr check.
@kas1e That's why this here would be an unintrusive way to add a nullptr check:
; just insert this
EXG D0,A2
TST.L D0
EXG D0,A2
BEQ.W LAB_0758
You can comment in / out the first BEQ.W. If the behaviour doesn't change for sure then salass00's asumption that the coder falsely asumed that movea changes cc is likely to be correct.