User Tools

Site Tools


base:detect_pal_ntsc

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
base:detect_pal_ntsc [2020-11-10 21:55] – [Source Code] copyfaultbase:detect_pal_ntsc [2020-11-11 01:49] (current) – [Very short full VIC-type detection] copyfault
Line 850: Line 850:
  
  
-==== Source Code ====+=== Source Code ===
 <code> <code>
 chk:           chk:          
Line 862: Line 862:
  
 Why that [''ldx #$aa'']? There's a small probability that the routine starts when we accidently **are** on line $0 (or $100). The [''bne chk+1''] would not branch, i.e. the check-for-line$0-loop would end promptly, without any backup value stored in X yet. Thus, we need an init value for X that forces the line$0-check to be repeated in this rare case. It could by any value with MSB set, but using $aa (=TAX as opcode) contributes to making the whole routine as short as possbile. Why that [''ldx #$aa'']? There's a small probability that the routine starts when we accidently **are** on line $0 (or $100). The [''bne chk+1''] would not branch, i.e. the check-for-line$0-loop would end promptly, without any backup value stored in X yet. Thus, we need an init value for X that forces the line$0-check to be repeated in this rare case. It could by any value with MSB set, but using $aa (=TAX as opcode) contributes to making the whole routine as short as possbile.
 +
 +
 +==== Very short full VIC-type detection ====
 +
 +Another approach for detecting the VIC-type in the machine at hand was brought up by Krill: by a combination of waiting for a specific line and waiting for a specific number of cycles.
 +
 +Before presenting a real 6510 code-snipplet, let's have a look at some pseudo-code first
 +<code>
 +wait_for_line($ff)
 +wait_for_no_of_cycles(63)
 +read_rasterline
 +</code>
 +Waiting for line $ff is just an example, it could be any line, assuming we do not have badlines "in the way". Since we're going to do some cycle calculation in the following, let's also assume that no IRQs are active that might "steal" cycles from the detection routine while running.
 +
 +In the above pseudo-code we wait for line $ff and then wait for 63 cycles.
 +
 +Simple question: in which raster line are we now?
 +
 +Not-so-simple answer: depends on 
 +  -  the cycle position in line $ff at which the wait_for_no_of_cycles(63) started and
 +  -  which VIC-type we have in our machine.
 +
 +If the wait_for_line($ff) ended at cycle position 1 (not possible in reality, but let's stick to it for the moment), then after 63 cycles we are at cycle position 64 - IF it exists! Here the different no. of cycles per line come into play: on an NTSC-system, we'd still be on line $ff (both old and new versions have >=64cycles per line), on a EU-PAL we'd be at cycle 1 of line $100 already. And PAL-N (as used in Drean-systems) also has 65 cycles per line, so on this platform we'd also be in line $ff, like on the NTSC-systems.
 +
 +What do we learn from this, how can we exploit this? The more cycles we wait, the bigger the difference between the cycle positions at which the wait-loop ends. One could think of the different values for no.of.cycles per frame as some kind of "travel speed" of a rasterline: while EU-PAL is the "fastest", NTSC old is "a bit slower", whereas NTSC new and PAL-N are the slowest of all the VIC-types. So if we wait for a constant no. of cycles, the rasterbeam travels at different (=system-specific) speed to a new position. Now we "just" have to find a suitable no. of cycles that ensure different (and unique) raster positions after the wait.
 +
 +One aspect that needs extra care is the //dreaded jitter//! In reality, waiting for a specific line does not end at a fixed cycle position but rather in a certain cycle-interval (at least when performing a rasterline-wait with a simple CMP-BRANCH-loop). So we have to consider the max- and min-value of these cycle positions. The picture of the travel speed remains valid, but we have to keep in mind that not only the rasterbeam travels at a given speed but also the cycle-interval. The aim is to move this whole interval to different raster positions, depending on the VIC-type in use.
 +
 +=== A closer look at the wait_for_rasterline ===
 +<code>
 +                  lda #$ff
 +waitraster:
 +                  cmp $d012
 +                  bne waitraster
 +</code>
 +While this is short and clean, it's not possible to completely possible to predict the cycle position of line $ff that we will find us at after the wait (the first ''cmp $d012'' might occur **on** line $ff). This can be circumvented by checking for another line first.
 +<code>
 +wait_line0:       
 +                  lda $d012
 +                  bne wait_line0
 +                  lda #$ff
 +waitraster:
 +                  cmp $d012
 +                  bne waitraster
 +</code>
 +Line 0 (or $100) must be reached first before the wait-loop for line $ff starts. This ensures that the waitraster-loop starts outside of line $ff, thus running through all cycles until the desired rasterline is reached. Since a CMP $d012 takes 4 cycles and a (branching!) BNE takes 3, the best case is when the read-access of the CMP $D012 happens at cycle pos.1 of line $ff. Cycle positions 2 and 3 will be taken by the (not-taken!) branch, so the wait-loop ends at cycle pos.4. Worst case is when the read-access misses line $ff by one cycle, leading to the maximal possible delay ending at cycle pos.10.
 +
 +This is the cycle-interval we have to deal with: cycle positions 4..10. Mind that these values describe the first "free" cycle positions, i.e. the next opcode will have its first cycle at cycle pos. 4..10.
 +
 +=== VIC-type detection via cycle-waiting - a first sketch ===
 +Simplifying the line-wait a bit, let's take a closer look at the following code:
 +<code>
 +wait_line0:       
 +                  ldx $d012
 +                  bne wait_line0
 +                  dex
 +waitraster:
 +                  cpx $d012
 +                  bne waitraster
 +                  
 +                  ldy #$fc
 +cycle_wait_loop:  
 +                  nop                 // 2 cycles
 +                  bit $ea             // 3 cycles
 +                  dey                 // 2 cycles
 +                  bne cycle_wait_loop // 3 cycles (taken) | 2 cycles (not taken)
 +                  
 +                  lda $d012           // 4 cycles
 +</code>
 +The waitraster-loop exits on line $ff at cycle pos.4..10. After the ''ldy #$fc'' it's cycle pos.6..12.
 +
 +Now we have that large loop. it runs for (2+3+2+3)*$fb + (2+3+2+2) = 10*252 - 1 = 2519 cycles. The rasterline is read four cycles later, so the read access of the lda $d012 happens 2523 cycles after the end of the waitraster-loop (cycle pos 6..12 in line $ff are the first "free" cycles, so we will do further calculations with 5..11 in order to match the read-access-cycle of the ''lda $d012'' exactly).
 +
 +Now it boils down to basic modulo-arithmetics, or even simpler, to decomposing the cycle-value into a multiple of N and the rest, with N again denoting the no. of cycles per line.
 +<code>
 +min. cycle:  5+2523 = 2528 = 40*63 +  8 = 39*64 + 32 = 38*65 + 58
 +max. cycle: 11+2523 = 2534 = 40*63 + 14 = 39*64 + 38 = 38*65 + 64
 +</code>
 +This shows that the complete interval ends on the same rasterline: for PAL (63cyc/line) it will be line $ff+40 = $127, for old NTSC it will be $ff+39 = $126(=does not exist on old NTSC) = $21(on old NTSC), for new NTSC it'll be $ff+38 = $125(=does not exist) = $1f(on new NTSC) - and finally, for PAL-N it's line $ff+38 = $125.
 +
 +In consequence, reading the $d012-value gives different values, depending on the VIC-type of the machine. 
 +
 +=== Source Code ===
 +The code above can be optimised at some spots. This has impact e.g. on the rasterline we start the cycle-wait in and consequently also on the rasterline that is read at the end.
 +<code>
 +chk_victype:      
 +                  sei
 +                  ldy #$04
 +ld_DEY:           
 +                  ldx #DEY     //DEY = $88
 +waitline:         
 +                  cpy $d012
 +                  bne waitline
 +                  dex
 +                  bmi ld_DEY + 1
 +cycle_wait_loop:  
 +                  lda $d012 - $7f,x
 +                  dey
 +                  bne cycle_wait_loop
 +                  
 +                  and #$03
 +                  rts
 +</code>
 +Instead of having two seperate checks of a rasterline, there are several rasterline checks combined now, effectively shortening that part. The waitline ends on line $fc, which is also the starting line for the cycle-wait-loop. Since I put the ''lda rasterline'' inside the wait-loop, the read access of the ''LDA'' of the last loop run is performed five cycles before the end of the loop. This is compensated for by the ''dex : bmi ld_dey + 1'' of the new waitline-loop, i.e. when the cycle_wait_loop starts, we're already at cycle positions 8..14 of line $ff. I leave it to the reader to repeat the calculation that the cycle-position interval also ends on different lines with this setup. The result (after the ''and #$03'') is
 +<code>
 +$00: EU-PAL
 +$01: NTSC old
 +$02: PAL-N
 +$03: NTSC-new
 +</code>
 +Ofcourse these values depend on the rasterline that is reached after the whole wait-procedure, which in turn depends on the starting line. You might want to play with it in case you need the order of the VIC-types changed. I like it this way as one can interpret bit0 of the result as "NTSC-flag" and bit1 as "65cycle-flag".
base/detect_pal_ntsc.1605041735.txt.gz · Last modified: 2020-11-10 21:55 by copyfault