VOGONS


CRT Terminator Digital VGA Feature Card ISA DV1000

Topic actions

Reply 120 of 236, by mkarcher

User metadata
Rank l33t
Rank
l33t
clb wrote on 2024-11-21, 19:20:
keenmaster486 wrote on 2024-11-21, 18:30:

Thanks for the quick reply. I tried your new DAC register tool, and it told me that it was already ENABLED. Running the tool had no effect on the palette.

Hmm, that's unfortunate. Re-reading the spec sheet, not quite sure where the issue would be, or if the Shadow DAC feature works somehow different than what I assume.

Your tool just got the logic the wrong way. If that bit is clear, the Cirrus chip will grab all palette writes, so the chipset is not forwarding it to the ISA bus. If that bit is set, the Cirrus chip will accept palette data, but not tell the chipset about it, so the chipset falls back to forwarding the palette writes to the ISA bus. This means you need a tool that sets the bit if it is clear, not a tool that clears the bit if it is set.

Reply 121 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie

Well of course I did. I got hung up on the "Reset: 1" field in the documentation and kept thinking that therefore 1=Disabled.

Updated the utility now to allow flipping the bit either way, that should help test more extensively.

Reply 123 of 236, by keenmaster486

User metadata
Rank l33t
Rank
l33t

This is what I'm getting now with the CLGD_DAC tool. "Unable to change DAC shadowing register bit"

The attachment Screenshot 2024-11-22 10-31-31.png is no longer available

World's foremost 486 enjoyer.

Reply 124 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie
keropi wrote on 2024-11-22, 17:24:

Just got my card, thanks clb!
Will eventually use it when I have more free time 😀

Awesome! Makes me chuckle to note we're purchasing vintage PC projects from each other 😀

keenmaster486 wrote on 2024-11-22, 17:32:

This is what I'm getting now with the CLGD_DAC tool. "Unable to change DAC shadowing register bit"

The attachment Screenshot 2024-11-22 10-31-31.png is no longer available

That means that the register bit is stuck in state. Browsing the spec PDF, it looks like there is a lock register at 3C5h/06h (extension register SR6) that might prevent access.

I updated the tool in GitHub once more to try to unlock that register first. I wonder if that makes any difference?

Reply 125 of 236, by keenmaster486

User metadata
Rank l33t
Rank
l33t
clb wrote on 2024-11-22, 18:15:

That means that the register bit is stuck in state. Browsing the spec PDF, it looks like there is a lock register at 3C5h/06h (extension register SR6) that might prevent access.
I updated the tool in GitHub once more to try to unlock that register first. I wonder if that makes any difference?

Same result, unfortunately.

World's foremost 486 enjoyer.

Reply 127 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie
keenmaster486 wrote on 2024-11-22, 18:29:
clb wrote on 2024-11-22, 18:15:

That means that the register bit is stuck in state. Browsing the spec PDF, it looks like there is a lock register at 3C5h/06h (extension register SR6) that might prevent access.
I updated the tool in GitHub once more to try to unlock that register first. I wonder if that makes any difference?

Same result, unfortunately.

Bummer. I'm a bit out of ideas on this one. Double and triple checked the code, but couldn't spot if it still might have some flaw.

Tried the program on a PCI-based CL-GD5434, which reports the bit to be 1 (enabled) already. There it does appropriately flip the bit between 0 and 1 - but it actually doesn't do anything (which I suppose is not that weird, since on PCI cards, the palette snoop is controlled by another register bit in the PCI configuration address space).

Reply 128 of 236, by keenmaster486

User metadata
Rank l33t
Rank
l33t

I have another Cirrus Logic based card that I could try instead. May put it in my machine and report back.

World's foremost 486 enjoyer.

Reply 129 of 236, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie
clb wrote on 2024-11-21, 20:51:
keenmaster486 wrote on 2024-11-21, 20:32:

One more thing I forgot to mention: the PALTSR tool takes up 7K of RAM. Couldn't this be much less? Seems like what it's doing isn't that complex.

I agree, and believe it could, if I only knew how. I get an impression that Borland Turbo C++ 3.0 that I used to create that is possibly not the best tool. Maybe handwriting the TSR fully in assembly would be the way to go for these types of things. I might try disassembling what Borland Turbo C++ 3.0 generated for that, and writing an assembler version of the same.

