6

Motivated by Wilson's question Can a Commodore 64 use two screenmodes on one scanline? I tried to implement a simple example that polls the beginning of a rasterline and then switches the screen mode in the middle. My code so far:

  c000  78          SEI
  c001  A2 19       LDX #$19  ;value for left half
  c003  A0 39       LDY #$39  ;value for right half
  c005  AD 12 D0    LDA $D012 
  c008  CD 12 D0    CMP $D012
  c00b  F0 FB       BEQ $C008 ; wait until rasterline changed
  c00d  29 07       AND #$07
  c00f  F0 12       BEQ $C023 ;skip badline
  c011  8E 11 D0    STX $D011 ;left side with text mode
  c014  EA          NOP       ;wait 2 cycles
  c015  EE FF FF    INC $FFFF ;wait 6 cycles
  c018  EE FF FF    INC $FFFF ;wait 6 cycles
  c01b  A5 FF       LDA $FF   ;wait 3 cycles
  c01d  8C 11 D0    STY $D011 ;right side with hires mode
  c020  4C 05 C0    JMP $C005
  c023  EA          NOP       ;burn some cycles
  c024  4C 05 C0    JMP $C005 

I ran into two problems here: In lines with badlines the VIC stuns the CPU during the time when the program should switch the screen mode. For that reason I skip the bad lines in above code, resulting in these horizontal lines. I tried to manipulate bits 0-2 in $D011 to surpress bad lines, but then the screen was not drawn properly. Is there a possibility to skip bad lines while still having a properly drawn screen?

Second issue: When I start the program (e.g. by SYS 49152) I get a varying offset of some cycles from the actual start of a rasterline, because my busy waiting loop is 4+2 cycles long. This shows itself in the split line being shifted to the left or right, however within my program I don't know how to detect or correct this. Any idea how to remove this start jitter?

After start, early split

After start, late split

4
  • The "jitter" is easy equalized by waiting for two lines first. Or start with an interrupt for the very first line (well, if not all that is).
    – Raffzahn
    Jan 13, 2019 at 18:11
  • Thanks for the hint, I came up with something, but it appeared rather complicated than easy. Since I can have a starting jitter between 0 to 5 cycles, I had to test up to five cases, I posted the implementation as an answer. Was that the idea that you meant or did I miss an obvious solution?
    – Peter B.
    Jan 13, 2019 at 21:21
  • 2
    No, it's about that, except it should also work in less iterations. But It's just go with the interrupt. Something like setting the IRQ to your program, seting D012/11 to 0, then when called incrementing D012 to set the interrupt to the next line. Done. Always cycle exact and shortest possible time from the line start. By doing the mode change first you might get it down to less than 14 characters from the left. Depending on your program structure you eiter RTS or, have S saved in X, just do an TXS followed by a bunch of NOPs (Ofc, this needs a way to exit at some point :))
    – Raffzahn
    Jan 13, 2019 at 21:43
  • 2
    Basically you got 63 clocks per line 'good line' (PAL) and 23 for 'bad lines'. So by using the interrupt routine and making whatever you got to do shorter than like 20 clocks, you will as well be able to switch on bad lines. Ofc, you may have to use different timing every 8th line (like D012 & 7) or so - best be calculated a line before were there are still 63 cycles. Keep in mind executing IRQ already uses 7 (+0..3) of them - RTI needs 6, so using the TXS trick can save some cycles.
    – Raffzahn
    Jan 13, 2019 at 22:01

2 Answers 2

5

Is there a possibility to skip bad lines while still having a properly drawn screen?

This is absolutely impossible for character modes and at least problematic for bitmap modes. The additional cycles in a badline are needed for accessing the "video matrix", which is a 12-bit wide access of the "screen ram" (8bit) and the color ram (4bit). In character modes, without this access, you won't have any characters as their codes are stored in the screen ram. With bitmap modes, the video matrix holds color information, so you would at least need one badline to have anything other than "black on black". Without further badlines, the colors will stay the same in vertical direction for the whole screen.

For reference, there's a very good documentation covering anything know about the VIC-II: http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt

[exact raster position] Any idea how to remove this start jitter?

