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 [2018-10-29 10:50] – [Sokrates' variant] mrr19121970base:detect_pal_ntsc [2020-11-11 01:49] (current) – [Very short full VIC-type detection] copyfault
Line 1: Line 1:
 ====== Detect NTSC/PAL ====== ====== Detect NTSC/PAL ======
  
-Since you cannot rely on $02a6 to detect NTSC/PAL, you better do the check yourself. The theory behind these checks is simply that PAL and NTSC systems have different amounts of rasterlines, which can thus serve as the basis for a detection check. +Since you cannot rely on $02a6 to detect NTSC/PAL, you better do the check yourself. The theory behind these checks is simply that PAL and NTSC systems have different amounts of rasterlines, which can thus serve as the basis for a detection check. For alternative approach, especially useful if you also need to use TOD, check [[efficient_tod_initialisation|Efficient TOD initialisation]] page.
 ===== J0x variant ===== ===== J0x variant =====
  
Line 827: Line 826:
 </code> </code>
  
 +===== When size really matters =====
 +
 +by Copyfault/The Solution/The Obsessed Maniacs
 +
 +In the following I'm going to present two routines for detecting PAL/NTSC: the first one is used for telling a EU-PAL-chip (with 63 cycles per line) apart from a "new NTSC"-chip (with 65cycles/line). This suffices in most cases since the mentioned systems were the most common in EU or US, resp.
 +
 +The 2nd routine is capable of detecting any of the four VIC-types that have been mentioned earlier on this page (EU-PAL, NTSC old _and_ new and the one for the Drean-PAL-system).
 +
 +==== Short PAL/NTSC detection ====
 +
 +The basic idea is to constantly read the raster beam position and keeping it in a backup register until rasterline==0 (or rasterline==$100) is reached. Then the backup register holds the value of the last line that was read before reaching line 0 (or $100 resp.).
 +
 +In case the last line was $ff, it does not tell much about the system at hand. Thus, the rasterline-read-and-backup-procedure is repeated until the backup value is not $ff afterwards.
 +
 +The well-known table of VIC-specs now reveals that the backup value must be one of the following:
 +<code>
 +#$37 -> 312 rasterlines -> PAL
 +#$06 -> 263 rasterlines -> new NTSC 
 +#$05 -> 262 rasterlines -> old NTSC
 +</code>
 +
 +It's even possible to tell new and old NTSC apart this way (not just PAL vs. NTSC as stated in the preface). The approach is NOT capable to distinguish between the different PAL-variants (PAL N as used in the Drean-systems and EU-PAL) since they have the same number of rasterlines per frame.
 +
 +
 +=== Source Code ===
 +<code>
 +chk:          
 +              ldx #$aa   //$aa = TAX
 +              lda $d012
 +              bne chk+1
 +              txa
 +              bmi chk
 +</code>
 +This routine continuously checks $D012==0 while saving the last read $D012-value in X and repeats the whole procedure when the value stored in X has the MSB set. This luckily suffices to distinguish the case "last line was $ff" from "last line was the last one of the frame", since the highest possible value is $37 (on PAL).
 +
 +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.txt · Last modified: 2020-11-11 01:49 by copyfault