Tuesday, December 5, 2017

Star Rider VGG Repair (corrupt video)



Yet another Star Rider repair video.  This time it's a VGG board with problems.

Monday, December 4, 2017

Star Rider Biphase data captured from a real laserdisc and real laserdisc player


I hooked up my PR-8210A to Star Rider to get some accurate captures of correctly decoded biphase data.  Hopefully this is one of the last times that I need to use the PR-8210A for research!

Here's the captures that it took.







Sunday, December 3, 2017

Enhancement to Star Rider's Disc Search Test

I've written an enhancement to one of the diagnostic tests that Star Rider provides.  I noticed that I could not find any test within the suite of tests that Star Rider offers that tests its biphase decoding.  The biphase decoding is used to decode the picture number from the video signal, as well as some other data that occurs at the bottom of the video frame.  The game still seems to work (kinda) even if this data is corrupt (I know because I have the game partially emulated in Daphne and I don't provide any of the data aside from the picture number at all).  So it's really hard to tell whether the biphase decoder is actually working without a nice test that explicitly shows this.

Here's what my new enhancement does:


It prints five rows and nine columns of hexadecimal bytes, and with CRC-16 values for each row.  The reason I show 45 bytes is because this is how many decoded bytes the ROM program seems to care about.  A careful observer may notice that the first three bytes of biphase data are always the VBI picture number.

A human can (hopefully) quickly seek to their favorite track on the laserdisc, and look at the CRC-16 values to make sure that the decoded biphase data is correct.  If any of the CRC-16 values are off, the human can then examine the larger data set on the left to see where the discrepancy is.

Here are the steps (done from a linux/bash shell) to add my mod to your existing U45 CPU ROM:

- Make sure your U45 ROM matches the one I modded:

$ md5sum r34u45.cpu
810dc65c353259e7fa600e31767fb0dc *r34u45.cpu

- Copy u45 to a new file.  In this new file, modify $12FC from JSR $9365 to JSR $9D3D (the location of the new code).  (4861 is 0x12FD in decimal)

$ cp r34u45.cpu u45_pre.bin
$ printf '\x9d\x3d' | dd of=u45_pre.bin bs=1 seek=4861 count=2 conv=notrunc

- (optional) check to make sure you are in sync with where I am at this point:

$ md5sum u45_pre.bin
5d86c0618b89ec210d88c748c766ab3c  u45_pre.bin

- Change length of file so we can append our new code to the end of it.
$ truncate -s 7485 u45_pre.bin

- Assemble my program (at the end of this blog post) with the asm6809 assembler found here.  You can also install it as an ubuntu package ("sudo apt-get install asm6809")

$ asm6809 -9 starrider_discsearch_ex.asm -o starrider_discsearch_ex.bin

- Append assembled binary to the end of the truncated file you made earlier.
$ cat u45_pre.bin starrider_discsearch_ex.bin > u45_new.bin

- Test to make sure the file you ended up with matches mine:
$ md5sum u45_new.bin
3e3d589b53b8b607938545b1e3975752 *u45_new.bin

- Replace the U45 CPU EPROM on your star rider with this image, burned to a new EPROM/EEPROM.

---------------------------

Here is my assembly language program.  NOTE that my instructions expect the file to by named 'starrider_discsearch_ex.asm' !

---------------------------

HW_WatchDog equ $c900
BlitLeftToRightIterate equ $5e1b
BlitWithoutSource equ $7fe1
PtrToBigFontBase equ $5fe4
BiPhaseCache equ $a172
InputThinkEtc equ $9365

; at the end of the U51 ram, hopefully using this won't stomp on anything else

OldCrc1 equ $a7ea
OldCrc2 equ $a7ec
OldCrc3 equ $a7ee
OldCrc4 equ $a7f0
OldCrc5 equ $a7f2

Crc1 equ $a7f4
Crc2 equ $a7f6
Crc3 equ $a7f8
Crc4 equ $a7fa
Crc5 equ $a7fc

; points to the next 16-bit address to store a crc calculation
CrcPtr equ $a7fe

; start at the end of U45/ROM5 on star rider where there is some empty room
org $9d3d

; =============== S U B R O U T I N E =======================================