I had a quick look at the C++ source, I could probably rewrite it fairly easily as assembly so it could be built as a .com file using yasm/nasm which would make it a lot smaller, probably less than 1KB resident - would there be any restrictions on what instructions to use, e.g. what is the minimum cpu architecture it needs to run on?

Reply 130 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie
jmarsh wrote on 2024-11-22, 21:52:
clb wrote on 2024-11-21, 20:51:
keenmaster486 wrote on 2024-11-21, 20:32:

One more thing I forgot to mention: the PALTSR tool takes up 7K of RAM. Couldn't this be much less? Seems like what it's doing isn't that complex.

I agree, and believe it could, if I only knew how. I get an impression that Borland Turbo C++ 3.0 that I used to create that is possibly not the best tool. Maybe handwriting the TSR fully in assembly would be the way to go for these types of things. I might try disassembling what Borland Turbo C++ 3.0 generated for that, and writing an assembler version of the same.

I had a quick look at the C++ source, I could probably rewrite it fairly easily as assembly so it could be built as a .com file using yasm/nasm which would make it a lot smaller, probably less than 1KB resident - would there be any restrictions on what instructions to use, e.g. what is the minimum cpu architecture it needs to run on?

Wow, I would be so grateful to dip in to your knowledge, that would be fantastically interesting to learn from.

The PALTSR.EXE program can only ever be needed on ISA VLB or PCI adapters. It is never needed on older ISA VGA adapters. So that would make it 386 arch minimum I suppose, as 286s never had VLB bus?

However CRT Terminator is a 8-bit ISA card. So it is good to avoid using 16-bit I/O port writes when communicating with it.

Reply 132 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie

Yay, so glad to see that UPS un-screwed themselves. Hope the card meets all your expectations, and then some!

Reply 133 of 236, by keenmaster486

User metadata
Rank l33t
Rank
l33t

Seeing an issue when running VGA programs. It will frequently lose sync and glitch out for a split second, as in the attached video. So far I've observed this in Doom and Jazz Jackrabbit.

The attachment 2024-11-23 16-22-27 small.mp4.zip is no longer available

World's foremost 486 enjoyer.

Reply 134 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie

This kind of sync problem points towards either Hsync or Display Enable signal seeing glitches on the VGA adapter line. What were the jumper settings on the board? Something you can try is to disable automatic video sampling phase detection by setting J2 to 2-3 (Disabled). This enables the manual signal sampling phase detection jumpers J3-J6 to take effect.

After disabling automatic video sampling phase, then try to toggle J4 and J6 jumpers to find a combination that gives the best results. Were you testing with the short 15cm or long 30cm Feature Connector video cable?

Reply 135 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie

I wrote a new manual page to detail which settings to use in OBS to record "unconventional" 70Hz video footage that would aim to not miss any frames.

Paired with a YouTube example video upload (which does lossily resample the video down to 60Hz): https://www.youtube.com/watch?v=8o7cu9mYL10

Reply 136 of 236, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie
clb wrote on 2024-11-22, 23:05:

Wow, I would be so grateful to dip in to your knowledge, that would be fantastically interesting to learn from.

The PALTSR.EXE program can only ever be needed on ISA VLB or PCI adapters. It is never needed on older ISA VGA adapters. So that would make it 386 arch minimum I suppose, as 286s never had VLB bus?

However CRT Terminator is a 8-bit ISA card. So it is good to avoid using 16-bit I/O port writes when communicating with it.

This is what I ended up with, seems to output the correct data on the CRTT ports when used under DOSBox.

paltsr.asm
; compile cmdline: "yasm -f bin -o paltsr.com paltsr.asm"

BITS 16
org 0x100

; jmp near = 3 bytes, 4 bytes needed for old_isr (overwrites entry point jump)
old_isr:
jmp near init
nop

pal: times 256*3 db 0
crtt_pal_index: dw 0

update_palette:
; N.b. the Palette DAC has a boolean "am I in read mode"
; vs "am I in write mode" state. Reading port 0x3C7
; is supposed to tell us which one we are currently in,
; but that does not seem to work reliably. See
; http://www.osdever.net/FreeVGA/vga/colorreg.htm#3C7

