The Raster bar, also known as Copper bar, is a very popular graphics effect seen on many of the personal computers in the 80’s through 90’s era.
It was widely used in video games and especially graphics demos. Here is a link to the wikipedia article for more detail, https://en.wikipedia.org/wiki/Raster_bar.
It is accomplished by changing the background color in-between scan lines being drawn on the monitor/screen. This requires accessing the video controller hardware directly and changing appropriate registers as well as a way to time it precisely with the screen being drawn.
The timing part can either be accomplished by setting an interrupt handler on systems that supported raster interrupts such as the Commodore 64, or by polling the video controller status registers for the Vsync and Hsync flags on hardware that didn’t, such as the IBM PC and clones. By doing this you could calculate what position the screen refresh is at and there by when to change the background color to draw your desired bars composed of screen raster lines.
How the actual color change was accomplished is hardware dependent, on some systems you would change the color look up table, on the CGI video controller you could simply write out to the color select register (port address 3D9hex).
On the VGA architecture it is done by reprogramming the internal DAC (digital to analog converter) for whatever color the background is, this again is done with some port writes to the VGA registers.
Rendering raster bars correctly is very time sensitive as such code not only had to be optimized to run fast, but often had to be written to be cycle exact in order not to change precalculated delays. Some amazing demos were accomplished by talented programmers using assembly programming, cycle counting and precomputed color tables.
Watching some YouTube videos talking about the gfx demo scene on Commodore 64 I felt inspired to dig up an old DOS PC laptop and write a few quick and very simple examples of this in Turbo C. They may or may not work correctly when emulated depending on the DOS emulator used, I ran them on old Pentium machine from 1998 booted in DOS.
The first example is implemented by only polling the Vsync flag and then delays are used to render the screen as desired, at that i’m using the Turbo C delay() function as such we have limited precision of where we render the bar, you can see that in the bar movement. This can be improved by using the PIT (programmable interrupt timer) as was often done or writing/using a more precise delay routine.
Here is the code snippet for this example (more technical detail in the comments of second example code):
// Hristogueorguiev.com while(!kbhit()){ do{}while(inportb(0x3da)&8); do{}while(!(inportb(0x3da)&8)); delay(vpos_delay); outportb( 0x3c8, 0x0); outportb( 0x3c9, (vpos_delay*10)); outportb( 0x3c9, vpos_delay); outportb( 0x3c9, 0x99); delay(1); outportb(0x3c8, 0x0) outportb(0x3c9, 0x00); outportb(0x3c9, 0x00); outportb(0x3c9, 0x00) anim_delay++; if(anim_delay > 15) { anim_delay = 0; vpos_delay++ if(vpos_delay > 13) vpos_delay = 1; } }
In the second example you can see another approach to this, where we poll both the Vsync and Hsync flags and are able to position things very very precisely, as well as auto adjust to different refresh rates. The disadvantage to polling is that those CPU cycles are wasted and we aren’t able to use that time to perform other graphics effects.
And here is the code for the second example with some more technical details:
// VGA horizontal Raster (Copper) bars 101 // Hristo I. Gueorguiev 2018 (Throwback week) // Hristogueorguiev.com #include <stdio.h> #include <dos.h> #include <conio.h> void main(){ int vpos_delay = 0; // Vertical position start counter int anim_delay = 0; // Animation delay / bar movement speed int i; // outportb(0x3d9, 0xFF); CGA Color select printf("test"); // put some text on sceen to scroll raster bar behind while(!kbhit()){ // Repeat our "demo" until a key is pressed do{}while(inportb(0x3DA)&8); // do{}while(!(inportb(0x3DA)&8)); // Wait for vertical retrace for(i=0;i<vpos_delay;i++){ // Wait for a number of horizontal retraces do{}while(inportb(0x3DA)&1); // This is how we place raster bar @ do{}while(!(inportb(0x3DA)&1)); // a selected vertical position } // Draw top half of raster bar by redefining the BG color from black to what // we want for specific section of screen by timing the position of the // "screen trace beam" (or in the case of now days, pixel refresh timing since CRTs // have been dead a long time). for(i=0;i<25;i++){ outportb(0x3C8, 0x00); // (Color index)Select color we want to change in DAC outportb(0x3C9, i*2); // Value for R = i * 2 outportb(0x3C9, 0x00); // Value for G outportb(0x3C9, 0x00); // Value for B do{}while(inportb(0x3DA)&1); do{}while(!(inportb(0x3DA)&1)); // Make sure we change the color only once per line } // Draw bottom half of raster bar for(i=24;i>0;i--){ outportb(0x3C8, 0x00); outportb(0x3C9, i*2); outportb(0x3C9, 0x00); outportb(0x3C9, 0x00); do{}while(inportb(0x3DA)&1); do{}while(!(inportb(0x3DA)&1)); } // Set BG color back to black for rest of screen trace outportb(0x3C8, 0x00); outportb(0x3C9, 0x00); outportb(0x3C9, 0x00); outportb(0x3C9, 0x00); //Calculate vertical position based on animation delay preset anim_delay++; if(anim_delay>1){ anim_delay = 0; vpos_delay++; if(vpos_delay > 350) vpos_delay = 1; } } }