Card B
Three individual video card designs, built out of 74-series derivative CMOS logic. No CPLDs or FPGAs! The cards are specifically intended to be used with old-school computer systems (Z80, 6502, et al.) and have 5V TTL/CMOS, 8-bit microprocessor-compatible control interfaces.
This page is currently under construction and serves as a scrapbook page as I develop, test and verify these designs. So far I have only fully completed card B, which is pictured here displaying the pretty Mandelbrot. The schematic diagram and the zipped Gerber files for this card can be downloaded from the links provided above.
I'm currently waiting on the boards for Card C to arrive from the PCB manufacturer. Card A is still in the PCB layout phase. Comprehensive documentation / technical details / applications information
for all three cards will eventually be produced/provided and collated into a single document.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CCS C Compiler // Demonstration mathematical and graphics drawing program for VGA card. // Initial prototype code // 21/04/2021 // Card B - 640x480x64C // www.glensstuff.com ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include <16f877A.h> #include <MATH.H> // For sin and cos #FUSES HS,NOWDT,PUT,NOPROTECT,NOLVP,NOCPD,NOWRT,BROWNOUT #use delay(clock=20000000) #use fast_io(A) #use fast_io(B) #use fast_io(C) #use fast_io(D) #use fast_io(E) #define xoffset 320; // Screen center x #define yoffset 240; // Screen center y int C, D, E; // Bytes for ClearRAM routine int32 address; // Video memory address int8 colour; // Video memory data int8 start; int16 iterations; int16 x1, y1, x2, y2; // DrawLine and DrawFill coordinates int16 xx, yy; // Working variables for line algorithm int32 x, y; // " float deltaX, deltaY, deltaError, error; // " float xxx, yyy, zzz, dx, dy, dz, radians, r, n; // Working variables for trig. and integration ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void WriteRam() // Routine to write pixel data to video RAM { address = (x + 1) + (y * 1024); // Calculate RAM address output_C(make8(address, 0)); // Update address bus output_D(make8(address, 1)); // " output_E(make8(address, 2)); // " while (!input(PIN_A4)); while (input(PIN_A4)); // Wait for negative edge of !H_BLANK output_B(colour); // Put pixel data on data bus output_low(pin_A0); // Assert !ACCESS_RAM output_low(pin_A1); output_high(pin_A1); // Strobe !WRITE output_high(pin_A0); // Un-assert !ACCESS_RAM } void QuadrantA() // Bresenham's line algorithm for x2 >= x1 & y2 >= y1 { deltaX = (x2 - x1); deltaY = (y2 - y1); if (deltaX >= deltaY) { deltaError = (deltaY / deltaX); error = 0; y = y1; for (x = x1; x <= x2; x++) { WriteRAM(); error = error + deltaError; if (error >= 0.5) { y++; error = error - 1; } } } else { deltaError = (deltaX / deltaY); error = 0; x = x1; for (y = y1; y <= y2; y++) { WriteRAM(); error = error + deltaError; if (error >= 0.5) { x++; error = error - 1; } } } } void QuadrantB() // Bresenham's line algorithm for x2 < x1 & y2 >= y1 { deltaX = (x1 - x2); deltaY = (y2 - y1); if (deltaX >= deltaY) { deltaError = (deltaY / deltaX); error = 0; y = y1; for (xx = (x1 + 1); xx > x2; xx--) { x = (xx - 1); WriteRAM(); error = error + deltaError; if (error >= 0.5) { y++; error = error - 1; } } } else { deltaError = (deltaX / deltaY); error = 0; x = x1; for (y = y1; y <= y2; y++) { WriteRAM(); error = error + deltaError; if (error >= 0.5) { x--; error = error - 1; } } } } void QuadrantC() // Bresenham's line algorithm for x2 < x1 & y2 < y1 { deltaX = (x1 - x2); deltaY = (y1 - y2); if (deltaX >= deltaY) { deltaError = (deltaY / deltaX); error = 0; y = y1; for (xx = (x1 + 1); xx > x2; xx--) { x = (xx - 1); WriteRAM(); error = error + deltaError; if (error >= 0.5) { y--; error = error - 1; } } } else { deltaError = (deltaX / deltaY); error = 0; x = x1; for (yy = (y1 + 1); yy > y2; yy--) { y = (yy - 1); WriteRAM(); error = error + deltaError; if (error >= 0.5) { x--; error = error - 1; } } } } void QuadrantD() // Bresenham's line algorithm for x2 >= x1 & y2 < y1 { deltaX = (x2 - x1); deltaY = (y1 - y2); if (deltaX >= deltaY) { deltaError = (deltaY / deltaX); error = 0; y = y1; for (x = x1; x <= x2; x++) { WriteRAM(); error = error + deltaError; if (error >= 0.5) { y--; error = error - 1; } } } else { deltaError = (deltaX / deltaY); error = 0; x = x1; for (yy = (y1 + 1); yy > y2; yy--) { y = (yy - 1); WriteRAM(); error = error + deltaError; if (error >= 0.5) { x++; error = error - 1; } } } } void DrawLine() // Routine for plotting lines from start point (x1, y1) to { // end point (x2, y2) if (x2 >= x1) { if (y2 >= y1) QuadrantA(); if (y2 < y1) QuadrantD(); } if (x2 < x1) { if (y2 >= y1) QuadrantB(); if (y2 < y1) QuadrantC(); } x1 = x; y1 = y; // Set start point of the next line to the end point of line } // just plotted if x1 and y1 not redefined on the next call void DrawFill() // Routine for drawing a rectangular fill. { // (x1, y1) = upper left corner. (x2, y2) = lower left corner for (y = y1; y <= y2; y++) { for (x = x1; x <= x2; x++) { WriteRAM(); } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void ClearRAM() // Fast routine for clearing video memory { // Does not wait for !H_SYNC C = 0; D = 0; E = 0; output_C(C); output_D(D); output_E(E); // Zero address bus output_B(0); // Data bus = 0x00 for black output_low(pin_A0); // Assert !ACCESS_RAM // Video is hardware blanked when !ACCESS_RAM low while (E < 8) { output_low(pin_A1); output_high(pin_A1); // Strobe !WRITE C++; output_C(C); // Increment through and update address if (C == 0) { // " D++; output_D(D); // " if (D == 0) { // " E++; output_E(E); // " } } } output_high(pin_A0); // Un-assert !ACCESS_RAM } void DrawPolarRoses() // Iterative overlaying polar rose plots for the { // form r = a sin(n(angle)), stepping n start = 0; colour = 53; for (n = 1; n <= 6; n = n + 0.5) { for (radians = 0; radians <= 6.2832; radians = radians + 0.05) { // Step angle, range 0 through 360 (2pi) degrees r = 200 * sin(n * radians); // Calculate r for polar rose function x2 = (sin(radians) * r) + xoffset; // Polar coordinates to Cartesian coordinates, x y2 = (cos(radians) * r) + yoffset; // Polar coordinates to Cartesian coordinates, y if (start == 0) { start = 1; x1 = x2; y1 = y2; // Set initial starting coordinate for DrawLine } else DrawLine(); } colour++; // Plot next rose in next colour of palette } } void DrawLinePattern() { colour = 0x0B; x2 = 0; y2 = 0; while (x2 < 639) { x1 = 0; y1 = 479; DrawLine(); x2 = x2 + 20; } colour = 0x0C; While (y2 < 479) { x1 = 0; y1 = 479; DrawLine(); y2 = y2 + 20; } x1 = 0; y1 = 479; y2 = 479; DrawLine(); colour = 0x3C; x2 = 639; y2 = 0; start = 0; while (start <= 31) { x1 = 639; y1 = 479; DrawLine(); x2 = x2 -20; start++; } colour = 0x0F; x2 = 0; while (y2 <479) { x1 = 639; y1 = 479; DrawLine(); y2 = y2 + 20; } x1 = 639; y1 = 479; y2 = 479; DrawLine(); } void DrawCascadingSquares() { colour = 0; x1 = 0; y1 = 0; while (colour <= 63) { x2 = x1 + 72; y2 = y1 + 38; DrawFill(); x1 = x1 + 9; y1 = y1 + 7; colour++; } colour = 0; x1 = 567; y1 = 0; while (colour <=63) { x2 = x1 + 72; y2 = y1 + 38; DrawFill(); x1 = x1 - 9; y1 = y1 + 7; colour++; } } void DrawChequerBoard() { colour = 1; for (x1 = 120; x1 <= 480; x1 = x1 + 40) { x2 = x1 + 39; colour = 1 - colour; for (y1 = 40; y1 <= 400; y1 = y1 + 40) { y2 = y1 + 39; DrawFill(); colour = 1 - colour; } } } void DrawSinXdivXfunction() // Iterative sin(x)/x plot with stepped variables in { // isometric projection colour = 0x3F; start = 0; dy = 1; dz = 10; // Plotting colour = white and initial conditions for (zzz = -100; zzz <= 100; zzz = zzz + 10) { // Step z start = 0; for (radians = -5; radians <= 5; radians = radians + 0.1) { // Step radians xxx = (radians * 20); // Compute x coordinate yyy = sin(radians * dy); // Compute y coordinate yyy = -(dz * (yyy / (radians * dy))); // " x2 = (xxx - zzz) + xoffset; // Isometric projection transform, x axis, 45 degrees y2 = (yyy - (xxx + zzz)) + yoffset; // Isometric projection transform, y axis, 45 degrees // Sin(45) = cos(45) so trig. omitted if (start == 0) { start = 1; x1 = x2; y1 = y2; // Set initial starting coordinate for DrawLine } else DrawLine(); } dy = dy + 0.2; // Linear step increase of ripple multiplier for next iteration dz = dz * 1.12; // Exponential step increase of amplitude multiplier for next iteration } } void DrawLandscape() { colour = 0x10; x1 = 0; y1 = 0; x2 = 639; y2 = 119; DrawFill(); // Draw blue and green screen fill colour = 0x20; x1 = 0; y1 = 120; x2 = 639; y2 = 239; DrawFill(); // " colour = 0x30; x1 = 0; y1 = 240; x2 = 639; y2 = 359; DrawFill(); // " colour = 0x04; x1 = 0; y1 = 360; x2 = 639; y2 = 399; DrawFill(); // " colour = 0x08; x1 = 0; y1 = 400; x2 = 639; y2 = 439; DrawFill(); // " colour = 0x0C; x1 = 0; y1 = 439; x2 = 639; y2 = 479; DrawFill(); // " colour = 0x03; // Draw red border x1 = 0; y1 = 0; x2 = 639; y2 = 3; DrawFill(); // " x1 = 636; y1 = 0; x2 = 639; y2 = 479; DrawFill(); // " x1 = 0; y1 = 476; x2 = 639; y2 = 479; DrawFill(); // " x1 = 0; y1 = 0; x2 = 3; y2 = 479; DrawFill(); // " colour = 0x0F; // Draw sun for (radians = 0; radians <= 6.28; radians = radians + 0.05) { // " x1 = 100; y1 = 100; // " x2 = 100 + (50 * sin(radians)); y2 = 100 + (50 * cos(radians)); // " DrawLine(); // " } } void ComputeAndPlotLorenzAttractor() { iterations = 0; start = 0; colour = 0x3F; xxx = 1; yyy = 0; zzz = 20; // Initial conditions while (iterations < 7000) { dx = (-10 * xxx) + (10 * yyy); // Compute x dy = (28 * xxx) - yyy - (xxx * zzz); // Compute y dz = (-2.67 * zzz) + (xxx * yyy); // Compute z xxx = xxx + (dx * 0.01); // Integration step x yyy = yyy + (dy * 0.01); // Integration step y zzz = zzz + (dz * 0.01); // Integration step z x2 = (xxx * 12) + xoffset; // Line plotting coordinate x y2 = (-zzz * 8) + 450; // Line plotting coordinate y if (start == 0) { start = 1; x1 = x2; y1 = y2; // Set initial starting coordinate for DrawLine } else { DrawLine(); iterations++; } } } void WipeOut() { x1 = 0; y1 = 0; x2 = 639; y2 = 479; colour = 0x00; DrawFill(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void MAIN() { delay_ms(3000); set_tris_A(0x10); set_tris_B(0xC0); set_tris_C(0x00); // Setup IO ports set_tris_D(0x00); set_tris_E(0x00); // " output_high(pin_A0); output_high(pin_A1); output_high(pin_A2); // !ACCESS_RAM, !WRITE & !READ = high ClearRAM(); DrawPolarRoses(); DrawLinePattern(); DrawCascadingSquares(); DrawChequerBoard(); DrawSinXdivXfunction(); delay_ms(1000); DrawLandscape(); ComputeAndPlotLorenzAttractor(); delay_ms(2000); WipeOut(); }