When should I take pre-orders for the final version of Dexter? December is a busy time of year, so I am leaning toward January, but maybe December would be better. What do you think?
Vote here .
Results here .
Sunday, November 29, 2015
Tuesday, November 24, 2015
I've been having email problems.
I've had email problems for at least a month with the email account linked to my Dexter "let me know when it's ready" link on the Dexter web page. So if you've tried to send me an email that way, I probably did not receive it. I'm very sorry for the inconvenience. It should be fixed now!
Thursday, November 19, 2015
Star Rider clever FIRQ handler revealed
In furtherance of my goal to get Star Rider working with Dexter, I decoded the FIRQ handler in the Star Rider main CPU's ROM. FIRQ stands for Fast IRQ and is an alternate interrupt available on the 6809E CPU which is "fast" because it does not push all of the registers on the stack before executing (meaning the programmer will have to assume this responsibility instead but has the option to use the registers cleverly to squeeze out some extra performance).
The Star Rider FIRQ handler is pretty nifty because it is designed to be called when the hardware blitter "special" chips have finished an operation and immediately give the blitter "special" chips more work to do. The neat thing about this design is that the main CPU can still be doing other work while the blitter chips are running. In previous Williams' games, such as Joust, the blitter chip halts the main CPU and no work can be done while the blitter chip is doing its thing. With Star Rider, the hardware guys (apparently) realized that halting the main CPU was a potential waste of time and instead let the main CPU keep running while simultaneously disabling the main CPU's access to the video/DMA bus. It's a really clever design for 1983/1984 or whenever.
Also cool is that the FIRQ handler can be called without an interrupt occurring (see $EAD9). If called in this way, the program will mimic what the CPU does for a real FIRQ so that the RTI (return from interrupt) instruction works correctly regardless of whether there was a real FIRQ or if the program faked it.
Finally, the FIRQ handler is even more nifty because it has an optional "wait for vsync" type behavior built-in. It can optionally check to see whether the next blit will cause visible vertical tearing and if so delay the blit until this is no longer a concern. I am not sure if this code actually ends up getting used in production because, just from eyeballing it, I am not 100% convinced that the vertical counter hardware value matches up with the actual vertical line value (maybe it does!) and the ROM program definitely seems to be making this assumption. Decide for yourself :)
ROM:EAD9 ; =============== S U B R O U T I N E =======================================
ROM:EAD9
ROM:EAD9 ; Execute the FIRQ interrupt handler without the FIRQ interrupt firing.
ROM:EAD9 ; This appears to create an environment where RTI will act like RTS.
ROM:EAD9 ; NOTE: it appears that IRQ's are not disabled
ROM:EAD9
ROM:EAD9 FakeFIRQ: ; CODE XREF: DoBlit:loc_0_EA90 P
ROM:EAD9 ; sub_0_EAB4+18 P
ROM:EAD9 tfr cc, a
ROM:EADB anda #$7F ; '' ; disable E flag (for subsequent RTI call)
ROM:EADD pshs a ; save CC flags (For subsequent RTI call)
ROM:EADF orcc #%1000000 ; disable F flag (for subsequent RTI call)
ROM:EAE1
ROM:EAE1 FIRQ: ; DATA XREF: ROM:FFF6 o
ROM:EAE1 tst PIA_VGG_U7_PERFDIR_A ; clear PIA's IRQA'
ROM:EAE4 dec BlitterPendingOperationsCount ; When FIRQ gets called, it means a blitter operation just finished.
ROM:EAE4 ; So decrement the pending count.
ROM:EAE7 beq FIRQ_End ; branch if we have no more blitter operations left
ROM:EAE9 pshs a,b,x ; save regs (since this is FIRQ, they won't be automatically saved)
ROM:EAEB ldx BlitterContextPtr ; points to the current blitter context to be sent to the blitter hardware
ROM:EAEE lda BlitDontAvoidTearing ; 0: try to avoid vsync tearing artifacts
ROM:EAEE ; 1: blit as fast as possible ignoring vertical tearing
ROM:EAEE ; (see EAEE)
ROM:EAF1 bne DoBlitAndAdvanceBlitterContextPtr ; X points to the blitter context
ROM:EAF3 lda 8,x
ROM:EAF5 bmi DoBlitAndAdvanceBlitterContextPtr ; branch if IMPG value has high-bit set
ROM:EAF7 lda 5,x ; A = blitter destination Y value
ROM:EAF9 adda 7,x ; add blit size height to A
ROM:EAF9 ; This makes A now the bottom coordinate of the blit rectangle
ROM:EAFB suba HW_VerticalCounter ; Subtract current vertical counter value from blit bottom Y value.
ROM:EAFB ; If positive, beam is above bottom of blit can tearing can occur.
ROM:EAFB ; If negative, beam is below bottom of blit and tearing is not a risk.
ROM:EAFB ; AT LEAST that's what it is looking like to me! :)
ROM:EAFE bls DoBlitAndAdvanceBlitterContextPtr ; branch if tearing is not a concern
ROM:EB00 ldb #65 ; this appears to create a new blit size
ROM:EB00 ; of up to 64x65 pixels, but can be less.
ROM:EB00 ; It appears to be a way to stall to avoid tearing artifacts on the screen.
ROM:EB02 cmpa #64
ROM:EB04 bcs DoNullBlit ; if width is already less than 64 pixels, branch
ROM:EB06 lda #64 ; force width down to 64 pixels
ROM:EB08
ROM:EB08 DoNullBlit: ; CODE XREF: FakeFIRQ+2B j
ROM:EB08 std HW_BlitterSize
ROM:EB0B inc BlitterPendingOperationsCount ; make sure next time FIRQ fires, the blit we didn't perform gets redone
ROM:EB0E lda #%11000000 ; this appears to execute a NOP to the blitter chips by suppressing all writes. It may be to queue up a future FIRQ.
ROM:EB10 sta HW_BlitterControl ; writing here executes the blitter chips.
ROM:EB10 ; Control Bits
ROM:EB10 ; 7: 1=write suppress odd pixels
ROM:EB10 ; 6: 1=write suppress even pixels
ROM:EB10 ; 5: 1=shift image 1 pixel to the right (odd flavor)
ROM:EB10 ; 4: 1=solid color mode (constant substitution)
ROM:EB10 ; 3: 1=zero write suppress (only blit non-zero color values)
ROM:EB10 ; 2: 1=sync to E clock (half speed writes, RAM to RAM)
ROM:EB10 ; 1: write format (0=serial, 1=block)
ROM:EB10 ; 0: read format (0=serial, 1=block)
ROM:EB13 bra FIRQ_RestoreRegsThenEnd
ROM:EB15 ; ---------------------------------------------------------------------------
ROM:EB15
ROM:EB15 DoBlitAndAdvanceBlitterContextPtr: ; CODE XREF: FakeFIRQ+18 j
ROM:EB15 ; FakeFIRQ+1C j ...
ROM:EB15 ldd 8,x ; X points to the blitter context
ROM:EB17 std HW_IMPG_PRIME
ROM:EB1A ldd 6,x
ROM:EB1C std HW_BlitterSize
ROM:EB1F ldd 4,x
ROM:EB21 std HW_BlitterDest
ROM:EB24 ldd 2,x
ROM:EB26 std HW_BlitterSource
ROM:EB29 ldd ,x
ROM:EB2B stb HW_BlitterSolidClr
ROM:EB2E sta HW_BlitterControl ; writing here executes the blitter chips.
ROM:EB2E ; Control Bits
ROM:EB2E ; 7: 1=write suppress odd pixels
ROM:EB2E ; 6: 1=write suppress even pixels
ROM:EB2E ; 5: 1=shift image 1 pixel to the right (odd flavor)
ROM:EB2E ; 4: 1=solid color mode (constant substitution)
ROM:EB2E ; 3: 1=zero write suppress (only blit non-zero color values)
ROM:EB2E ; 2: 1=sync to E clock (half speed writes, RAM to RAM)
ROM:EB2E ; 1: write format (0=serial, 1=block)
ROM:EB2E ; 0: read format (0=serial, 1=block)
ROM:EB31 leax 10,x ; advance to the next blitter context in the array
ROM:EB31 ; (each context is 10 bytes long)
ROM:EB33 cmpx #word_0_AC1C ; have we got to the end of the blitter contexts?
ROM:EB36 bcs StoreNewBlitterContextPointer
ROM:EB38 ldx #BlitterContextArray ; a 1000 byte array which has room for 100 10-byte blitter context entries.
ROM:EB3B
ROM:EB3B StoreNewBlitterContextPointer: ; CODE XREF: FakeFIRQ+5D j
ROM:EB3B stx BlitterContextPtr ; points to the current blitter context to be sent to the blitter hardware
ROM:EB3E
ROM:EB3E FIRQ_RestoreRegsThenEnd: ; CODE XREF: FakeFIRQ+3A j
ROM:EB3E puls x,b,a
ROM:EB40
ROM:EB40 FIRQ_End: ; CODE XREF: FakeFIRQ+E j
ROM:EB40 rti
ROM:EB40 ; End of function FakeFIRQ
The Star Rider FIRQ handler is pretty nifty because it is designed to be called when the hardware blitter "special" chips have finished an operation and immediately give the blitter "special" chips more work to do. The neat thing about this design is that the main CPU can still be doing other work while the blitter chips are running. In previous Williams' games, such as Joust, the blitter chip halts the main CPU and no work can be done while the blitter chip is doing its thing. With Star Rider, the hardware guys (apparently) realized that halting the main CPU was a potential waste of time and instead let the main CPU keep running while simultaneously disabling the main CPU's access to the video/DMA bus. It's a really clever design for 1983/1984 or whenever.
Also cool is that the FIRQ handler can be called without an interrupt occurring (see $EAD9). If called in this way, the program will mimic what the CPU does for a real FIRQ so that the RTI (return from interrupt) instruction works correctly regardless of whether there was a real FIRQ or if the program faked it.
Finally, the FIRQ handler is even more nifty because it has an optional "wait for vsync" type behavior built-in. It can optionally check to see whether the next blit will cause visible vertical tearing and if so delay the blit until this is no longer a concern. I am not sure if this code actually ends up getting used in production because, just from eyeballing it, I am not 100% convinced that the vertical counter hardware value matches up with the actual vertical line value (maybe it does!) and the ROM program definitely seems to be making this assumption. Decide for yourself :)
ROM:EAD9 ; =============== S U B R O U T I N E =======================================
ROM:EAD9
ROM:EAD9 ; Execute the FIRQ interrupt handler without the FIRQ interrupt firing.
ROM:EAD9 ; This appears to create an environment where RTI will act like RTS.
ROM:EAD9 ; NOTE: it appears that IRQ's are not disabled
ROM:EAD9
ROM:EAD9 FakeFIRQ: ; CODE XREF: DoBlit:loc_0_EA90 P
ROM:EAD9 ; sub_0_EAB4+18 P
ROM:EAD9 tfr cc, a
ROM:EADB anda #$7F ; '' ; disable E flag (for subsequent RTI call)
ROM:EADD pshs a ; save CC flags (For subsequent RTI call)
ROM:EADF orcc #%1000000 ; disable F flag (for subsequent RTI call)
ROM:EAE1
ROM:EAE1 FIRQ: ; DATA XREF: ROM:FFF6 o
ROM:EAE1 tst PIA_VGG_U7_PERFDIR_A ; clear PIA's IRQA'
ROM:EAE4 dec BlitterPendingOperationsCount ; When FIRQ gets called, it means a blitter operation just finished.
ROM:EAE4 ; So decrement the pending count.
ROM:EAE7 beq FIRQ_End ; branch if we have no more blitter operations left
ROM:EAE9 pshs a,b,x ; save regs (since this is FIRQ, they won't be automatically saved)
ROM:EAEB ldx BlitterContextPtr ; points to the current blitter context to be sent to the blitter hardware
ROM:EAEE lda BlitDontAvoidTearing ; 0: try to avoid vsync tearing artifacts
ROM:EAEE ; 1: blit as fast as possible ignoring vertical tearing
ROM:EAEE ; (see EAEE)
ROM:EAF1 bne DoBlitAndAdvanceBlitterContextPtr ; X points to the blitter context
ROM:EAF3 lda 8,x
ROM:EAF5 bmi DoBlitAndAdvanceBlitterContextPtr ; branch if IMPG value has high-bit set
ROM:EAF7 lda 5,x ; A = blitter destination Y value
ROM:EAF9 adda 7,x ; add blit size height to A
ROM:EAF9 ; This makes A now the bottom coordinate of the blit rectangle
ROM:EAFB suba HW_VerticalCounter ; Subtract current vertical counter value from blit bottom Y value.
ROM:EAFB ; If positive, beam is above bottom of blit can tearing can occur.
ROM:EAFB ; If negative, beam is below bottom of blit and tearing is not a risk.
ROM:EAFB ; AT LEAST that's what it is looking like to me! :)
ROM:EAFE bls DoBlitAndAdvanceBlitterContextPtr ; branch if tearing is not a concern
ROM:EB00 ldb #65 ; this appears to create a new blit size
ROM:EB00 ; of up to 64x65 pixels, but can be less.
ROM:EB00 ; It appears to be a way to stall to avoid tearing artifacts on the screen.
ROM:EB02 cmpa #64
ROM:EB04 bcs DoNullBlit ; if width is already less than 64 pixels, branch
ROM:EB06 lda #64 ; force width down to 64 pixels
ROM:EB08
ROM:EB08 DoNullBlit: ; CODE XREF: FakeFIRQ+2B j
ROM:EB08 std HW_BlitterSize
ROM:EB0B inc BlitterPendingOperationsCount ; make sure next time FIRQ fires, the blit we didn't perform gets redone
ROM:EB0E lda #%11000000 ; this appears to execute a NOP to the blitter chips by suppressing all writes. It may be to queue up a future FIRQ.
ROM:EB10 sta HW_BlitterControl ; writing here executes the blitter chips.
ROM:EB10 ; Control Bits
ROM:EB10 ; 7: 1=write suppress odd pixels
ROM:EB10 ; 6: 1=write suppress even pixels
ROM:EB10 ; 5: 1=shift image 1 pixel to the right (odd flavor)
ROM:EB10 ; 4: 1=solid color mode (constant substitution)
ROM:EB10 ; 3: 1=zero write suppress (only blit non-zero color values)
ROM:EB10 ; 2: 1=sync to E clock (half speed writes, RAM to RAM)
ROM:EB10 ; 1: write format (0=serial, 1=block)
ROM:EB10 ; 0: read format (0=serial, 1=block)
ROM:EB13 bra FIRQ_RestoreRegsThenEnd
ROM:EB15 ; ---------------------------------------------------------------------------
ROM:EB15
ROM:EB15 DoBlitAndAdvanceBlitterContextPtr: ; CODE XREF: FakeFIRQ+18 j
ROM:EB15 ; FakeFIRQ+1C j ...
ROM:EB15 ldd 8,x ; X points to the blitter context
ROM:EB17 std HW_IMPG_PRIME
ROM:EB1A ldd 6,x
ROM:EB1C std HW_BlitterSize
ROM:EB1F ldd 4,x
ROM:EB21 std HW_BlitterDest
ROM:EB24 ldd 2,x
ROM:EB26 std HW_BlitterSource
ROM:EB29 ldd ,x
ROM:EB2B stb HW_BlitterSolidClr
ROM:EB2E sta HW_BlitterControl ; writing here executes the blitter chips.
ROM:EB2E ; Control Bits
ROM:EB2E ; 7: 1=write suppress odd pixels
ROM:EB2E ; 6: 1=write suppress even pixels
ROM:EB2E ; 5: 1=shift image 1 pixel to the right (odd flavor)
ROM:EB2E ; 4: 1=solid color mode (constant substitution)
ROM:EB2E ; 3: 1=zero write suppress (only blit non-zero color values)
ROM:EB2E ; 2: 1=sync to E clock (half speed writes, RAM to RAM)
ROM:EB2E ; 1: write format (0=serial, 1=block)
ROM:EB2E ; 0: read format (0=serial, 1=block)
ROM:EB31 leax 10,x ; advance to the next blitter context in the array
ROM:EB31 ; (each context is 10 bytes long)
ROM:EB33 cmpx #word_0_AC1C ; have we got to the end of the blitter contexts?
ROM:EB36 bcs StoreNewBlitterContextPointer
ROM:EB38 ldx #BlitterContextArray ; a 1000 byte array which has room for 100 10-byte blitter context entries.
ROM:EB3B
ROM:EB3B StoreNewBlitterContextPointer: ; CODE XREF: FakeFIRQ+5D j
ROM:EB3B stx BlitterContextPtr ; points to the current blitter context to be sent to the blitter hardware
ROM:EB3E
ROM:EB3E FIRQ_RestoreRegsThenEnd: ; CODE XREF: FakeFIRQ+3A j
ROM:EB3E puls x,b,a
ROM:EB40
ROM:EB40 FIRQ_End: ; CODE XREF: FakeFIRQ+E j
ROM:EB40 rti
ROM:EB40 ; End of function FakeFIRQ
Monday, November 16, 2015
Dexter Star Rider search bug solution
My birthday party is over (don't think that I mentioned that on here) so I now am turning my focus back to shipping Dexter.
Star Rider has almost worked perfectly with Dexter for quite some time and I had the opportunity to do more Star Rider research recently and here's some of my interesting findings.
First of all, the reason that Dexter wasn't working was because during the self-test, it would routinely fail with a search to frame 6 (as I recall) but would usually be able to successfully seek to other test frames on the disc. Why was it always failing to seek to frame 6?
Well, after much studying of the ROM disassembly, I think I have a pretty good grasp of how it works.
First of all, to initiate a seek, the Star Rider program calls a function at $7E75:
ROM3:7E75 ; =============== S U B R O U T I N E =======================================
ROM3:7E75
ROM3:7E75 ; frame number stored in D
ROM3:7E75 ; on return carry may be set if advance button was pressed
ROM3:7E75
ROM3:7E75 InitiateDiscSearch: ; CODE XREF: DiscTestSearchTo+2F P
ROM3:7E75 ; ROM3:7DE7 P ...
ROM3:7E75 pshs x,y,u
ROM3:7E77 pshs a,b
ROM3:7E79 clra
ROM3:7E7A clrb
ROM3:7E7B std PifSendError ; Will contain an error code (such as 0x507) if trying to send commands to the PIF board or read laserdisc VBI data failed.
ROM3:7E7B ; (see 19e2). Otherwise it will be 0.
ROM3:7E7E lda #$17
ROM3:7E80 sta PifResponseRetryCount
ROM3:7E83 puls b,a
ROM3:7E85 jsr SetupPifBuffersForSearching ; It appears that this is a "search to frame" function, where the frame is stored in D
ROM3:7E85 ; (A1BC needs to get a non-zero value stored to it or else it's an error)
ROM3:7E85 ; A1A4: 0x5 0x5 0x13 FrameHi FrameLo 0x5
ROM3:7E85 ; A1B4: FrameHi FrameLo
ROM3:7E85 ; A1BC: 0x13 0x0A 0x64
ROM3:7E85 ;
ROM3:7E85 ; A1A7 and A1B4 = D
ROM3:7E85 ; A1A4 = 0x505
ROM3:7E85 ; A1A6,A1BC = 0x13
ROM3:7E85 ; A1BD = 0xA64
ROM3:7E85 ; A1A9 = 5
ROM3:7E88
ROM3:7E88 WaitSearchFinish: ; CODE XREF: InitiateDiscSearch+21 j
ROM3:7E88 clra
ROM3:7E89 ldd PifSendError ; Will contain an error code (such as 0x507) if trying to send commands to the PIF board or read laserdisc VBI data failed.
ROM3:7E89 ; (see 19e2). Otherwise it will be 0.
ROM3:7E8C bne Done
ROM3:7E8E jsr IfAdvancePressedSetCarry ; set carry if advance is pressed
ROM3:7E8E ; clear carry if advance not pressed
ROM3:7E91 bcs Done
ROM3:7E93 jsr TestExpectedFrameNumStatus ; Does a TST on memory location A1BC
ROM3:7E93 ; Z will be set if expected frame number matches actual frame number.
ROM3:7E96 bne WaitSearchFinish
ROM3:7E98 ldd PifSendError ; Will contain an error code (such as 0x507) if trying to send commands to the PIF board or read laserdisc VBI data failed.
ROM3:7E98 ; (see 19e2). Otherwise it will be 0.
ROM3:7E9B
ROM3:7E9B Done: ; CODE XREF: InitiateDiscSearch+17 j
ROM3:7E9B ; InitiateDiscSearch+1C j
ROM3:7E9B puls pc,u,y,x
ROM3:7E9B ; End of function InitiateDiscSearch
Star Rider has almost worked perfectly with Dexter for quite some time and I had the opportunity to do more Star Rider research recently and here's some of my interesting findings.
First of all, the reason that Dexter wasn't working was because during the self-test, it would routinely fail with a search to frame 6 (as I recall) but would usually be able to successfully seek to other test frames on the disc. Why was it always failing to seek to frame 6?
Well, after much studying of the ROM disassembly, I think I have a pretty good grasp of how it works.
First of all, to initiate a seek, the Star Rider program calls a function at $7E75:
ROM3:7E75 ; =============== S U B R O U T I N E =======================================
ROM3:7E75
ROM3:7E75 ; frame number stored in D
ROM3:7E75 ; on return carry may be set if advance button was pressed
ROM3:7E75
ROM3:7E75 InitiateDiscSearch: ; CODE XREF: DiscTestSearchTo+2F P
ROM3:7E75 ; ROM3:7DE7 P ...
ROM3:7E75 pshs x,y,u
ROM3:7E77 pshs a,b
ROM3:7E79 clra
ROM3:7E7A clrb
ROM3:7E7B std PifSendError ; Will contain an error code (such as 0x507) if trying to send commands to the PIF board or read laserdisc VBI data failed.
ROM3:7E7B ; (see 19e2). Otherwise it will be 0.
ROM3:7E7E lda #$17
ROM3:7E80 sta PifResponseRetryCount
ROM3:7E83 puls b,a
ROM3:7E85 jsr SetupPifBuffersForSearching ; It appears that this is a "search to frame" function, where the frame is stored in D
ROM3:7E85 ; (A1BC needs to get a non-zero value stored to it or else it's an error)
ROM3:7E85 ; A1A4: 0x5 0x5 0x13 FrameHi FrameLo 0x5
ROM3:7E85 ; A1B4: FrameHi FrameLo
ROM3:7E85 ; A1BC: 0x13 0x0A 0x64
ROM3:7E85 ;
ROM3:7E85 ; A1A7 and A1B4 = D
ROM3:7E85 ; A1A4 = 0x505
ROM3:7E85 ; A1A6,A1BC = 0x13
ROM3:7E85 ; A1BD = 0xA64
ROM3:7E85 ; A1A9 = 5
ROM3:7E88
ROM3:7E88 WaitSearchFinish: ; CODE XREF: InitiateDiscSearch+21 j
ROM3:7E88 clra
ROM3:7E89 ldd PifSendError ; Will contain an error code (such as 0x507) if trying to send commands to the PIF board or read laserdisc VBI data failed.
ROM3:7E89 ; (see 19e2). Otherwise it will be 0.
ROM3:7E8C bne Done
ROM3:7E8E jsr IfAdvancePressedSetCarry ; set carry if advance is pressed
ROM3:7E8E ; clear carry if advance not pressed
ROM3:7E91 bcs Done
ROM3:7E93 jsr TestExpectedFrameNumStatus ; Does a TST on memory location A1BC
ROM3:7E93 ; Z will be set if expected frame number matches actual frame number.
ROM3:7E96 bne WaitSearchFinish
ROM3:7E98 ldd PifSendError ; Will contain an error code (such as 0x507) if trying to send commands to the PIF board or read laserdisc VBI data failed.
ROM3:7E98 ; (see 19e2). Otherwise it will be 0.
ROM3:7E9B
ROM3:7E9B Done: ; CODE XREF: InitiateDiscSearch+17 j
ROM3:7E9B ; InitiateDiscSearch+1C j
ROM3:7E9B puls pc,u,y,x
ROM3:7E9B ; End of function InitiateDiscSearch
The summary is that this method calls another function at $7E85 to store data in some buffers that will be later sent to the PIF board and then it waits for either the search to succeed ($7E98) or for the search to fail with an error ($7E8C).
Here is the function at $7E85:
ROM1:1C5B ; =============== S U B R O U T I N E =======================================
ROM1:1C5B
ROM1:1C5B ; It appears that this is a "search to frame" function, where the frame is stored in D
ROM1:1C5B ; (A1BC needs to get a non-zero value stored to it or else it's an error)
ROM1:1C5B ; A1A4: 0x5 0x5 0x13 FrameHi FrameLo 0x5
ROM1:1C5B ; A1B4: FrameHi FrameLo
ROM1:1C5B ; A1BC: 0x13 0x0A 0x64
ROM1:1C5B ;
ROM1:1C5B ; A1A7 and A1B4 = D
ROM1:1C5B ; A1A4 = 0x505
ROM1:1C5B ; A1A6,A1BC = 0x13
ROM1:1C5B ; A1BD = 0xA64
ROM1:1C5B ; A1A9 = 5
ROM1:1C5B
ROM1:1C5B SetupPifBuffersForSearching: ; CODE XREF: sub_0_35A0+C P
ROM1:1C5B ; sub_0_4471+98 P ...
ROM1:1C5B pshs cc
ROM1:1C5D orcc #$F0 ; '=' ; suppress interrupts
ROM1:1C5F std PifArray5Bytes_3
ROM1:1C62 std ExpectedHexPicNum ; The picture number we expect to be on (used to detect successful searches, etc).
ROM1:1C62 ; (see 1b9c)
ROM1:1C65 ldd #$505 ; 5 is a NOP command, so this starts with two NOP commands
ROM1:1C68 std PifArray5Bytes ; this array will be sent to pif board if the first byt is not zero
ROM1:1C68 ; it is 5 bytes in size
ROM1:1C6B lda #$13 ; 13 is the FIRQ cmd to initiate disc search
ROM1:1C6D sta PifArray5Bytes_2
ROM1:1C70 sta ExpectedFrameNumStatus ; May indicate current/pending pif command. (see 1a5f)
ROM1:1C70 ; gets 0x13 stored when a new search is initiated (FIRQ search command)
ROM1:1C73 ldd #$A64 ; 0xA is a slow decrementing counter that determines when we start calling the subroutine at 1b68,
ROM1:1C73 ; while 0x64 is an even slower decrementing counter that, if it reaches 0, triggers a 507 error (see 1b7f)
ROM1:1C76 stb SlowerPifCounter
ROM1:1C79 sta SlowPifCounter ; may throttle how often we check for seek complete among other things
ROM1:1C7C
ROM1:1C7C loc_0_1C7C: ; DATA XREF: sub_0_2DDA+19A r
ROM1:1C7C jsr Store5ToA1A9 ; A1A9 = 5
ROM1:1C7F puls pc,cc
ROM1:1C7F ; End of function SetupPifBuffersForSearching
This function was quite difficult to decode. It basically stores some commands to be sent to the PIF board at a later time in some buffers. Of particular note is a 5-byte array which will look like this: 0x05 0x05 0x13 FrameHi FrameLo. By disassembling the PIF board ROM, I was able to discover that command 0x5 is a single byte NO-OP while command 0x13 is a 'search to frame' command which is followed by the desired frame number in binary/hex format.
So when does this command actually get executed?
It happens during the IRQ service routine, which gets triggered on vblank.
We'll skip some details and start at $1A5F which gets called every top field and checks to see if there's anything to send to the PIF board. In our case, after the search command has been received, we will end up here:
ROM1:1A97 A1BCNotZeroAmongOtherTHings: ; CODE XREF: SendPifCmdsAndMore+5 j
ROM1:1A97 ; SendPifCmdsAndMore+A j ...
ROM1:1A97 orcc #$F0 ; '=' ; disable interrupts
ROM1:1A99 lda PIA_VGG_U7_CTRL_B ; PIF PIA
ROM1:1A9C lbpl DecreaseCounterAndCheck ; branch if pif board is not ready / waiting for seek to finish (?)
ROM1:1AA0 ldx NextPifBufStruct ; seems to contain a pointer to the next pif buffer structure after pif board becomes ready again (see 1AA0)
ROM1:1AA3 beq DecrementPriorityCounter ; branch if there is no active policy
ROM1:1AA5 ldb PifTimeoutBytesLeftToSend ; do we need to finish sending pif bytes we tried to send previously?
ROM1:1AA8 beq NoPifBytesLeftToSend ; if we have no bytes left to send from a previous attempt, branch
ROM1:1AAA ldu #PifTimeoutSendBuf ; If sending to pif timed out, this buffer
ROM1:1AAA ; will hold what we still need to send after
ROM1:1AAA ; the pif board becomes ready again.
ROM1:1AAA ; The array size is 4 bytes because the max
ROM1:1AAA ; size of a pif send buffer is 5, so at
ROM1:1AAA ; most 4 bytes will not have been sent if
ROM1:1AAA ; the pif board becomes busy.
ROM1:1AAD jsr SendCommandsToPif ; B holds how many bytes to send and will be modified
ROM1:1AAD ; U points to array of bytes to send and will be modified
ROM1:1AAD ; carry is set if we don't get the expected response
ROM1:1AB0 bcs OnPifTimeout ; if we timed out, it can mean that a seek has initiated (maybe can mean other things)
ROM1:1AB0 ; and so we should wait for the pif board to become ready
ROM1:1AB0 ; before sending anything else (within reason timeout period).
$1A99 checks to see if the PIF board is 'ready' or not. If it returns a result with the high-bit clear, it means the PIF board is not ready, so the program branches to the end of this function and just decrements a timer. If this timer expires, the PIF board is assumed to have become unresponsive and a fatal error is thrown. It is pretty important to understand that as long as the PIF board is 'busy', this method won't try to check to see whether the laserdisc is on the proper frame.
So once the PIF board has become non-busy, the program will then try to match up the frame number.
ROM1:1B68 ; =============== S U B R O U T I N E =======================================
ROM1:1B68
ROM1:1B68 ; If enough time has passed, check to see if the expected picture number equals the actual picture number.
ROM1:1B68 ; If there is a discrepancy, send PIF jump commands to compensate.
ROM1:1B68 ; If enough time hasn't passed, this will update the timer/counter.
ROM1:1B68 ;
ROM1:1B68 ; This sets Z flag on success (D = 0) and return an error code in D on error.
ROM1:1B68
ROM1:1B68 ThinkActualFrameNumIsExpected: ; CODE XREF: SendPifCmdsAndMore+5E P
ROM1:1B68 ; SendPifCmdsAndMore+98 P
ROM1:1B68 lda SlowPifCounter ; may throttle how often we check for seek complete among other things
ROM1:1B6B beq CompareActualPicNumWithExpected
ROM1:1B6D dec SlowPifCounter ; may throttle how often we check for seek complete among other things
ROM1:1B70 beq ReadActualPicNumIfAvailable
ROM1:1B72 jsr Store5ToA1A9 ; make sure that this gets called again
ROM1:1B75 clra ; sets Z flag to indicate success
ROM1:1B76 rts
ROM1:1B77 ; ---------------------------------------------------------------------------
ROM1:1B77
ROM1:1B77 ReadActualPicNumIfAvailable: ; CODE XREF: ThinkActualFrameNumIsExpected+8 j
ROM1:1B77 lda #4
ROM1:1B79 sta SlowPifCounter ; may throttle how often we check for seek complete among other things
ROM1:1B7C dec SlowerPifCounter
ROM1:1B7F beq Error507 ; if this counter expires, it's an error
ROM1:1B81 lda FieldsLeftBeforeStability ; If this is 0, it means it's safe to read the VBI picture number. (See 1B81)
ROM1:1B81 ; Gets decremented by IRQ if it is not 0.
ROM1:1B81 ; It indicates how many fields the IRQ needs to see before it considers the software/hardware field values to be stable. (see e488)
ROM1:1B81 ; It gets set to non-zero any time the hardware field value does not match the software field value.
ROM1:1B84 bne Skip4TracksForward ; skip 4 tracks forward
ROM1:1B84 ; (to recover from bad VBI read?)
ROM1:1B86 jsr ConvertBCDPicNumToHexPicNum ; Converts the last read BCD picture number (from laserdisc VBI) to a hex picture number.
ROM1:1B86 ; Carry will be clear on success or set on failure.
ROM1:1B86 ; 'D' and $A133 will contain the converted picture number, or 50000 (decimal) on failure.
ROM1:1B89 bcc CompareActualPicNumWithExpected
ROM1:1B8B
ROM1:1B8B Skip4TracksForward: ; CODE XREF: ThinkActualFrameNumIsExpected+1C j
ROM1:1B8B lda #$10 ; skip 4 tracks forward
ROM1:1B8B ; (to recover from bad VBI read?)
ROM1:1B8D sta PifArray1ByteSpecial ; This byte will be sent to the pif board if it is non-zero.
ROM1:1B8D ; It is special in that it can receive a higher priority to be sent (I don't fully understand it yet).
ROM1:1B90 clra
ROM1:1B91 rts
ROM1:1B92 ; ---------------------------------------------------------------------------
ROM1:1B92
ROM1:1B92 Error507: ; CODE XREF: ThinkActualFrameNumIsExpected+17 j
ROM1:1B92 ldd #$507 ; this may be an error code
ROM1:1B95 rts
ROM1:1B96 ; ---------------------------------------------------------------------------
ROM1:1B96
ROM1:1B96 CompareActualPicNumWithExpected: ; CODE XREF: ThinkActualFrameNumIsExpected+3 j
ROM1:1B96 ; ThinkActualFrameNumIsExpected+21 j
ROM1:1B96 clr SlowPifCounter ; may throttle how often we check for seek complete among other things
ROM1:1B99 ldd CurrentHexPicNumber ; Stores current laserdisc picture number in hex (converted from BCD).
ROM1:1B99 ; (see 6f3)
ROM1:1B99 ; Gets 50000 (decimal) stored if VBI frame number appears to be invalid.
ROM1:1B99 ; (see 6E4)
ROM1:1B9C subd ExpectedHexPicNum ; The picture number we expect to be on (used to detect successful searches, etc).
ROM1:1B9C ; (see 1b9c)
ROM1:1B9F beq ActualFrameNumMatchesExpectedFrameNum ; This gets cleared when the expected frame number matches the actual frame number. (see $1BDD)
ROM1:1B9F ; This is used by the disc "search to" test to indicate that an expected frame number was detected by the VBI reader.
ROM1:1BA1 bhi ActualPicNumGreaterThanExpected ; are we off by more than 2 tracks?
ROM1:1BA3 coma ; negate the delta (I am pretty sure, but I haven't tested it)
ROM1:1BA3 ; so it is positive instead of negative
ROM1:1BA4 negb
ROM1:1BA5 sbca #$FF
ROM1:1BA7 cmpd #4 ; is delta greater than 4?
ROM1:1BAB bcs SendSkipForwardCommand
ROM1:1BAD ldd #4 ; assume delta is 4 if it is larger than 4 because we only have 4 skip forward commands available to us.
ROM1:1BB0
ROM1:1BB0 SendSkipForwardCommand: ; CODE XREF: ThinkActualFrameNumIsExpected+43 j
ROM1:1BB0 pshs b
ROM1:1BB2 addd CurrentHexPicNumber ; Stores current laserdisc picture number in hex (converted from BCD).
ROM1:1BB2 ; (see 6f3)
ROM1:1BB2 ; Gets 50000 (decimal) stored if VBI frame number appears to be invalid.
ROM1:1BB2 ; (see 6E4)
ROM1:1BB5 std CurrentHexPicNumber ; Adjust current picture number under the assumption that the
ROM1:1BB5 ; track jump will be successful.
ROM1:1BB5 ; We need to track it in case we have to perform multiple track jumps in a row.
ROM1:1BB8 puls b
ROM1:1BBA addb #$C ; send one of 4 PIF 'skip forward' commands,
ROM1:1BBA ; either 0xD, 0xE, 0xF, or 0x10
ROM1:1BBC stb PifArray1ByteSpecial ; This byte will be sent to the pif board if it is non-zero.
ROM1:1BBC ; It is special in that it can receive a higher priority to be sent (I don't fully understand it yet).
ROM1:1BBF clra
ROM1:1BC0 rts
ROM1:1BC1 ; ---------------------------------------------------------------------------
ROM1:1BC1
ROM1:1BC1 ActualPicNumGreaterThanExpected: ; CODE XREF: ThinkActualFrameNumIsExpected+39 j
ROM1:1BC1 cmpd #2 ; are we off by more than 2 tracks?
ROM1:1BC5 bcs SendSkipBackwardCommand ; save difference
ROM1:1BC7 ldd #2 ; Assume we are off by 2 if it's more than that.
ROM1:1BC7 ; This is because there are only two reverse PIF commands,
ROM1:1BC7 ; 0x11 and 0x12 so our difference value must be
ROM1:1BC7 ; either 1 or 2 since we add to 0x10 below.
ROM1:1BCA
ROM1:1BCA SendSkipBackwardCommand: ; CODE XREF: ThinkActualFrameNumIsExpected+5D j
ROM1:1BCA pshs a,b ; save difference
ROM1:1BCC ldd CurrentHexPicNumber ; Stores current laserdisc picture number in hex (converted from BCD).
ROM1:1BCC ; (see 6f3)
ROM1:1BCC ; Gets 50000 (decimal) stored if VBI frame number appears to be invalid.
ROM1:1BCC ; (see 6E4)
ROM1:1BCF subd ,s
ROM1:1BD1 std CurrentHexPicNumber ; Adjust current frame number as if the track jump has already occurred and is successful.
ROM1:1BD1 ; We need to track this here in case we need to perform multiple
ROM1:1BD1 ; track jumps in a row.
ROM1:1BD4 puls b,a ; restore difference
ROM1:1BD6 addb #$10 ; this converts the difference value into one of the two PIF 'jump backward' commands (0x11 or 0x12).
ROM1:1BD8 stb PifArray1ByteSpecial ; This byte will be sent to the pif board if it is non-zero.
ROM1:1BD8 ; It is special in that it can receive a higher priority to be sent (I don't fully understand it yet).
ROM1:1BDB clra
ROM1:1BDC rts
ROM1:1BDD ; ---------------------------------------------------------------------------
ROM1:1BDD
ROM1:1BDD ActualFrameNumMatchesExpectedFrameNum: ; CODE XREF: ThinkActualFrameNumIsExpected+37 j
ROM1:1BDD clr ExpectedFrameNumStatus ; This gets cleared when the expected frame number matches the actual frame number. (see $1BDD)
ROM1:1BDD ; This is used by the disc "search to" test to indicate that an expected frame number was detected by the VBI reader.
ROM1:1BE0 lda #$F
ROM1:1BE2 sta word_0_A1C0 ; Gets set to 0xF when expected pic number matches actual pic num.
ROM1:1BE2 ; (see 1be2)
ROM1:1BE5 ldd CurrentHexPicNumber ; Stores current laserdisc picture number in hex (converted from BCD).
ROM1:1BE5 ; (see 6f3)
ROM1:1BE5 ; Gets 50000 (decimal) stored if VBI frame number appears to be invalid.
ROM1:1BE5 ; (see 6E4)
ROM1:1BE8 std SearchedHexPicNumber ; Gets picture number after a successful search.
ROM1:1BE8 ; (see 1be8)
ROM1:1BEB clr byte_0_A1B8 ; Gets cleared when expected picture number matches actual picture number
ROM1:1BEB ; (see 1beb)
ROM1:1BEE clra
ROM1:1BEF rts
ROM1:1BEF ; End of function ThinkActualFrameNumIsExpected
Again, this function was quite difficult to understand and took me a long time.
$1B68 checks to see whether it's time to read the real frame number from the video signal.
If it's time, it reads the frame number at $1B86 and converts it from the BCD laserdisc format to regular binary/hex format.
If the frame number cannot be converted from BCD because it contains 'hex' digits, or if it is not a valid VBI picture number, or if it times out, it will try to skip 4 tracks forward and retry ($1B8B).
$1B96 is the routine that actually compares the two frame numbers (actual vs expected). The expected picture number will be subtracted from the actual picture number. If the difference is 0, the search is assumed to be successful ($1BDD). Otherwise, the program will attempt to _skip_ to the right location (which is pretty nifty). It will either skip forward ($1BB0) or backward ($1BCA).
CONCLUSION:
To fix Dexter, I probably need to make sure that I am sending correct picture numbers in the VBI signal a few fields/frames before I send the 'search has finished' signal. This will allow the frame decoding hardware on the VGG board time to get a good read before the ROM program tries to validate whether it is correct. Alternatively, I can make all seeks completely instantly for PR-8210A mode which is probably a lot safer but not as autentic.
Subscribe to:
Posts (Atom)