EraseAndRenderBiphaseDataIfNeeded

; preserve all
pshs a,b,x,y,u

; calculate CRC values so we can tell whether anything's changed
ldx #Crc1
stx CrcPtr

lda #9 ; size of array for CRC calculation
ldy #BiPhaseCache
jsr CalcCrc
ldy #BiPhaseCache+9
jsr CalcCrc
ldy #BiPhaseCache+18
jsr CalcCrc
ldy #BiPhaseCache+27
jsr CalcCrc
ldy #BiPhaseCache+36
jsr CalcCrc

; now compare CRC values to see if any have changed
ldx Crc1
cmpx OldCrc1
bne DisplayDataIsInvalid
ldx Crc2
cmpx OldCrc2
bne DisplayDataIsInvalid
ldx Crc3
cmpx OldCrc3
bne DisplayDataIsInvalid
ldx Crc4
cmpx OldCrc4
bne DisplayDataIsInvalid
ldx Crc5
cmpx OldCrc5
bne DisplayDataIsInvalid

; if we get this far, CRCs still match so nothing to do

jmp Done

DisplayDataIsInvalid
; update old crc values so that we don't keep re-rendering if nothing has changed
ldx Crc1
stx OldCrc1
ldx Crc2
stx OldCrc2
ldx Crc3
stx OldCrc3
ldx Crc4
stx OldCrc4
ldx Crc5
stx OldCrc5

ldx #$10B0 ; starting coordinates
lda #9 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #BiPhaseCache ; biphase RAM cache start

; one-time erase
jsr EraseBiphaseArea

jsr RenderHexDigits

ldx #$10BA ; starting coordinates
lda #9 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #BiPhaseCache+9

jsr RenderHexDigits

ldx #$10C4 ; starting coordinates
lda #9 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #BiPhaseCache+18

jsr RenderHexDigits

ldx #$10CE ; starting coordinates
lda #9 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #BiPhaseCache+27

jsr RenderHexDigits

ldx #$10D8 ; starting coordinates
lda #9 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #BiPhaseCache+36

jsr RenderHexDigits

; now render each CRC value
ldx #$70B0 ; starting coordinates
lda #2 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #Crc1 ; biphase RAM cache start
jsr RenderHexDigits

ldx #$70BA ; starting coordinates
lda #2 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #Crc2 ; biphase RAM cache start
jsr RenderHexDigits

ldx #$70C4 ; starting coordinates
lda #2 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #Crc3 ; biphase RAM cache start
jsr RenderHexDigits

ldx #$70CE ; starting coordinates
lda #2 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #Crc4 ; biphase RAM cache start
jsr RenderHexDigits

ldx #$70D8 ; starting coordinates
lda #2 ; bytes to render
ldb #$44 ; white
ldu #PtrToBigFontBase
ldy #Crc5 ; biphase RAM cache start
jsr RenderHexDigits

Done
; restore all
puls u,y,x,b,a

; go to where the program went before we hijacked the flow
jmp InputThinkEtc
; End of function EraseAndRender6DigitsWithBigFont


; =============== S U B R O U T I N E =======================================

; X: starting coordinates (upper-left)

EraseBiphaseArea

pshs a,b,x,y,u
ldd #$12FF ; solid color FF
ldy #$7032 ; 112 x 50
leax -1,x ; move Y dest up one pixel
jsr BlitWithoutSource ; Performs a blit where the source does not matter (solid color is typical use case).
; Y: width (msb) and height (lsb)
; X: destination video address
; D: control byte (msb) and solid color byte (lsb)
; IMPG and YLATE are set to 0.
puls pc,u,y,x,b,a
; End of function EraseDigitWithColorF

; =============== S U B R O U T I N E =======================================

; Renders the hex value pointed to by Y
; X: destination to render digit
; A: how many bytes to render
; Y: bytes to render
; B: the color
; U: the font set

RenderHexDigits
deca
pshs a
clr ,-s ; not used, left here to avoid errors

; feed watchdog
lda #$15
sta HW_WatchDog

RenderHiNibble
lda ,y
lsra ; shift nibble to low position
lsra
lsra
lsra
bsr RenderHexDigit
; A: digit to be rendered
; X: coordinate to render to
; B: color to render
; U: font to use

