//------------------------------------------------------------------- // flipflop.cpp // // This program implements a visual simulation of the principle // employed by the PC's Programmable Interval-Timer, as used to // generate a square-wave output-signal for producing the sound // of a musical tone on the PC's internal speaker. Varying the // value in the 'Latch' register controls the tone's frequency. // // compile with: $ g++ flipflop.cpp int86.cpp -o flipflop // // programmer: ALLAN CRUSE // date begun: 09 SEP 2005 // completion: 10 SEP 2005 //------------------------------------------------------------------- #include // for getchar() #include // for fcntl() #include // for read() #include // for atexit() #include // for strlen() #include // for signal() #include // for inb(), outw(), etc. #include // for tcgetattr(), tcsetattr() #include // for nanosleep() #include "int86.h" // for init8086(), int86() #define VRAM_BASE_FOR_VGA 0x000A0000 #define KEY_ESCAPE 0x1B typedef unsigned char u8; u8 *font, *vram = (u8*)VRAM_BASE_FOR_VGA; struct vm86plus_struct vm; int vga_mode = 0x6A, hres = 800, vres = 600; int done = 0, font_ht = 16, pitch = hres/8; char *title[] = { " Square-Wave Output ", " Timer-Counter Simulation ", " ", " Professor Allan Cruse ", " Computer Science Department ", " University of San Francisco ", " 10 September 2005 " }; struct termios origtty; void upon_exit( void ) { tcsetattr( STDIN_FILENO, TCSAFLUSH, &origtty ); } void on_input( int signo ) { int inch = 0; read( STDIN_FILENO, &inch, sizeof( inch ) ); if ( inch == KEY_ESCAPE ) done = 1; } void draw_glyph( int x, int y, int fgcolor, int bgcolor, int len, u8 *pat ) { int which = y * hres + x; int where = which / 8; int shift = which % 8; int mask = 0xFF >> shift; u8 *dst = vram + where; outw( 0x0305, 0x3CE ); // Write Mode 3 ("Masked Write") for (int row = 0; row < len; row++) { outw( (bgcolor<<8) | 0, 0x3CE ); // Set/Reset u8 temp1 = dst[ row * pitch ]; dst[ row * pitch ] = mask; temp1 = dst[ row * pitch + 1 ]; dst[ row * pitch + 1] = ~mask; outw( (fgcolor<<8) | 0, 0x3CE ); // Set/Reset u8 temp2 = dst[ row * pitch ]; dst[ row * pitch ] = pat[ row ]>>shift; temp2 = dst[ row * pitch + 1 ]; dst[ row * pitch + 1] = pat[ row ] << (8 - shift); } } void setpixel( int x, int y, int color ) { int which = y * hres + x; int where = which / 8; int shift = which % 8; int mask = 0x80 >> shift; outw( 0x0305, 0x3CE ); // "Masked Write" (Write Mode 3) outw( (color<<8) | 0, 0x3CE ); // Set/Reset register u8 latch = vram[ where ]; vram[ where ] = mask; } void copy_area( int dx, int dy, int horz, int vert, int sx, int sy ) { u8 *dst = vram + ( (dy + vert) * hres + dx )/8; u8 *src = vram + ( (sy + vert) * hres + sx )/8; outw( 0x0105, 0x3CE ); // Write Mode 1 ("Latched Write") for (int row = 0; row < vert; row++) { src -= pitch; dst -= pitch; for (int col = 0; col < horz/8; col++) dst[ col ] = src[ col ]; } } void draw_message( int x, int y, int color, char *msg ) { u8 *pat; int ch, i = 0; while ( ch = msg[i] ) { pat = font + font_ht*ch; draw_glyph( x, y, color, 0, font_ht, pat ); x += 8; ++i; } } void vertical_retrace_sync( void ) { // stall if vertical retrace is already active while ( ( inb( 0x3DA ) & 8 ) == 8 ); // Input Status #1 // now wait until next vertical retrace begins while ( ( inb( 0x3DA ) & 8 ) == 0 ); // Input Status #1 } int main( int argc, char **argv ) { // accept 'latchvalue' entered as command-line argument int latchvalue = 16, input = 0; if ( argc > 1 ) input = atoi( argv[1] ); if (( input > 0 )&&( input < 64 )) latchvalue = input; // enable noncanonical keyboard input tcgetattr( STDIN_FILENO, &origtty ); atexit( upon_exit ); struct termios worktty = origtty; worktty.c_lflag &= ~( ECHO | ICANON | ISIG ); worktty.c_cc[ VTIME ] = 0; worktty.c_cc[ VMIN ] = 1; tcsetattr( STDIN_FILENO, TCSAFLUSH, &worktty ); // setup pointer to 8x16 character-font glyph-table init8086(); // configure memory and IO vm.regs.eax = 0x1130; // Find Font-Table Address vm.regs.ebx = 0x0600; // 8x16 character-glyphs int86( 0x10, vm ); // invoke BIOS service font = (u8*)( (vm.regs.es * 16L) + vm.regs.ebp ); font_ht = vm.regs.ecx; // count of glyph scanlines // set the graphhics display mode vm.regs.eax = vga_mode; // Set Display Mode int86( 0x10, vm ); // invoke BIOS service // draw a white screen border int xmin = 0, xmax = hres-1, ymin = 0, ymax = vres-1; int x = xmin, y = ymin, color = 15; do { setpixel( x, y, color ); ++x; } while ( x < xmax ); do { setpixel( x, y, color ); ++y; } while ( y < ymax ); do { setpixel( x, y, color ); --x; } while ( x > xmin ); do { setpixel( x, y, color ); --y; } while ( y > ymin ); // setup parameters int base_y = vres/2; int latchx = 720, countx = 640, slabs = 64, slabht = 4; int latchy = base_y - slabs * slabht; u8 stack1[] = { 0xFF, 0xC0, 0xC0, 0xC0, 0xFF }; u8 stack2[] = { 0xFF, 0x00, 0x00, 0x00, 0xFF }; u8 stack3[] = { 0xFF, 0x03, 0x03, 0x03, 0xFF }; // draw the title and legend messages int titlex = ( hres - 8*strlen( title[0] ) )/2; int titley = ( vres - 7*16 )/4 + (vres / 2); for (int i = 0; i < 7; i++) { int y = titley + 16*i; color = ( i < 2 ) ? 10 : 2; draw_message( titlex, y, color, title[i] ); } getchar(); // draw the 'latch' stack color = 7; draw_message( latchx-12, base_y+24, color, "LATCH" ); draw_message( countx-12, base_y+24, color, "COUNT" ); int fgcolor = 15, bgcolor = 0; for (int i = 0; i < slabs; i++) { x = latchx; y = base_y - (i+1) * slabht; bgcolor = ( i < latchvalue ) ? 3 : 0; draw_glyph( x+0, y, fgcolor, bgcolor, slabht+1, stack1 ); draw_glyph( x+8, y, fgcolor, bgcolor, slabht+1, stack3 ); } // draw the 'flipflop' sprite u8 flip1[] = { 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; u8 flip2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xC0, 0xE0, 0xB0, 0x98, 0x0C, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; u8 flop1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F }; u8 flop2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x0C, 0x98, 0xB0, 0xE0, 0xC0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //--------------------- // begin the animation //--------------------- // activate asynchronous keyboard notification signal( SIGIO, on_input ); fcntl( STDIN_FILENO, F_SETOWN, getpid() ); int val = fcntl( STDIN_FILENO, F_GETFL, 0 ); fcntl( STDIN_FILENO, F_SETFL, val | O_ASYNC ); struct timespec tm = { 0, 100000000 }; int ff_x = 600, ff_y = base_y - 16, ff_len = 32; int flipflop = 1; while ( !done ) { // draw the 'arrow' stack u8 arrow1[] = { 0x30, 0x60, 0xFF, 0x60, 0x30 }; u8 arrow2[] = { 0x00, 0x00, 0xFF, 0x00, 0x00 }; int arrowx = 680; fgcolor = 3, bgcolor = 0; for (int i = 0; i < slabs; i++) if ( i % 2 ) { x = arrowx; y = base_y - (i+1) * slabht; draw_glyph( x+0, y, fgcolor, bgcolor, 5, arrow1 ); draw_glyph( x+8, y, fgcolor, bgcolor, 5, arrow2 ); } // draw the 'count' stack int latchwide = 16, latchhigh = 1 + slabs * slabht; int dx = countx, dy = latchy, sx = latchx, sy = latchy; copy_area( dx, dy, latchwide, latchhigh, sx, sy ); // draw the 'flipflop' sprite fgcolor = 4, bgcolor = 0; int ff_x = 600, ff_y = base_y - 16, ff_len = 32; u8 *pat1 = ( flipflop % 2 ) ? flip1 : flop1; u8 *pat2 = ( flipflop % 2 ) ? flip2 : flop2; draw_glyph( ff_x+0, ff_y, fgcolor, bgcolor, ff_len, pat1 ); fgcolor |= 8; draw_glyph( ff_x+16, ff_y, fgcolor, bgcolor, ff_len, pat2 ); // animate the 'count' stack for (int i = 0; i < latchvalue; i++) { int sy = latchy; int dy = sy + slabht; int ht = latchhigh - slabht; copy_area( countx, dy, latchwide, ht, countx, sy ); copy_area( 8, ff_y, ff_x-8, ff_len, 16, ff_y ); nanosleep( &tm, NULL ); vertical_retrace_sync(); if ( done ) break; if ( i > 0 ) continue; // dim the 'arrow' stack fgcolor = 0; for (int i = 0; i < slabs; i++) if ( i % 2 ) { x = arrowx; y = base_y - (i+1) * slabht; draw_glyph( x+0, y, 8, 0, 5, arrow1 ); draw_glyph( x+8, y, 8, 0, 5, arrow2 ); } } ++flipflop; } // return to standard text mode vm.regs.eax = 0x0003; // Set Display Mode 3 int86( 0x10, vm ); // invoke BIOS service }