There are a lot of possible approaches, and one possibility is to do exactly what you described in your self-answer. An easy way to avoid badlines with this method is to start inside the top/bottom border. Other options include the well-known "double IRQ" (have a second raster IRQ in a NOP slide, then remove the final 1-cycle jitter by checking when exactly the next raster line is reached), using slightly out-of-phase CIA timers, syncing with a sprite, and stabilizing by forcing a VIC-II DMA.

You'll find all of these methods here: http://codebase64.org/doku.php?id=base:interrupts

The DMA method is sometimes very elegant, it's explained in this thread: https://csdb.dk/forums/?roomid=11&topicid=88971&showallposts=1


But, studying the VIC-II reference above, the first time the CPU is allowed to run again in a badline, you're already at horizontal raster pos $150, which is the last character column. Therefore, unfortunately, a "sensible" horizontal split isn't possible in a badline.

9
  • The range of content one could display in character mode if one suppressed all bad lines would be rather limited, but if one wants to show the same row of characters over and over forever, I would think one could suppress all bad lines continuously once one allowed the VIC-II to fetch the desired content, so long as one never "slipped" and let it fetch the wrong row of data.
    – supercat
    Jan 15, 2019 at 0:47
  • @supercat, yes, principally same as for colors in bitmap modes, would need at least one badline per frame. But then, I didn't consider a single (maybe repeating) line of text/char gfx a "sensible" display ;) Jan 15, 2019 at 6:12
  • If the primary focus of the display (or a portion thereof) is sprite graphics, a single repeating text line could be a very reasonable and useful background (especially for scenarios where e.g. one simply wants everything blank). If the goal is to have sprites which can travel into horizontal borders, losing scan lines to bad lines would ruin the effect. To be sure, keeping all the sprites consistently "busy" or dealing with varying amounts of sprite loading would complicate things, but I think a game where sprites can fly anywhere in the borders would be possible.
    – supercat
    Jan 15, 2019 at 6:32
  • @supercat the question tells what "properly drawn screen" meant in this context -- which isn't possible without badlines... Jan 15, 2019 at 6:45
  • And btw, I think such a game is pretty much impossible. You have to fiddle with screen width twice per scanline, exactly timed (so, no interrupts possible for that), that at best allows code executing precalculated stuff -- the only opportunity for "free-form" code is in vblank, and you need some place to play music as well. Jan 15, 2019 at 6:59
6

Inspired by Raffzahn, I found a way to remove the jitter by waiting for a rasterline, then checking after 62 cycles if the next rasterline is already there, which would be an indication that could have detected the line earlier. After at most five iterations the earliest detection point is found. This only works if there is no badline during the tests, bit since a badline comes every 8 lines it is easily possible by waiting for a given line in order to start with badline-free horizon.

This programm implements the algorithm and is to be run together with the code in the question. Note that the timing between the routines is also critical, so putting the routines directly together without the JMP $C000 would require some instructions to burn 3 CPU cycles.

    c100  78          SEI
    c101  A2 19       LDX #$19  
    c103  8E 11 D0    STX $D011 ;set yscroll for ease badline detection
    c106  A9 01       LDA #$01  ;wait for rasterline 1, which is followed by non-badlines
    c108  CD 12 D0    CMP $D012
    c10b  D0 FB       BNE $C108
    c10d  AD 12 D0    LDA $D012 
    c110  CD 12 D0    CMP $D012 ;wait for a line change
    c113  F0 FB       BEQ $C110
    c115  A5 03       LDA $03   ;just burn 3 CPU cycles
    c117  AD 12 D0    LDA $D012
    c11a  A2 09       LDX #$09  ;loop to burn 44 CPU cycles
    c11c  CA          DEX
    c11d  D0 FD       BNE $C11C
    c11f  A6 03       LDX $03   ;just burn 3 CPU cycles
    c121  CD 12 D0    CMP $D012 ;after 62 cycles, is the new rasterline already there?
    c124  EA          NOP       ;for timing
    c125  D0 F0       BNE $C117 ;jump if the rasterline was already there
    c127  4C 00 C0    JMP $C000 ;we are in sync, start routine
1
  • if you turn the screen off you can reduce the code to a few lines :D
    – Zibri
    May 12, 2020 at 5:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .