First post here! I've been working on a simple DOS emulator - mainly as a learning exercise with the goal to build just enough to run the DOS version of "Oh No, more Lemmings". It's written in C# with an OpenGL shader to render the VGA frame buffer and seems to be working quite well so far.
But, I've hit on something a little intriguing... when the Lemmings start up options "VGA" and "High Performance PC" are chosen it appears to be doing some kind of "in frame" palette animation - by which I mean on every frame 8 palette registers seem to be re-programmed twice, presumably so the bottom part of the screen can use different colors to the main game area. ie: 24 colors in a 16 color video mode (mode 0x0d)
I noticed this because my emulator is rendering some colors in the bottom panel incorrectly and I can see continuous reprogramming of the palette registers. DOSBox gets it wrong too - unless the machine type is set to "vgaonly" in which case it works correctly.
I've not heard of this technique before and wondering if someone can explain how it works? How does it get the timing accurate enough to know when to flip the palette?
(Or, perhaps it's doing something else and I'm misinterpreting, but that's sure what it looks like)
Any clues appreciated.
Brad
Last edited by bradr on 2017-12-03, 05:10. Edited 1 time in total.
I think I figured this out. Looks like after detecting the vertical retrace it's setting up the programmable interval timer to raise an interrupt at the required scan line - at which point it reprograms the palette.
Interesting! I know of a few titles that do that - 1991 Donut by Scali (a member here) switches palettes twice in EGA mode - there are three palettes on-screen at once, and he used some really tight timing to pull it off. Hopefully he'll see this thread & chime in with some more details.
California Games does it in CGA and manages to get six colours onto the screen in 320x200x"4" mode. Note halfway down where it changes from cyan/red/white/black (already a 'tweak' palette IIRC) to red/yellow/green/black. Shame more games didn't use this back then.
twitch.tv/oldskooljay - playing the obscure, forgotten & weird - most Tuesdays & Thursdays @ 6:30 PM PDT. Bonus streams elsewhen!
For anyone who's interested in the details on how this is done, I did some reverse engineering. There are two places where this is used in Lemmings - first on the level intro screen and second in the game itself. Each uses a slightly different approach.
Level Intro Screen
The level intro screen runs in mode 10h (16 color, 640x350) uses one palette for the top 60 rows and another for the rest of the screen.
To determine when to switch palettes it just spins a busy loop waiting first for the horizontal retrace and then counts through 60 "display disabled" cycles. Display disabled is true once for everything after vertical retrace pulse until start of display and then once again at the end for each scan line during the horizontal blanking.
Row 60 in the above screen shot is immediately below last visible pixel the level preview screen - so there are quite a few blank lines before the second palette needs to be loaded.
1; Wait for vertical retrace 21060:7772 mov dx,word ptr [0x1FD6] 31060:7776 add dl,0x06 ; DX = 0x3DA (Video input status) 41060:7779 in al,dx 51060:777A test al,0x08 ; Bit 3 (0x08) = in vertical retrace 61060:777C jz 0x7779 71060:777E in al,dx 81060:777F test al,0x08 91060:7781 jnz 0x777E 101060:7783 cli 11 12; Load pallete A 131060:7784 push dx 141060:7785 mov al,0x00 151060:7787 call 0x7EC5 161060:778A pop dx 171060:778B cli 18 19; Wait for 60 scan lines 201060:778C mov cx,0x003C ; 0x3C = 60 211060:778F in al,dx 221060:7790 test al,0x01 ; Bit 0 (0x01) = display disabled (horiztonal blank) 231060:7792 jnz 0x778F 241060:7794 in al,dx 251060:7795 test al,0x01 261060:7797 jz 0x7794 271060:7799 loop 0x778F 28 29; Load pallete B 301060:779B mov al,0x07 311060:779D call 0x7EC5
In Game Screen
The in game screen runs in mode 0x0D (16 color 320x200) and uses one palette for the first 160 lines and another for the remaining 40 lines (the control panel at the bottom of the screen). Note that mode 0x0D is scan line doubled so the CRTC timing is based on 400 display rows.
Pixel row 160 (aka scan line 320) in the above screen shot is immediately below the play area and there are a couple of blank lines following which presumably provide enough time to reprogram the palette. Note too that the screen shot is from my emulator where this palette switching isn't implemented so the palette is wrong in the bottom 40 rows - most noticeably in the red pixels in the "OUT: IN: TIME:" display area.
This screen uses the Programmable Interval Timer (PIT) and some startup calibration. The calibration works like this:
1. Wait for vertical retrace
2. Setup PIT to count down from 65535
3. Wait for 320 display disabled cycles.
4. Stop the PIT timer
5. Read back the PIT counter
6. Subtract the read value from 65535 to get the PIT timer reload value to use while the game is running.
1; Measure how long it takes to get from end of vertical retrace pulse 2; to line 320 (ie: pixel row 160) 3 41060:7C63 mov cx,0xFFFF 5 6; wait for retrace 71060:7C66 mov dx,word ptr [0x1FD6] 81060:7C6A add dl,0x06 91060:7C6D in al,dx 101060:7C6E test al,0x08 111060:7C70 jz 0x7C6D 121060:7C72 in al,dx 131060:7C73 test al,0x08 141060:7C75 jnz 0x7C72 15 16; Start pit timer 171060:7C77 mov al,0x30 181060:7C79 out 0x43,al 191060:7C7B mov al,cl 201060:7C7D out 0x40,al 211060:7C7F mov al,ch 221060:7C81 out 0x40,al 23 24; Wait for 320 horizontal blanks 251060:7C83 mov cx,bx 261060:7C85 in al,dx 271060:7C86 test al,0x01 281060:7C88 jnz 0x7C85 291060:7C8A in al,dx 301060:7C8B test al,0x01 311060:7C8D jz 0x7C8A 321060:7C8F loop 0x7C85 33 34; Stop PIT timer 351060:7C91 mov al,0x06 361060:7C93 out 0x43,al 371060:7C95 mov al,0x36 381060:7C97 out 0x43,al 391060:7C99 nop 401060:7C9A nop 411060:7C9B nop 42 43; Read value 441060:7C9C in al,0x40 451060:7C9E mov cl,al 461060:7CA0 in al,0x40 471060:7CA2 mov ch,al 481060:7CA4 ret
The in game PIT interrupt handler then fires whenever scan line 320 is hit and does the following:
1. Load palette B
2. Set screen start address (for page flipping)
3. Wait for vertical retrace
4. Restart the PIT timer using the value calculated during calibration.
5. Load palette A
6. Other undetermined logic (screen update, sound perhaps, not sure...)
FWIW, I got this working nicely in my emulator now. It's a subtle improvement over the non-palette switching version but I think worth the extra bit of effort:
That's because for CGA, it only works at 4.77 MHz and even then is very complicated.
CGA is actually simpler than EGA/VGA, because CGA is synchronized to the PIT.
You won't have to re-set the PIT every frame, because you know that a CGA frame is exactly 19912 PIT ticks at all time.
8088 MPH exploits this characteristic in some areas, such as the title screen rolldown and the DeLorean sprite part.
For EGA/VGA (like with 1991 Donut), the video card has its own clock generator, and as such it runs asynchronously. This is why I had to re-set my timer at every vertical blank in 1991 Donut, because otherwise the timing of the PIT and the CRTC would drift apart after a few frames, and you'd actually see the palette changes shift up or down the screen.
I don't think it was too common on PC, especially on CGA, because it's a very limited technique. California Games is the only game I know of that uses it, and even there it is only used in one screen. It just happened to work there.
Also, I find it interesting that Lemmings actually rewrites the palette itself.
In 1991 Donut I exploit a feature of VGA which allows you to create multiple palette banks. Especially on slow PCs, it is impossible to switch all 16 colours of the palette in the horizontal blank interval, or even within the time of an entire scanline. So you got visual aliasing.
By setting up the palette banks in advance, I needed far fewer writes to the VGA registers to switch to 16 new colours, which even an 8088 at 4.77 MHz could do without aliasing.
I wondered about that too. Even just rewriting the palette map in the attribute controller I would have thought would be simpler.
Random guessing: Because the game is a port of an Amiga game, were the coper chip is routinely used to re-program the palette on the flight ?
So it seemed the most simple to replicate the behaviour ?
(As opposed to hardware where palette switching per scan-line is the practice - as in Apple IIGs, I've read ?)
Incidentally, the Apple IIgs port of Lemmings uses the Atari ST graphics (Brutal Deluxe had the luxury of picking the best features of each port ex: the music is from the DOS version). That system's graphics are more in line with the IIgs. The IIgs VGC can switch between 16 user programmed palettes on a scanline basis. Further tricks with swapping palettes on the fly (racing the beam) can further increase the total color output to 3200 colors (one unique 16 color palette per scanline).