the best thing about ghidra is that no matter which version you're using, setting equates sometimes just doesn't work, but if you google it, you only find several bug reports which basically say "equates don't work" and then it's "closed, fixed in X.Y.Z" and that's a version you're upgraded way past

port_out(0x3c4,0xf02);

I SO WANT TO EQUATE 0x3C4 TO THE VGA SEQUENCER INDEX REGISTER BUT I CAN'T

the weird thing is the equates table says it's been set, says there's a reference to it at that address, but if I go to that address, there's nothing. just 0x3c4
OH GOODY I found a mistake in OSDEV
their docs on the PC gameport are incorrect
and I can't rename some local variables either!?
what the fuck, ghidra?

okay I've fixed OSDEV:
https://wiki.osdev.org/Game_port

They had the introduction of the gameport listed as "the PCjr" (it's from 1981, the PCjr came out in 1984... and didn't even have this gameport connector!) but more importantly, they had the order of the joystick axises wrong.

Game port - OSDev Wiki

and the IBM Game Control Adapter is mentioned in the August 1981 IBM PC Technical Reference, so IBM clearly had the gameport ready at day one of the IBM PC, even if it was an optional add-in.
mind you, on the IBM PC, just about everything is an optional add-in. You get a keyboard and a cassette port for free: you want graphics/text, floppies, hard drives, serial/parallel ports? that's extra.

god. Duke Nukem 1 has so much copy-pasted code.
Like, there's a ton of hint dialog boxes like "You need the key to open this door!" or "you get more points if you catch the balloon"

you'd think they'd just have ShowMessage() and pass it a different string, but nope. it's a different function.

LEARN TO WRITE REUSABLE FUNCTIONS

interesting. there's 12 hint-variables which are used to make sure you don't get hinted for something you've used before.

One of them is still maintained, but not used. So there must have been another hintable thing that got dropped

although I'm looking at DN1, episode 1. Maybe it does do something in DN1E2 or DN1E3?
because yeah this was also the era where you'd have 3 separate executables for 3 separate episodes of the same game

like, it has a function that loops through all 16 color indices and sets them to black. it takes one parameters, which tells it how long to wait between each color is set, so it'll fade slowly to black, instead of being instant.

there's also an identical function that does the same, but sets it to white, instead.

so instead of fade_screen(COLOR_BLACK) and fade_screen(COLOR_WHITE), it's two separate functions!
naturally instead of a function like "adjust_duke_health(n)" there's a "give_duke_one_health_point" function
todd replogle: look, I know programmers who use function parameters and data-driven design, and they're all cowards
The game starts with these two screens.
There's a function to draw Dr Proton, a different function to draw Duke Nukem, a function to draw Proton's text, and another function to draw Duke's

the only difference between the "draw proton"/"draw duke" functions is what base address they draw from.

YOU CAN PASS ADDRESSES TO FUNCTIONS, TODD

there are 5 of these functions in total.
Exactly identical, except they point to different buffers for different fullscreen images
oh god please don't tell me there's a hidden cheat code that's been missed for... 32 years?
@foone pls show me the code of a modern AAA where is no tape or glue to be found :D
@foone Gotta love Copy/Paste programming. No, wait, not love, what's the other thing...

@foone There are languages/formalisms where you can only pass one parameter to a function.

Maybe they were working in a language where you can't pass *any* parameters to a function?

I'm sure there's some mathematician that's managed it. Somewhere.

@darkling you'd think, but nope! this was compiled with Borland Turbo C.
@foone Real functions don't take parameters, they go reference a couple of globals and run off those.
@foone Could this be the result of compiler inlining?
@foone Oh wow, I didn't know you were on here! ❤️

@foone

I suspect this is a pragmatic decision done out of discomfort with passing pointers through the stack instead of treating it as a subroutine(which, as I recall, was a problem at the time - there was that one C book where the author did not understand how it worked and gave bad advice) - and it also allows for a top-down-ish "design once" approach of creating the whole sequence with placeholder stubs, and then finishing it with copy-paste-modify.

@foone the book i was thinking of is "Mastering C Pointers" https://wozniak.ca/blog/2018/06/25/1/index.html
Massacring C Pointers

@foone i wonder if eduke32 (the source port for duke nukem) changed any of these practices..
@foone If you're not explicitly setting the registers and then using JMP, you're just wasting your time, and mine.
@foone I love it. Parameter passing is slow and this circumvents it entirely! :D
@eniko it's not THAT slow!
especially when none of these are inner-loop things. They're all "draw some text and then wait for the user to push a key" things

@foone If it was fading each index (slowly changing to the target, rather than just switching one palette entry at a time), then I could understand making it two functions, as it probably makes the code a bit simpler.

Of course, if it's just:

for(int i=0; i<16; i++) { palette[i] = 0; }

then separate functions are less attractive.

Who said old code was more efficient? 

@foone If you think DN1 is badly written, don't look at DN3D's menu screen code.
@foone I bet you it's a compiler macro in the original source.
@darkling I really hope so

@foone Assuming C, it's a bit weird. You'd have to do something like:

DEF_DBOX(restore_which_game, "Restore which game?")

void some_func(...) {

int value = USE_DBOX(restore_which_game);
}

Not sure how that's any better than just writing a sane dbox function.

@foone AAAAAAAAAAAAAAAA MY EYES
@foone You know they teach people to specifically program like this in school, right?
@drwho WHAT SCHOOL?

@foone IUP. They kept beating us over the head with that in all the C++ courses.

Their curriculum is decidedly behind the times, or was when I went there. They cancelled their Java classes after one semester, but two semestrs of COBOL are required to graduate with a comp.sci degree.

Or were when I went there. It's been almost 25 years, to be honest.

@drwho @foone I do not regret my lack of formal education one bit
@foone I do, in fact, need_robohand
@foone They also have dialog_box_custom. Does that not let you pass a string, solving the rest of the problem?

@goofpunk not exactly. it passes some kind of number that's an index to a list of strings.

so... almost

@foone @goofpunk When I have seen this style of functions named the same way, it was in old old C code, and involved creating a map/array from a string to a function pointer. This was apparently a way to use static+const strings before const was a thing or to achieve interned strings like in e.g. C#. Maybe that was what was going on there.
@foone damn these people heard "dont repeat yourself" and wanted to rebel against that...
@foone *dialog_box_eat_turkey*, some of my finest work
@foone The best option, IMHO. Integrating the drive controllers... eh, okay, but other things, it's nice to have them separate. The sound chip on my current board started making Dalek mating calls one day, so I had to disable it, plugged in an Audigy 5/Rx. Been having issues with the two 8TB USB 3.0 drives connected, and might need a new card for that, but I'm out of PCIe slots.
@foone Wait, the default was cassette and not floppy ?
@foone FWIW, I believe in IDA this kind of thing mostly happens because they want to attach the new name to _something_ in the assembly code, so when the decompiler has introduced the variable, it's often not clear what it should be attached to, and things get weird and broken.
@dougall yeah I've had that happen. Not here, though. The variable it can't rename is just a standard register