mov dx, 0x3C8 ; dx = 0x3C8
in al, dx ; al = inp(0x3C8)
push ax ; old_dac_write_index
dec dx ; dx = 0x3C7
xor ax, ax
out dx, al ; Switch DAC to reading VGA palette index 0

add dx, 2 ; dx = 0x3C9
; si is used to index the local palette memory
; bh is used as a counter to iterate through the DAC palette entries
; di is used to cache the value of crtt_pal_index
mov si, pal
xor bx, bx ; set bh to zero
movzx di, [crtt_pal_index]
.loop:
in al, dx ; fetch red
movzx cx, al
xchg [si], al
cmp cl, al ; compare current red to old red, set/clear EQ flag

in al, dx ; fetch green
mov ch, al
xchg [si+1], al
jne .blue
cmp ch, al ; compare current green to old green, set/clear EQ flag

.blue:
in al, dx ; fetch blue
mov bl, al
xchg [si+2], al
jne .update ; if EQ is not clear, update CRTT
cmp bl, al ; compare current blue to old blue, set/clear EQ flag
je .update_done ; if EQ is clear don't update CRTT

.update:
; does the CRTT index need to be updated?
movzx ax, bh ; ax = i
cmp di, ax ; if (i != crtt_pal_index) {
je .skip_index_update

Show last 103 lines
  mov dx, 0x123             ; dx = 0x123
mov di, ax ; crtt_pal_index = i;
out dx, al ; outp(0x123, i)
; }

.skip_index_update:
mov dx, 0x124 ; dx = 0x124
; expand 6-bit -> 8-bit by copying 2 MSBs to LSBs
shrd ax, cx, 6
shrd ax, cx, 14
movzx cx, ch
out dx, al ; outp(0x124, red)
shrd ax, cx, 14
out dx, al ; outp(0x124, green)
shrd ax, bx, 6
shrd ax, bx, 14
inc di ; crtt_pal_index++
out dx, al ; outp(0x124, red)
mov dx, 0x3C9 ; dx = 0x3C9

.update_done:
inc bh ; increment index - will overflow to zero when 256 is reached
lea si, [si+3] ; increment palette pointer (without setting flags)
jne .loop

; Set DAC back to palette write mode, and set the old VGA palette write
; index. This assumes that games/programs never rely on the DAC being
; left in read mode, and that they disable interrupts while programming
; the palette.
; (because if interrupts would occur in the middle of writing G or B
; component, we cannot reset write index back to that color component)
pop ax ; al = old_dac_write_index
dec dx ; dx = 0x3C8
mov [crtt_pal_index], di ; save crtt pal index for next time
out dx, al ; outp(0x3C8, old_dac_write_index);

retn

timer_handler:
; push used registers
push ax
push bx
push cx
push dx
push si
push di
push ds
mov ax, cs
mov ds, ax

call update_palette

; pop used registers
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax

; we are done, branch to previous timer routine
jmp far [cs:old_isr]

init:
; disable interrupts
cli

; initialize crtt
xor ax, ax
mov dx, 0x123
out dx, al ; Sync initial CRT Terminator palette write index state
call update_palette

mov cx, 256
mov bx, pal
.loop: ; for (bx=&pal[0], i=256; i > 0; i--) {
xor byte [bx], 0x80 ; *bx.r ^= 0x80
add bx, 3 ; bx++;
loop .loop ; }

call update_palette

; enable interrupts
sti

; get old timer routine
mov ax, 0x351C
int 0x21
mov [old_isr], bx
mov [old_isr+2], es

; set new timer routine
mov dx, timer_handler
; ds should already be set to our (single) segment
mov ax, 0x251C
int 0x21

; terminate and stay resident, release all memory after init
mov dx, (init+15) >> 4
mov ax, 0x3100
int 0x21

You can build it into a .com file using yasm, the command line is at the top of the file. It takes up a little over 1KB of memory since the PSP is 256 bytes, another 768 bytes for the palette plus what's needed for the code.

Reply 137 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie

Thanks, this is so awesome. I gave it a test and it works really well. Memory consumption is 1,584 bytes, whereas with the CPP version compiled with Borland Turbo C++ 3.0 memory consumption was 8,072 bytes.

