/
rgb_to_fb.S
2568 lines (2219 loc) · 83.4 KB
/
rgb_to_fb.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "rpi-base.h"
#include "defs.h"
#include "macros.S"
.text
.global rgb_to_fb
.global poll_keys_only
.global key_press_reset
.global measure_vsync
.global analyse_sync
.global clear_full_screen
.global clear_menu_bits
.global clear_screen
.global measure_n_lines
.global sw1counter
.global sw2counter
.global sw3counter
.global vsync_line
.global total_lines
.global customPalette
.global dummyscreen
.global elk_mode
.global vsync_period
.global vsync_comparison_lo
.global vsync_comparison_hi
.global hsync_period
.global total_hsync_period
.global hsync_comparison_lo
.global hsync_comparison_hi
.global hsync_width
.global linecountmod10
.global sync_detected
.global last_sync_detected
.global delay_in_arm_cycles
.global benchmarkRAM
.global jitter_offset
.global debug_value
.global param_ntscphase
.global ntsc_status
.global param_delay
.global sw1_power_up
.global field_type_threshold
.global elk_lo_field_sync_threshold
.global elk_hi_field_sync_threshold
.global odd_threshold
.global even_threshold
.global hsync_threshold
.global other_hsync_threshold
.global equalising_threshold
.global frame_minimum
.global line_minimum
.global frame_timeout
.global hsync_scroll
.global line_timeout
.global vsync_retry_count
#ifdef USE_MULTICORE
.global run_core
.global read_pointer
.global line_buffer
#endif
.global capture_line_normal_1bpp_table
.global capture_line_normal_3bpp_table
.global capture_line_normal_6bpp_table
.global capture_line_normal_9bpplo_table
.global capture_line_normal_9bpphi_table
.global capture_line_normal_12bpp_table
.global capture_line_odd_3bpp_table
.global capture_line_even_3bpp_table
.global capture_line_double_3bpp_table
.global capture_line_half_odd_3bpp_table
.global capture_line_half_even_3bpp_table
.global capture_line_simple_12bpp_trailing_pos_table
.global capture_line_simple_12bpp_leading_pos_table
.global capture_line_simple_12bpp_trailing_neg_table
.global capture_line_simple_12bpp_leading_neg_table
.global capture_line_simple_12bpp_trailing_both_table
.global capture_line_simple_12bpp_leading_both_table
rgb_to_fb:
push {r4-r12, lr}
// Save the capture_info_t parameters to absolute addresses
ldr r2, [r0, #O_FB_PITCH]
str r2, param_fb_pitch
ldr r2, [r0, #O_FB_WIDTH]
str r2, param_fb_width
ldr r2, [r0, #O_FB_HEIGHT]
str r2, param_fb_height
ldr r2, [r0, #O_FB_SIZEX2]
str r2, param_fb_sizex2
ldr r2, [r0, #O_FB_BPP]
str r2, param_fb_bpp
ldr r2, [r0, #O_CHARS_PER_LINE]
str r2, param_chars_per_line
ldr r2, [r0, #O_NLINES]
str r2, param_nlines
ldr r2, [r0, #O_H_OFFSET]
str r2, param_h_offset
ldr r2, [r0, #O_V_OFFSET]
str r2, param_v_offset
ldr r2, [r0, #O_NCAPTURE]
str r2, param_ncapture
ldr r2, [r0, #O_CAPTURE_LINE]
str r2, param_capture_line
ldr r2, [r0, #O_PALETTE_CONTROL]
str r2, param_palette_control
ldr r2, [r0, #O_SAMPLE_WIDTH]
str r2, param_sample_width
ldr r2, [r0, #O_H_ADJUST]
str r2, param_h_adjust
ldr r2, [r0, #O_V_ADJUST]
str r2, param_v_adjust
ldr r2, [r0, #O_SYNCTYPE]
str r2, param_sync_type
ldr r3, [r0, #O_DETSYNCTYPE]
str r3, param_detected_sync_type
ldr r2, [r0, #O_VSYNCTYPE]
str r2, param_vsync_type
ldr r2, [r0, #O_VIDEOTYPE]
str r2, param_video_type
bic r1, r1, #BIT_INTERLACED_VIDEO
cmp r2, #0 //VIDEO_PROGRESSIVE;
tstne r3, #SYNC_BIT_INTERLACED
orrne r1, r1, #BIT_INTERLACED_VIDEO
ldr r2, [r0, #O_NTSCPHASE]
str r2, param_ntscphase
ldr r2, [r0, #O_BORDER]
str r2, param_border
ldr r2, [r0, #O_DELAY]
str r2, param_delay
ldr r2, [r0, #O_FB_BASE]
str r2, param_framebuffer0
ldr r9, ntsc_status
and r9, #0x10 // last detected burst state
ldr r8, param_ntscphase // 2 bits phase + 1 bit artifact on/off + 1 bit Y invert
orr r9, r9, r8
str r9, ntsc_status
// Sanity check chars_per_line <= fb_pitch
ldr r3, param_fb_pitch
ldr r2, param_fb_bpp
cmp r2, #4
moveq r3, r3, lsl #1
mov r3, r3, lsr #3
ldr r2, param_chars_per_line
cmp r2, r3
strgt r3, param_chars_per_line
// Sanity check nlines <= fb_height
ldr r3, param_fb_height
ldr r10, param_fb_sizex2
ands r10, r10, #1
movne r3, r3, lsr #1
ldr r2, param_nlines
cmp r2, r3
strgt r3, param_nlines
#ifdef MULTI_BUFFER
// Calculate the base address of each of the 4 frame buffers
ldr r10, param_fb_height
ldr r11, param_fb_pitch
ldr r2, param_framebuffer0
mul r10, r10, r11
add r2, r10
str r2, param_framebuffer1
add r2, r10
str r2, param_framebuffer2
add r2, r10
str r2, param_framebuffer3
// Default to displaying buffer 0 in Mode 7 (or on probe)
tst r1, #(BIT_INTERLACED_VIDEO | BIT_PROBE) // options currently in r1!
beq skip_swap
push {r0-r3}
mov r0, #0
bl swapBuffer
pop {r0-r3}
skip_swap:
#endif
// Setup r4 as a constant
ldr r4, =GPLEV0
// Setup r3 with the flags/options parameter (as per before)
mov r3, r1
// Setup r2 with the framebuffer pitch (as per before)
ldr r2, param_fb_pitch
// Setup r2 with the number of active characters per line (as per before)
ldr r1, param_chars_per_line
tst r3, #BIT_CLEAR
blne clear_screen
// Clear the following state bits:
bic r3, r3, #(BIT_FIELD_TYPE)
bic r3, r3, #(BIT_FIELD_TYPE1_VALID)
// In Mode 7 (or on probe) write to buffer 0, display buffer 0
bic r3, r3, #(MASK_LAST_BUFFER | MASK_CURR_BUFFER)
#ifdef MULTI_BUFFER
mov r8, #NBUFFERS
str r8, buffer_total
tst r3, #(BIT_INTERLACED_VIDEO | BIT_PROBE)
// In modes 0..6, restore the previous buffer state
ldreq r10, buffer_state
orreq r3, r3, r10
#endif
ldr r9, param_fb_sizex2
tst r9, #1
bicne r3, r3, #BIT_NO_LINE_DOUBLE
orreq r3, r3, #BIT_NO_LINE_DOUBLE
tst r9, #4
orrne r3, r3, #BIT_NO_LINE_DOUBLE
bicne r9, #1
ldr r8, param_video_type
cmp r8, #1 //VIDEO_INTERLACED
bne skip_double_check
tst r3, #BIT_INTERLACED_VIDEO
beq skip_double_check
ands r8, r3, #MASK_INTERLACE
orreq r3, r3, #BIT_NO_LINE_DOUBLE
biceq r9, #1
skip_double_check:
ldr r7, param_sample_width
ldr r8, param_palette_control
cmp r7, #3 // is sample width SAMPLE_WIDTH_9LO, SAMPLE_WIDTH_9HI, SAMPLE_WIDTH_12 (from above test)
movge r8, #0 // if so then force palette control to 0
ldr r10, param_h_offset
cmp r8, #6 // is it PALETTECONTROL_ATARI_GTIA ?
moveq r10, r10, lsl #1 // if so double the offset as sample rate is 2x pixel rate
add r10, r10, #1 // first psync test is wait for a zero after csync
str r10, param_h_offset
ldr r7, param_fb_bpp
cmp r7, #8
moveq r7, #1
movne r7, #0
// r7 0= 4 bpp or 16bpp, 1=8 bpp
// r8 0=normal, 1= in band, 2=CGA ntsc, 3=mono ntsc, 4=auto mono ntsc, 5 = pal artifact, 6 = atari gtia
// r9 0=normal, 1=Hx2, 2=Wx2, 3=H&Wx2
orr r10, r7, r8, lsl #1 // slow index in r10 now 0-9
tst r9, #2 // double width?
addne r10, r10, #14 // slow index in r10 now 10-19
add r7, r7, #28 // main index initially points to fast 4bpp or fast 8bpp (20-21)
cmp r8, #0 // palette control?
tsteq r9, #3 // double size?
tsteq r3, #BIT_OLD_FIRMWARE_SUPPORT // if version < 3 have to do the second PSYNC read
movne r7, r10 // if any are enabled make index point to non-fast versions
ldr r9, param_capture_line
ldr r8, [r9, r7, lsl #2]
ldr r9, param_video_type
cmp r9, #2 //VIDEO_TELETEXT
ldreq r8, =capture_line_mode7_4bpp
ldr r10, param_border
tst r10, #0x80
ldrne r8, =capture_line_null
str r8, capture_address
ldr r8, =sentinel
ldr r9, =0x48444d49 // "HDMI" sentinel
str r9, [r8]
mov r8, #5 // number of frames before h and v sync timing is analysed
str r8, frame_countdown
bl restore_menu_bits
frame:
ldr r8, =inBandPointer
ldr r9, =inBandData
str r9, [r8]
ldr r8, =paletteFlags
ldr r9, [r8]
bic r9, r9, #BIT_IN_BAND_DETECTED //in band data detected
str r9, [r8]
bl wait_for_vsync
// Working registers while frame is being captured
//
// r0 = scratch register
// r1 = number of 8-pixel blocks to capture (=param_chars_per_line)
// r2 = frame buffer line pitch in bytes (=param_fb_pitch)
// r3 = flags register
// r4 = GPLEV0 constant
// r5 = line counter (counts down to 0)
// r6 = scratch register
// r7 = scratch register
// r8 = value read from GPLEV0
// r9 = scratch register
// r10 = scratch register
// r11 = pointer to current line in frame buffer
// Pick the next draw buffer
// In Mode 7, or if MULTI_BUFFER disabled, than draw to 0
// else draw to the "spare" buffer
mov r0, #0
#ifdef MULTI_BUFFER
tst r3, #(BIT_INTERLACED_VIDEO | BIT_PROBE)
bne buffer_chosen
// Draw to the buffers cyclically, i.e. pick the one
// after the last completed buffer, modulo <nbuffers + 1>
// r8 and r9 are free at this point
mov r8, r3, lsr #OFFSET_LAST_BUFFER
and r8, r8, #3
mov r9, r3, lsr #OFFSET_NBUFFERS
and r9, r9, #3
cmp r8, r9
beq buffer_chosen
add r0, r8, #1
and r0, r0, #3
buffer_chosen:
#endif
ldr r8, =param_framebuffer0
ldr r11, [r8, r0, lsl #2]
// remember this as the current buffer
bic r3, r3, #MASK_CURR_BUFFER
orr r3, r3, r0, lsl #OFFSET_CURR_BUFFER
// The odd vs even field can be distinguished by the time between
// the last two rising edges:
// odd field (first field) should be 21/23us
// even field (second field) should be 53/55us
subs r6, r6, r7
rsbmi r6, r6, #0
subs r5, r7, r5 // work out length of field sync pulse (r5 is start, r7 is end)
rsbmi r5, r5, #0
bic r3, r3, #BIT_ELK // clear elk mode
ldr r8, param_vsync_type
cmp r8, #0 // auto
beq auto_detect_vsync
cmp r8, #2 // interlaced half line
orreq r3, r3, #BIT_ELK
ble get_field_type // branch for both types of interlace
tst r3, #BIT_MODE_DETECT
bne auto_detect_vsync // "if mode 7 detect enabled then don't use non-interlaced mode as it messes up mode 7 switching"
orr r3, r3, #BIT_ELK
bic r3, r3, #BIT_FIELD_TYPE // Odd, clear bit
bge got_field_type // if non interlaced then always elk and odd field
auto_detect_vsync:
ldr r7, elk_lo_field_sync_threshold
cmp r5, r7 // test for electron field sync which is 2.5 lines (160uS) instead of a whole number (normally 2 lines (128uS) with a 6845)
blt get_field_type
ldr r7, elk_hi_field_sync_threshold
cmp r5, r7
orrlt r3, r3, #BIT_ELK
get_field_type:
// Save the current field type
ldr r7, field_type_threshold
cmp r6, r7
biclt r3, r3, #BIT_FIELD_TYPE // Odd, clear bit
orrge r3, r3, #BIT_FIELD_TYPE // Even, set bit
got_field_type:
tst r3, #BIT_ELK
moveq r0, #0
movne r0, #1
str r0, elk_mode
// Check for mode change:
// Odd: Mode 0..6 should be 21us, Mode 7 should be 23us
// Even: Mode 0..6 should be 53us, Mode 7 should be 55us
//
// The above changes with smooth horizontal scrolling
// - with R3= 6: 20.0us/52.0us
// - with R3= 7: 20.5us/52.5us
// - with R3= 8: 21.0us/53.0us <<< "Normal" case
// - with R3= 9: 21.5us/53.5us
// - with R3=10: 22.0us/54.0us
//
// Hence we use thresholds of 22.5us and 54.5us
tst r3, #BIT_FIELD_TYPE
ldreq r5, odd_threshold // Use 22.5us threshold in odd field
ldrne r5, even_threshold // Use 54.5us threshold in even field
cmp r6, r5
movlt r0, #0 // Modes 0-6
movge r0, #1 // Mode 7
ldr r9, sync_detected
cmp r9, #0
bne normal_mode_test
// preserve existing mode 7 state if no sync
tst r3, #BIT_MODE7
moveq r0, #0 // Modes 0-6
movne r0, #1 // Mode 7
normal_mode_test:
tst r3, #BIT_PROBE
bne exit
tst r3, #BIT_CALIBRATE
bne skip_switch_test
// Test for keys being pressed, with variable rate auto repeat
// Note: macro uses r5 as a scratch register
ldr r8, [r4]
ldr r9, sw1_power_up
KEY_PRESS_DETECT SW1_MASK, RET_SW1, sw1counter
KEY_PRESS_DETECT SW2_MASK, RET_SW2, sw2counter
KEY_PRESS_DETECT SW3_MASK, RET_SW3, sw3counter
tst r0, #RET_SW1
moveq r8, #0
streq r8, sw1_power_up
cmpne r9, #0
bicne r0, #RET_SW1
tst r0, #(RET_SW1 | RET_SW2 | RET_SW3)
bne exit
skip_switch_test:
ldr r9, sync_detected
cmp r9, #0
beq skip_interlace_test
ldr r9, frame_countdown
cmp r9, #5
bne not_first_frame
subs r9, r9, #1
str r9, frame_countdown
ldr r9, param_detected_sync_type
tst r9, #SYNC_BIT_INTERLACED
tstne r3, #BIT_OSD
tstne r3, #BIT_FIELD_TYPE // test for interlaced sync with OSD on and wrong field
bne frame // wait for next fieldsync so always start on the same field to prevent interlace 'bounce'
not_first_frame:
cmp r9, #0
bne skip_interlace_test
tst r3, #BIT_MODE_DETECT // Have we been told to exit on mode change
beq do_interlace_test
tst r3, #BIT_INHIBIT_MODE_DETECT
bne force_interlace_test
tst r3, #BIT_MODE7
moveq r5, #0 // Modes 0-6
movne r5, #1 // Mode 7
cmp r5, r0 // Check if we have changed mode
bne exit // If so, then bail, as the frame buffer needs to be resized
b force_interlace_test
do_interlace_test:
ldr r9, param_video_type
cmp r9, #1 //VIDEO_INTERLACE
bne skip_interlace_test
ldr r9, param_sync_type
and r9, #7
cmp r9, #4
blt skip_interlace_test //if sync type is separate h/v
force_interlace_test:
// only test for interlace if video type set to interlaced or if BBC auto detect enabled
tst r3, #BIT_FIELD_TYPE1_VALID
beq detect_interlace // "we haven't yet seen two fields, so skip the test"
// XOR BIT_FIELD_TYPE and BIT_FIELD_TYPE1 to determine if the current frame is interlacd
// FT1 FT
// 0 0 -> 0
// 0 1 -> 1
// 1 0 -> 1
// 1 1 -> 0
// then XOR BIT_INTERLACED and if the result is 1 then the interlace mode has changed
tst r3, #BIT_FIELD_TYPE
eorne r3, #BIT_FIELD_TYPE1
ldr r8, param_detected_sync_type
tst r8, #SYNC_BIT_INTERLACED
eorne r3, #BIT_FIELD_TYPE1
tst r3, #BIT_FIELD_TYPE1
orrne r0, #RET_INTERLACE_CHANGED
bne exit
detect_interlace:
// copy BIT_FIELD_TYPE to BIT_FIELD_TYPE1
tst r3, #BIT_FIELD_TYPE
biceq r3, #BIT_FIELD_TYPE1
orrne r3, #BIT_FIELD_TYPE1
orr r3, #BIT_FIELD_TYPE1_VALID // set the valid bit
skip_interlace_test:
ldr r8, param_fb_pitch
ldr r9, param_v_adjust
mul r9, r9, r8
ldr r8, param_h_adjust
add r9, r9, r8
add r11, r11, r9
ldr r8, last_sync_detected
ldr r9, sync_detected
str r9, last_sync_detected
ldr r10, param_border
cmp r8, #0
cmpne r9, #1
cmpne r10, #0
beq no_sync_loss
bl clear_screen // clear non-zero border on loss of sync
b skip_all_lines
no_sync_loss:
cmp r8, #0
cmpeq r9, #1
andeq r0, r3, #BIT_MODE7
orreq r0, #RET_SYNC_TIMING_CHANGED
beq exit // if sync just returned, bail to allow recalculation of sampling clock etc
cmp r8, #0 // if sync lost for 2 fields then increase hsync threshold
cmpeq r9, #0
ldreq r5, other_hsync_threshold
streq r5, hsync_threshold
// Save a copy of the frame buffer base
push {r11}
#if defined(USE_CACHED_COMPARISON_BUFFER)
ldr r5, =dummyscreen
sub r5, r5, r11 // put comparison buffer in cached memory
#else
ldr r5, param_framebuffer0
ldr r6, param_framebuffer1
sub r5, r6, r5 // put comparison buffer in uncached screen memory
#endif
str r5, video_offset
ldr r5, param_v_offset
ldr r9, param_vsync_type
cmp r9, #4 //flywheel vsync
bne skip_fix_vsync_jitter
SHOW_VSYNC
WAIT_FOR_CSYNC_0_LONG
WAIT_FOR_CSYNC_1_LONG
subs r5, r5, #1
movmi r5, #0
READ_CYCLE_COUNTER r9
tst r3, #BIT_OSD | BIT_CALIBRATE | BIT_PROBE
bne skip_fix_vsync_jitter_saving_timestamp
ldr r10, sync_detected
ldr r8, last_sync_detected
ands r8, r8, r10
beq skip_fix_vsync_jitter_saving_timestamp
ldr r10, first_hsync_timestamp
subs r6, r9, r10
rsbmi r6, r6, #0
ldr r0, required_vsync_period
add r10, r0, r0, lsr #1
fixvloop:
cmp r6, r10
subge r6, r6, r0
bge fixvloop
subs r8, r6, r0 // flag used below
add r6, r6, r0
mov r6, r6, lsr #1 // average latest frame time with previous
ldr r0, jitter_offset
mov r7, r0
ldr r10, hsync_period
bmi negative
add r8, r8, r10, lsr #1 // half line rounding
subsloop:
subs r8, r8, r10
subpl r0, r0, #1
bpl subsloop
b continue
negative:
sub r8, r8, r10, lsr #1 // half line rounding
addsloop:
adds r8, r8, r10
addmi r0, r0, #1
bmi addsloop
continue:
cmp r0, r7
streq r6, required_vsync_period // update vertical period timing if no jitter detected.
cmp r0, #MAX_JITTER_LINES
movgt r0, #0
cmp r0, #-MAX_JITTER_LINES
movlt r0, #0
adds r5, r5, r0
movmi r5, #0
str r0, jitter_offset
skip_fix_vsync_jitter_saving_timestamp:
str r9, first_hsync_timestamp
skip_fix_vsync_jitter:
// Correct the relative positions of the odd and even frames
// In Mode 0..6, reduce the number of active lines by one for the even frame
// In Mode 7, increment the frame buffer pointer by one line for the even field
ldr r9, param_video_type
cmp r9, #1 //VIDEO_INTERLACE mode (primarily amiga)
bne nointadj
tst r3, #BIT_INTERLACED_VIDEO //is video interlaced?
beq nointadj
tst r3, #BIT_FIELD_TYPE
addne r5, r5, #1 // add one line in even fields
addne r11, r11, r2
subeq r11, r11, r2
nointadj:
ldr r6, param_fb_sizex2
eor r6, r6, #1
tst r6, #1 //now 0 if double height
tsteq r3, #BIT_INTERLACED_VIDEO
addeq r11, r11, r2
tst r3, #BIT_ELK
bne is_elk
tst r3, #BIT_INTERLACED_VIDEO
beq fixupmodes
tst r3, #BIT_FIELD_TYPE
addeq r11, r11, r2
fixupmodes:
tst r3, #BIT_FIELD_TYPE
subne r5, r5, #1 // skip one less line in even fields
is_elk:
CLEAR_VSYNC
ldr r6, param_nlines
add r5, r5, r6
str r5, total_lines
str r5, vsync_line // default for vsync line if undetectable in blanking area
skip_line_loop:
cmp r5, r6
ble skip_line_loop_exit
SHOW_VSYNC
WAIT_FOR_CSYNC_0_LONG
WAIT_FOR_CSYNC_1_LONG
subs r5, r5, #1
b skip_line_loop
skip_line_loop_exit:
push {r1-r5, r11}
ldr r12, capture_address
sub r12, r12, #4
// Call preload capture line function (runs all paths of capture code to preload it into cache)
// waits for csync so loses one line
blx r12
pop {r1-r5, r11}
// Compute the current scanline mod 10
ldr r5, param_v_offset
add r5, r5, #1
mod10:
subs r5, r5, #10
bpl mod10
add r5, r5, #10
str r5, linecountmod10
mov r5, #0
str r5, total_hsync_period
// Process active lines
ldr r5, param_nlines
b process_line_loop
.align 6 // so cache loads align
process_line_loop:
SHOW_VSYNC
// Preserve the state used by the outer code
push {r1-r5, r11}
// The capture line function is provided the following:
// r0 = pointer to current line in frame buffer
// r1 = number of complete psync cycles to capture (=param_chars_per_line)
// r2 = frame buffer line pitch in bytes (=param_fb_pitch)
// r3 = flags register
// r4 = GPLEV0 constant
// r5 = line number count down to 0 (initial value =param_nlines)
// r6 = scan line count modulo 10
// r7 = number of psyncs to skip
// r8 = frame buffer height (=param_fb_height)
// r9 = hsync scroll limits
// All registers are available as scratch registers (i.e. nothing needs to be preserved)
// Setup parameters
// Load the address of the capture_line function into r12
ldr r12, capture_address
mov r0, r11
orr r3, r3, #BIT_NO_SKIP_HSYNC
ldr r6, linecountmod10
ldr r7, param_h_offset
ldr r8, video_offset
ldr r9, hsync_scroll
// Call capture line function
blx r12 // exits with h sync timestamp in r0
// Restore the state used by the outer code
pop {r1-r5, r11}
ldr r7, last_hsync_time
str r0, last_hsync_time
subs r7, r0, r7
rsbmi r7, r7, #0
str r7, hsync_period
ldr r0, param_nlines
cmp r0, r5 //ignore 1st line as time undefined
ldrne r0, total_hsync_period
addne r0, r0, r7
strne r0, total_hsync_period
ldr r7, param_fb_sizex2
ands r7, r7, #1
// Skip a whole line to maintain aspect ratio
ldr r0, linecountmod10
addne r11, r11, r2, lsl #1
addeq r11, r11, r2
add r0, r0, #1
cmp r0, #10
moveq r0, #0
str r0, linecountmod10
subs r5, r5, #1
bne process_line_loop
tst r3, #BIT_INHIBIT_MODE_DETECT
bicne r3, #BIT_MODE7
pop {r11}
skip_all_lines:
ldr r8, frame_countdown
subs r8, r8, #1
strpl r8, frame_countdown
bpl skip_sync_time_test
ldr r8, sync_detected
cmp r8, #0
beq skip_sync_time_test // if no sync then timing comparison is meaningless
tst r3, #BIT_OSD | BIT_CALIBRATE | BIT_PROBE
bne skip_sync_time_test
ldr r7, hsync_comparison_lo
ldr r8, hsync_comparison_hi
cmp r7,r8
beq skip_sync_time_test //no sensible window
and r0, r3, #BIT_MODE7
orr r0, #RET_SYNC_TIMING_CHANGED
ldr r6, hsync_period
ldr r7, hsync_comparison_lo
ldr r8, hsync_comparison_hi
cmp r6, r7
blt exit
cmp r6, r8
bgt exit
ldr r6, vsync_period
ldr r7, vsync_comparison_lo
ldr r8, vsync_comparison_hi
cmp r6, r7
blt exit
cmp r6, r8
bgt exit
// ble no_test_half
// mov r6, r6, lsr #1 // workaround to check for half the time in case frame dropped due to genlock or palette update taking too long
// cmp r6, r7
// blt exit
// cmp r6, r8
// bgt exit
//no_test_half:
tst r3, #BIT_NO_AUTOSWITCH
bne skip_sync_time_test
ldr r8, param_sync_type
tst r8, #SYNC_BIT_COMPOSITE_SYNC //set if composite so don't check vsync polarity
bne skip_sync_time_test
orr r0, #RET_VSYNC_POLARITY_CHANGED
mov r8, #VERSION_MASK
str r8, [r4, #(GPCLR0 - GPLEV0)] //briefly switch to vsync on psync by clearing version bit
ldr r9, [r4] // dummy read for delay
ldr r6, [r4]
ldr r9, [r4] // dummy read for delay
ldr r7, [r4]
str r8, [r4, #(GPSET0 - GPLEV0)] //restore version bit
ldr r8, param_sync_type
tst r8, #SYNC_BIT_VSYNC_INVERTED
eorne r6, r6, #PSYNC_MASK
eorne r7, r7, #PSYNC_MASK
orr r6, r6, r7 // or together in case of glitches
tst r6, #PSYNC_MASK
beq exit
skip_sync_time_test:
push {r1-r5, r11}
ldr r9, =paletteFlags
ldr r8, [r9]
bic r8, r8, #BIT_SET_MODE2_16COLOUR // mode 2 emulation flag
bic r8, r8, #BIT_MODE2_PALETTE
mov r9, #0 // palette changed flag
tst r8, #BIT_IN_BAND_DETECTED
beq noInBandData
adrl r10, customPalette
ldr r12, =inBandData
ldrb r11, [r12], #1 //read 1 byte of command data
cmp r11, #0
beq noInBandData
cmp r11, #76 //sanity check on size
bgt noInBandData
mov r11, r11, lsr #1
ORR r8, #BIT_SET_MODE2_16COLOUR // mode 2 emulation enabled
ORR r8, #BIT_MODE2_PALETTE
commandloop:
ldrb r1, [r12], #1 //read 1 byte of command data
and r0, r1, #0x0f
ldrb r1, [r12], #1 //read 1 byte of command data
and r3, r1, #0xf0
orr r0, r0, r3, lsl #4
and r3, r1, #0x0f
orr r0, r0, r3, lsl #16
orr r0, r0, r0, lsl #4
ldr r2, [r10]
str r0, [r10], #4
cmp r0, r2
movne r9, #1
subs r11, r11, #1
bne commandloop
noInBandData:
ldr r10, =paletteFlags
ldr r7, [r10]
str r8, [r10]
cmp r9, #0
cmpeq r7, r8
blne osd_update_palette
pop {r1-r5,r11}
// Update the OSD in Mode 0..6
tst r3, #BIT_CLEAR
bne force_osd_update
ldr r0, param_video_type
cmp r0, #2 //VIDEO_TELETEXT
beq skip_osd_update
tst r3, #BIT_OSD
beq skip_osd_update
push {r1-r5, r11}
mov r0, #0 //do not force genlock
bl recalculate_hdmi_clock_line_locked_update
bl wait_for_vsync //wait for field sync as sometimes the update will be on the ragged edge of finishing during field sync causing glitches
pop {r1-r5, r11}
subs r6, r6, r7
rsbmi r6, r6, #0
ldr r7, field_type_threshold
cmp r6, r7
biclt r3, r3, #BIT_FIELD_TYPE1 // Odd, clear bit
orrge r3, r3, #BIT_FIELD_TYPE1 // Even, set bit
force_osd_update:
push {r1-r5, r11}
mov r0, r11
mov r1, r2 // bytes per line
bl osd_update_fast
pop {r1-r5, r11}
skip_osd_update:
bic r3, r3, #BIT_CLEAR
#ifdef MULTI_BUFFER
// Update the last drawn buffer
mov r0, r3, lsr #OFFSET_CURR_BUFFER
and r0, #3
bic r3, r3, #MASK_LAST_BUFFER
orr r3, r3, r0, lsl #OFFSET_LAST_BUFFER
// Flip to it on next V SYNC
FLIP_BUFFER
#endif
push {r1-r5, r11}
mov r0, #0 //do not force genlock
bl recalculate_hdmi_clock_line_locked_update
// Returns:
// r0=0 genlock disabled - LED off
// r0=1 genlock enabled (unlocked) - LED flash
// r0=2 genlock enabled (locked) - LED on
READ_CYCLE_COUNTER r1
mov r2, #LED1_MASK
tst r0, #1 // should LED flash?
tstne r1, #(1 << 26) // flash rate ~ 8Hz
tsteq r0, #2 // should LED be on?
ldrne r1, =GPSET0 // LED on
ldreq r1, =GPCLR0 // LED off
str r2, [r1]
pop {r1-r5, r11}
// Loop back if required number of fields has not been reached
// or if negative (capture forever)
ldr r5, param_ncapture
cmp r5, #0
blt frame
subs r5, #1
str r5, param_ncapture
bne frame
// Setup the response code
and r0, r3, #BIT_MODE7
orr r0, #RET_EXPIRED
// Return
exit:
#ifdef MULTI_BUFFER
// Save the old buffer state before exiting
and r3, r3, #MASK_LAST_BUFFER
str r3, buffer_state
// Return the current buffer state
orr r0, r0, r3
#endif
push {r0}
ldr r9, ntsc_status
tst r9, #4
moveq r0,#0
movne r0,#1
bl set_ntsccolour
pop {r0,r4-r12, lr}
mov pc, lr
key_press_reset:
push {r4-r12, lr}
ldr r4, =GPLEV0
ldr r8, [r4]
mov r0, #0
tst r8, #SW1_MASK
orreq r0, r0, #1
tst r8, #SW2_MASK
orreq r0, r0, #2
tst r8, #SW3_MASK
orreq r0, r0, #4
pop {r4-r12, lr}
mov pc, lr
// ======================================================================
// Local Variables
// ======================================================================
.align 6
.ltorg
sw1counter:
.word 0
sw2counter:
.word 0
sw3counter:
.word 0
param_framebuffer0: