VOGONS


First post, by markot

User metadata
Rank Member
Rank
Member

I would like to make a patch for Hard Hat Mack game so it would run better on newer computers without the speed issue. I'm no expert i in assembly language, so how difficult would it be to add some kind of delay to the game loop of the game? The DOS version of the game is about 40 kbytes in size, so it should not be so difficult to make it work good on newer systems. I have successfully removed copy protections from some older DOS games by just adding NOP instructions, but adding a delay in the middle of the code probably requires much more things to change when the file size grows.

So, where could I find information how to modify MS-DOS .COM files by adding code to an existing program?

Reply 1 of 13, by Jorpho

User metadata
Rank l33t++
Rank
l33t++
markot wrote:

I'm no expert i in assembly language, so how difficult would it be to add some kind of delay to the game loop of the game?

About as difficult as becoming an expert in assembly language. 🤣

The DOS version of the game is about 40 kbytes in size, so it should not be so difficult to make it work good on newer systems.

The size of the file doesn't really correlate with the difficulty. In fact, a smaller file may indicate more highly-optimized code that would be more difficult to patch.

Reply 3 of 13, by VileR

User metadata
Rank l33t
Rank
l33t

Just inserting more delay wouldn't really help, at least with this particular game, because it'd still be CPU-dependent - you may be able to get the speed you want on *one* newer computer, but not on all of them. Might as well just use DOSBox, as suggested above. Or if you plan to run the game in native DOS, use one of those ubiquitous slowdown programs like MoSlo or Bremze.

You *could* try to patch the game logic so that timing is done by something constant, like the screen refresh. But that'd take some working knowledge of debugging, disassembly and assembly, and (as you note) you'd need to find a way and a place to insert extra code. The latter is not impossible -- in fact, if you look at an *unpacked* Hard Hat Mack .COM you can see some 'easter egg' text from the authors which isn't used anywhere in the game, and gives you around a hundred bytes you could use for your own instructions (that's what I used in my own patch). 😁

That may be a fun project if you want to practice your skillz, but ultimately not really useful since you're better off using either DOSBox, or some slowdown utility.

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 4 of 13, by keenmaster486

User metadata
Rank l33t
Rank
l33t

VileR, I like your new avatar.

World's foremost 486 enjoyer.

Reply 5 of 13, by ripa

User metadata
Rank Oldbie
Rank
Oldbie

This is off the top of my head, so it might be incorrect, but here goes. I would write a simple delay routine and test it using a standalone test program in Dosbox to make sure it works with different CPU speeds. I would then inject it into the original executable in a suitable place. To find a suitable place (places), run the game in Dosbox and use the debugger to find the main loop(s).

I haven't done this before, but this might work. The main loop probably calls several subroutines - pick one of the subroutine calls and replace the call site with a call to a wrapper subroutine which calls the original subroutine AND your delay subroutine. You should be able to append your wrapper subroutine and your delay subroutine after the original code and data. You have plenty of space in the executable for your injected subroutines since the executable is 40 KiB (COM is limited to 64 KiB).

I'm not sure what tools to use to patch the executable. Maybe disassemble the COM file with IDA Freeware and try re-assembling it with your modifications.

When modifying executables like this, it's important that you don't add or remove anything within a section/segment, because that messes up addresses. You can append stuff at the end or prepend stuff at the beginning, or replace things such as instructions or addresses in the middle as long as the number of bytes doesn't change. Also, your wrapper subroutine must have the same calling convention as the subroutine that it's replacing - that means preserving certain registers.

Please ask if you have further questions - I'll try to answer. I've also struggled to understand this stuff by myself 😀

Reply 6 of 13, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

It does not have to be a subroutine call you hook into.

Just at the start of the main game loop, you can patch a jump to your piece of code, and in your code, you replicate what you overwrote, perform the delay, and then jump back to run the game loop.

I'd make the delay routine to wait for a timer interrupt to happen. For instance in this case it should just be enough to wait until the BIOS timer tick counter has changed from previous read. Then it only depends on timer interrupt rate, and you can reprogram the timer rate to any value you want.

Reply 7 of 13, by VileR

User metadata
Rank l33t
Rank
l33t
ripa wrote:

You have plenty of space in the executable for your injected subroutines since the executable is 40 KiB (COM is limited to 64 KiB).

Probably not wise to count on that - the game code may be counting on that too and using the some of that segment space for stack/scratch space/other stuff.

[ WEB ] - [ BLOG ] - [ TUBE ] - [ CODE ]

Reply 8 of 13, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

True. I recall the whole memory (first available block it fits into) is allocated for .COM program.
And just yesterday I built a few hundred byte COM file with almost 63 kilobytes of BSS section which is not stored into file, but used as buffer area.

Reply 9 of 13, by NewRisingSun

User metadata
Rank Oldbie
Rank
Oldbie

A proper frame limiter will be based on a source that is independent from machine speed. The most common source is the 8253 Programmable Interval Timer, as Jepael suggested.

For example, if you patch the game to call the following subroutine each game cycle:

FrameLimit	proc
@@: mov ah, 0 ; Get Tick Count into CX:DX
int 1Ah ; Time of Day
mov ax, dx ; Save low word for later

; Determine if at least one tick has passed
; since FrameLimit was called the last time.
sub dx, [LastTickCount]
jnz @f ; At least one tick passed, wait no longer
hlt ; Wait until an interrupt request occurs
jmp @b ; Try again

; Restore low word and save it as a comparison value for next time
@@: mov [LastTickCount], ax
ret
FrameLimit endp
LastTickCount word ?

The game will be locked to a frame rate of 18.2 Hz, independently of the machine speed. That is, unless the machine is too slow to even achieve an 18.2 Hz frame rate, in case of which the routine just does not wait at all.

Reply 10 of 13, by Great Hierophant

User metadata
Rank l33t
Rank
l33t

Gemini's recent review of Jill of the Jungle identifies that game as using an 18.2 frames per second frame rate. I assume that the video controller will keep on displaying whatever is in the video buffer until you change it, even though it is sending frames to the monitor faster than your game. As long as the screen remains static, like in Hard Hat Mack, there should be few issues. But wouldn't there be screen tearing when the screen scrolls because 18.2Hz is not an even multiple of 60Hz or 70Hz?

http://nerdlypleasures.blogspot.com/ - Nerdly Pleasures - My Retro Gaming, Computing & Tech Blog

Reply 11 of 13, by NewRisingSun

User metadata
Rank Oldbie
Rank
Oldbie

Oh sure. But tearing is much less noticable if your update rate is far lower than the frame rate.

Reply 12 of 13, by MERCURY127

User metadata
Rank Member
Rank
Member

But wouldn't there be screen tearing when the screen scrolls because 18.2Hz is not an even multiple of 60Hz or 70Hz?

8253 use 1193182 Hz as base clock. 18.2 Hz is just minimal possible frequency with 16 bit divider: 1193182 / 65535 = 18.2.
U can reprogram channel 0 for any need frequency. 19886 divider give u just perfect 60 Hz. But u should remember, that this 18.2 Hz is standard for all programs in PC, what related with clocks - DOS clock, BIOS ticks, game clock... So if u change channel 0 divider to 60 Hz, u get 3 times more fast clocks anywere. 🤣

Reply 13 of 13, by LSS10999

User metadata
Rank Oldbie
Rank
Oldbie

I'm not an expert in assembly, but I want to ask about the possible methods for programs to get information about the system's CPU clock speed, as in Thor's Hammer Trilogy by Escape Programming, the game seems to use the system's CPU clock speed as some kind of divider that controls the player's movement speed as well as speed for opening doors and so on (something like setting how many frames per sec based on the running system's CPU clock speed). At low CPU clock speed (like 233MHz), it results in a rough but fast movement, but when the CPU speed goes higher, the movement becomes slower until to a point of being too slow to be playable. Other parts of the game (even including enemy movements) are not affected by CPU clock speed.

I'm asking about this because I'm trying to disassemble the program and look for what it could be dividing against (it may not necessarily be the CPU clock itself, but some kind of counter that's proportional to it).

Slowdown software don't work well with it since they also slow the aspects that don't need to be slowed, so it's better patched so it could be made to divide against a constant value resulting in consistent speed.

(PS, this is just a speculation as I'm still looking into such possibility. As there are no known clues (immediate values) it's not going to be easy to figure out)