What a glutton for punishment you are!uze6666 wrote:We would need to gather the manufacturer code and/or model from the card dynamically and match against a table of delay values.
Anyways, maybe I'm just biased but I like my solution better.
I realize now that I probably didn't explain my idea very well and also that not everyone has recently spent hours poring over the sources. I'll explain it later but first, let me say:
IT WORKS!
I was able to incorporate my hack into the mode 7 kernel files where it can now be called a fix. I'm including a working player showing it off.
The matrix trailer will now play perfectly from any spot on the card, so you can now watch 12 copies back to back . Or, do something else cool with mode 7!! It just takes ~1 second to sync when playback starts.
As I expected, 8-bit trip has a little bit of trouble synching up on it's own. For this, I've left my button-press-delay hack in the included player. You just have to press any button a few times and it's good to go.
How it works:
.uzm's are encoded with a 250-byte filler near the beginning of each video frame. I noticed that when the picture lost sync, the filler was all black (as you'd expect) and reasoned that this filler could be used as a sort of VSYNC indicator.
So, I re-wrote read_filler to actually check that this filler is all black and if it isn't, delay playback by leaving render_start set to 0. What happens is that every frame the filler isn't found, playback is skipped to check on the next frame if the next sector is, in fact, the one with the filler. At least, I *think* that's what's going on.
So, if you're making a new video, (say, for a cutscene ) you'd want to make sure not to leave lines of total black, just use very light grey instead.
Here's the new code for VideoMode7.s. The only changes are immediately around read_filler but I'll post it all to assist the lazy:
Code: Select all
;***************************************************
; Video Mode 7
; 170x120
; Movie player
;***************************************************
#define LINE_BUFFER_SIZE 170
#define LINES_PER_SECTOR 6
.global load_sound_buffer
.global render_start
.global playback_start
.global StartPlayback
.global StopPlayback
.global InitializeVideoMode
.global DisplayLogo
.global VideoModeVsync
.global vram
.section .bss
vram: .space 1
line_buffer: .space LINE_BUFFER_SIZE*2
render_start: .byte 1
playback_start: .byte 1
.section .text
//*** Main routine ****
sub_video_mode7:
;waste line to align with next hsync in render function
ldi ZL,222-170-2
mode7_render_delay:
lpm
nop
dec ZL
brne mode7_render_delay
lpm
lpm
;clear the line buffer
ldi r16,LINE_BUFFER_SIZE
clr r17
ldi XL,lo8(line_buffer)
ldi XH,hi8(line_buffer)
clr_loop:
st X+,r17
st X+,r17
dec r16
brne clr_loop
ldi r16,LINE_BUFFER_SIZE/2
mov r25,r16
mov r13,r16
ldi r16,lo8(LINE_BUFFER_SIZE)
ldi r17,hi8(LINE_BUFFER_SIZE)
movw r14,r16
clr r21 ;first frame sector
ldi r22,224+4 ;decremental line counter
clr r23 ;incremental line counter
ldi r24,LINES_PER_SECTOR ;lines per SD sector
ser r20
clr r6
lds r18,render_start
cpi r18,1
brne next_line_wait
next_line:
;trigger 1st line pixel read
out _SFR_IO_ADDR(SPDR),r20
;***First Line***
rcall hsync_pulse ;3+144=147
;draw line
eor r25,r13 ;add half line offset
rcall render_tile_line
inc r23
dec r22
brne next_line
rjmp end_frame
next_line_wait:
rcall hsync_pulse ;3+144=147
ldi r24,lo8(1670/4)
ldi r25,hi8(1670/4)
wait:
sbiw r24,1
brne wait
rjmp .
dec r22
brne next_line_wait
end_frame:
;frame rendering finished
rcall hsync_pulse ;145
;set vsync flag if beginning of next frame (each two fields)
ldi r17,1
lds r16,curr_field
eor r16,r17
sts curr_field,r16
lds r18,curr_frame
tst r16
breq .+2
eor r18,r17
sts curr_frame,r18
;set vsync flag if beginning of next frame
ldi ZL,1
sts vsync_flag,ZL
;clear any pending timer int
ldi ZL,(1<<OCF1A)
sts _SFR_MEM_ADDR(TIFR1),ZL
// call load_sound_buffer
ret
;*************************************************
; RENDER SCANLINE
;
; r22 = Lines remaining
; r23 = current line
; r24 = sector lines left
; r25 = next line buffer displacement
;
; cycles = 1679
;*************************************************
render_tile_line:
;current buffer
ldi XL,lo8(line_buffer)
ldi XH,hi8(line_buffer)
movw YL,XL
sbrc r23,1
add XL,r14
sbrc r23,1
adc XH,r15
;next line buffer
sbrs r23,1
add YL,r14
sbrs r23,1
adc YH,r15
;add half line offset each other line
add YL,r25
adc YH,r6
ldi r18,84
loop:
nop
;in r17,_SFR_IO_ADDR(SPSR) ;clear flag
in r17,_SFR_IO_ADDR(SPDR) ;read next pixel
out _SFR_IO_ADDR(SPDR),r20
ld r19,X+
out _SFR_IO_ADDR(DATA_PORT),r19
st Y+,r17
rjmp .
rjmp .
ld r19,X+
out _SFR_IO_ADDR(DATA_PORT),r19
dec r18
brne loop ;18*84=1512
;write last line pixel
;in r17,_SFR_IO_ADDR(SPSR) ;clear flag
nop
in r17,_SFR_IO_ADDR(SPDR) ;read next pixel
st Y+,r17
out _SFR_IO_ADDR(DATA_PORT),r6
dec r24
breq next_sector
ldi r19,42
dec r19
brne .-4
rjmp .
ret
;1660
next_sector:
;-read the two last sector's bytes
;-2 CRC bytes
;-wait 8 clocks
;-read next data token (0xfe)
ldi r19,6
read_blocks:
;read first CRC byte
out _SFR_IO_ADDR(SPDR),r20
ldi r18,5
dec r18
brne .-4 ;wait 15 cycles
dec r19
;in r17,_SFR_IO_ADDR(SPSR) ;clear flag
nop
in r17,_SFR_IO_ADDR(SPDR) ;read crc
brne read_blocks ;21
ldi r24,LINES_PER_SECTOR
ret
;1660
;********************************
; Can destroy r18-r27 and r30-r31
;********************************
load_sound_buffer:
;set target bank adress
lds r18,mix_bank
tst r18
breq lset_hi_bank
ldi ZL,lo8(mix_buf)
ldi ZH,hi8(mix_buf)
rjmp lend_set_bank
lset_hi_bank:
ldi ZL,lo8(mix_buf+MIX_BANK_SIZE)
ldi ZH,hi8(mix_buf+MIX_BANK_SIZE)
lend_set_bank:
ldi r24,lo8(262)
ldi r25,hi8(262)
ser r20
lsb_loop:
out _SFR_IO_ADDR(SPDR),r20
ldi r18,4
dec r18
brne .-4 ;wait 12 cycles
sbiw r24,1
nop
in r19,_SFR_IO_ADDR(SPSR) ;clear flag
in r19,_SFR_IO_ADDR(SPDR) ;read crc
st Z+,r19
brne lsb_loop ;22
; 262 sound bytes were read.
;-250 filler bytes ?
;-2 CRC bytes
;-wait 8 clocks
;-read next data token (0xfe) (twice, presumably)
ldi r19,250
ldi r21,0 ; keeps track of filler
ldi r18,0
;read first filler byte
read_filler:
out _SFR_IO_ADDR(SPDR),r20
or r21, r18 ; or the most recent filler byte with r21
nop
nop
ldi r18,4
dec r18
brne .-4 ;wait 12 cycles
; read some more filler bytes
dec r19
in r17,_SFR_IO_ADDR(SPSR) ;clear flag
in r18,_SFR_IO_ADDR(SPDR) ;read next byte
brne read_filler ;21
; read four more bytes that we don't care about (CRC and such)
ldi r19, 4
read_dont_care:
out _SFR_IO_ADDR(SPDR),r20
ldi r18,5
dec r18
brne .-4 ;wait 15 cycles
; read some more dont_care bytes
dec r19
in r17,_SFR_IO_ADDR(SPSR) ;clear flag
in r18,_SFR_IO_ADDR(SPDR) ;read whatever
brne read_dont_care ;21
ldi r18,1
sts render_start,r18
cpi r21, 0 ; ensure that we only read black for the duration of the filler,
brne Offsync ; otherwise, we know the video is out-of-sync
ret
Offsync:
ldi r18,0
sts render_start,r18 ; just kidding; don't start rendering until we're sycnhronized :)
ret
StartPlayback:
ldi r24,1
sts playback_start,r24
ret
StopPlayback:
ldi r24,0
sts render_start,r24
sts playback_start,r24
ret
;No logo for this mode
DisplayLogo:
ret
VideoModeVsync:
lds r24,playback_start
cpi r24,0
breq skip
rcall load_sound_buffer
skip:
ret
InitializeVideoMode:
ldi XL,lo8(line_buffer)
ldi XH,hi8(line_buffer)
ldi r24,64*2
ldi r25,0xff
m7_init_loop:
st X+,r25
dec r24
brne m7_init_loop
sts render_start,r1
sts playback_start,r1
ret
I've attached the blanks in a .zip. (Windows) Instructions included.