RenderLoNibble
lda ,y+
anda #$F
bsr RenderHexDigit
; A: digit to be rendered
; X: coordinate to render to
; B: color to render
; U: font to use
dec 1,s ; decrement counter
bpl RenderHiNibble ; if we're not done, loop
leas 2,s ; de-allocate temporary stack space
rts
; End of function RenderDigitsNoLeadingZeroes

; =============== S U B R O U T I N E =======================================

; Renders digit
; A: digit to be rendered
; X: coordinate to render to
; B: color to render
; U: font to use

RenderHexDigit
cmpa #$A
bmi RenderDigit ; branch if we're within range of 0-9
inca ; font A-F is 0xb-0x10, so increment A once to render properly

RenderDigit
jsr BlitLeftToRightIterate ; Part of a left-to-right blit loop
;
; X contains the destination address and will be modified to point to the next one on exit
; U points to a pointer that points to the beginning of font info for the first character.  Each entry contains a width/height byte (width is one nibble, height is another), followed by the source DMA address where the character bitmap lives.
; A contains the character to render (non-ascii, 0 is the first character)
; B contains solid color value
rts

; =============== S U B R O U T I N E =======================================

; A: number of bytes in the array
; Y: ptr to beginning of array
; CrcPtr: points to where final CRC value will be stored
; All registers are preserved, CrcPtr will be advanced by 2

CalcCrc
pshs a,b,x,y,u

; we'll access the counter via memory instead of having it in a register
pshs a

; feed watchdog
lda #$15
sta HW_WatchDog

; load address to store CRC
ldx CrcPtr
ldd #$FFFF ; initial seed value
std ,x

CalcCrcLoop
jsr CalcCrcUpdate

; decrement counter
dec ,s

; if we're not done yet, then keep going
bne CalcCrcLoop

CalcCrcDone
; pop the counter value
leas 1,s

; advance crc pointer for next calculation
leax 2,x
stx CrcPtr

puls pc,u,y,x,b,a

; =============== S U B R O U T I N E =======================================

; existing crc is in memory location pointed to by X, next byte is pointed to by Y

; Here is the C version of this function for reference:

; crc_ccitt_update(unsigned char x)
; {
; uint16_t crc_new = (unsigned char)(g_crc >> 8) | (g_crc << 8);
; crc_new ^= x;
; crc_new ^= (unsigned char)(crc_new & 0xff) >> 4;
; crc_new ^= crc_new << 12;
; crc_new ^= (crc_new & 0xff) << 5;
; g_crc = crc_new;
; }

CalcCrcUpdate
; grab current crc value
ldd ,x

; swap top and bottom bytes
exg a,b

; xor bottom byte with new byte, advance y
eorb ,y+

; store new value so we can do more maths on it
std ,x

; (crc_new & 0xFF) >> 4
clra ; this instruction may not be needed, double-check
lsrb
lsrb
lsrb
lsrb

; crc_new ^= (previous val)
eorb 1,x
stb 1,x

; (crc_new << 12)
; NOTE : b already contains the right value so we don't need to load it from memory
tfr b,a
lsla
lsla
lsla
lsla

; crc_new ^= (previous_val)
eora 0,x
sta 0,x

; (crc_new & 0xFF) << 5
clra
; b still contains the right value so we don't need to load it
lslb
rola
lslb
rola
lslb
rola
lslb
rola
lslb
rola

; crc_new ^= (previous_val)
eora 0,x
eorb 1,x

; store finished value
std ,x

rts

; ---------------------------------------------------------------------

; force a specific file size
org $9fff

fcb 0

Monday, November 20, 2017

Does Digilent's Digital Discovery get the job done?

I wanted a logic analyzer that would let me capture the address bus, the data bus, and a few other signals from an old 8-bit CPU (such as the MC6809E used in Star Rider).

After doing some research, I decided to get the Digilent Digital Discovery and give it a try.

I was not disappointed!

Here is an overview of all signals captured.


Here is a zoomed-in view.  I've added notes in yellow showing which CPU instructions are being executed.


For reference, here is the disassembled code that is being executed (look at the address numbers to help match things up).


Conclusion?  Money well spent! :)