Gave a performance a test by running DOOM high res from Phil's DOS Benchmark Pack three times in each case: (lower is better)

No TSR (i.e. incorrect colors): 2798, 2798, 2810 realtics. Avg: 2806 realtics.
PALTSR.ASM: 2855, 2846, 2857 realtics. Avg: 2852.7 realtics. (+1.66% slower than no TSR)
PALTSR.CPP: 2851, 2864, 2853 realtics. Avg: 2856 realtics (+1.78% slower than no TSR)
PCI Palette Snoop enabled: 2801, 2813, 2800 (2804.7) (i.e. practically same as palette snooping disabled)

This test was done on a 80MHz Cyrix 486 PC with a PCI-based Hercules Stingray Pro (ARK1000PV) graphics card. The last test was done by enabling ISA Palette Snooping the intended way by flipping the Palette Snoop bit in PCI configuration register space, without using a TSR. This was to sanity check/baseline the performance effect of enabling palette snooping in general. I.e. in this game, there does not seem to be an adverse observable perf difference in enabling palette snooping. (palette snooping was disabled originally to save on performance, but it is very rare, or application dependent as to whether there is an observable perf penalty)

When using both PALTSR.ASM, it should be noted that the video output might not be completely glitch-free. Because palette updates are then unsynchronized with respect to drawing, there can be occassional frames where palette updates come out delayed by a frame or two.

Thank you so much jmarsh for developing this TSR. <3 I sent you a direct message as a follow-up.

Reply 138 of 236, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie

I did notice when I was testing that there were times the display palette would get corrupted during times of heavy updates e.g. fade-in/fade-outs such as splash screen or menu transitions in Descent. Pretty sure this is because the game doesn't disable interrupts when updating the palette and the timer/TSR is jumping in and resetting the update index in the middle of a write.

Does the VGA hardware typically update the DAC red and green values as they are written, or are those values stored temporarily and only get updated when the final/blue value is written?

Reply 139 of 236, by clb

User metadata
Rank Oldbie
Rank
Oldbie

Yeah, that is correct. The PALTSR approach is fundamentally fallible that way, and guarantees to be working only if the original game gates palette uploads inside an interrupts disabled block.

I did run tests way back to check on the "do VGA cards actually apply R/G/B palette colors after every single color component write, or only after the whole triplet?", and the result I got on a couple of cards that I tested (maybe CL-GD5422 and Tseng ET4000AX, I don't quite recall), was that the color was updated only after the final Blue component was updated. So if a sequence of red and green writes that might have been interrupted, but blue was not written, there wouldn't be any partial change. So I modeled that behavior to CRT Terminator FPGA's snooping of the palette registers as well.

Though I'm not sure if that behavior was ever specified in VGA, so it could be that one cannot guarantee that behavior to take place for all VGA adapters. But I think that is the source of the glitches that PALTSR gets, for example seen in Doom.

(Now that I think about this, that R/G partial upload behavior would be a semi-interesting quirk test to add to SNOOP, which has a bunch of these corner case quirk tests for various VGA adapters in it)

What makes the glitch worse is that when such an interruption occurs, the palette sub-index (are we on R, G or B) goes off sync "permanently" for the code that is resuming the palette upload after recovering from interrupts. So what happens is that the game will then program incorrect palette colors potentially for a long time to other palette indices, and might not get around to correcting it in quite a while, if ever. So the glitch leads to potentially a long period of incorrect colors, not just transient one frame.

One way that I thought to mitigate this problem might be for the PALTSR interrupt to use a heuristic : when the update interrupt it reads the currently active palette write index from 3C8h, it would compare it to what the active palette write index at the start of the previous interrupt run would be. If the previously active palette write index is different than the current one, the idea is to think "maybe we're right in the middle of an update", and the interrupt would skip mirroring the palette to CRTT. I.e. only when the palette index is seen to be identical twice in a row, the palette is mirrored to CRTT.

Actually, I now got encouraged to give that heuristic a try now in PALTSR.CPP, and at least running Doom timedemo 5 times, I did not see a single glitch (whereas before I would see some intermittent glitches every now and then). Maybe that heuristic might be useful - testing with more games will be needed. Nevertheless seems to work so well, so I pushed it to git: https://github.com/juj/crt_terminator/commit/ … ba100993R38-R46