VGA modus 800x600 - filled circle wird nicht bunt auf bildschirm

jkallup

Erfahrenes Mitglied
Hallo,

Ich versuche mich gerade in VGA programmierung.
Es scheint alles zu funktionieren, außer daß ich keine filled circle zeichnen kann,
kann da mal einer drüber schauen?
Danke
jens

Code:
SetVideoMode(800,600,8);
VbeWrite(5,0);memset(fb,1,0xffff);
VbeWrite(5,1);memset(fb,1,64);
VbeWrite(5,0);
drawcircle(30,30,30,14);
plot(30,30,15);
fillcircle(230,340,30,15);     // diese zeile scheint nicht ausgeführt???
.....

#define VBE_DISPI_IOPORT_INDEX 0x01CE
#define VBE_DISPI_IOPORT_DATA  0x01CF
#define VBE_DISPI_INDEX_ID              0x0
#define VBE_DISPI_INDEX_XRES            0x1
#define VBE_DISPI_INDEX_YRES            0x2
#define VBE_DISPI_INDEX_BPP             0x3
#define VBE_DISPI_INDEX_ENABLE          0x4
#define VBE_DISPI_INDEX_BANK            0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH      0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT     0x7
#define VBE_DISPI_INDEX_X_OFFSET        0x8
#define VBE_DISPI_INDEX_Y_OFFSET        0x9

#define VBE_DISPI_DISABLED              0x00
#define VBE_DISPI_ENABLED               0x01
#define VBE_DISPI_GETCAPS               0x02
#define VBE_DISPI_8BIT_DAC              0x20
#define VBE_DISPI_LFB_ENABLED           0x40
#define VBE_DISPI_NOCLEARMEM            0x80

void VbeWrite (uint16_t index, uint16_t value) {

   outw(VBE_DISPI_IOPORT_INDEX, index);
   outw(VBE_DISPI_IOPORT_DATA, value);
}

void SetVideoMode (int xres, int yres, uint16_t bpp) {

   VbeWrite (VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
   VbeWrite (VBE_DISPI_INDEX_XRES, xres);
   VbeWrite (VBE_DISPI_INDEX_YRES, yres);
   VbeWrite (VBE_DISPI_INDEX_BPP, bpp);
   VbeWrite (VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);   
}

void SetVideoBank(uint16_t bank) { VbeWrite(VBE_DISPI_INDEX_BANK, bank); }

typedef struct __attribute__ ((packed)) {
    unsigned short di, si, bp, sp, bx, dx, cx, ax;
    unsigned short gs, fs, es, ds, eflags;
} regs16_t;
// tell compiler our int32 function is external
extern void pm_int32(unsigned char intnum, regs16_t *regs);

void testint(void);

char *VGA = (char *)0xA0000;

void plot_fast(int x,int y,byte color)
{
  VGA[y*320+x]=color;
}


void plot(uint16_t x, uint16_t y, uint16_t color)
{
    unsigned char* fb   = (unsigned char*) 0xa0000;   static
   unsigned int old_bank  = 0;
   unsigned int bank_size = 0xffff+1;
   unsigned int pixel  = y * 800 + x;
   unsigned int bank   = pixel / bank_size ;
   unsigned int offset = pixel - bank * bank_size;

   if (bank != old_bank) {
       old_bank  = bank;
       SetVideoBank(bank);
   }

    fb[offset] = color;
}

int abs(int j)
{
   return (j < 0 ? -j : j);
}

double powerOfTen(int num)
{
   double rst = 1.0;
   int i;
   if(num >= 0){
       for(i = 0; i < num ; i++){
           rst *= 10.0;
       }
   } else {
      int a = 0-num;
       int i;
       for(i = 0; i < a; i++){
           rst *= 0.1;
       }
   }
   return rst;
}
double sqrt(double a)
{
   /*
         find more detail of this method on wiki methods_of_computing_square_roots
         *** Babylonian method cannot get exact zero but approximately value of the square_root
   */
   double z = a;
   double rst = 0.0;
   int max = 8;     // to define maximum digit
   int i;
   double j = 1.0;
   for(i = max ; i > 0 ; i--){
       // value must be bigger then 0
       if(z - (( 2 * rst ) + ( j * powerOfTen(i)))*( j * powerOfTen(i)) >= 0)
       {
           while( z - (( 2 * rst ) + ( j * powerOfTen(i)))*( j * powerOfTen(i)) >= 0)
           {
               j++;
               if(j >= 10) break;
           }
           j--; //correct the extra value by minus one to j
           z -= (( 2 * rst ) + ( j * powerOfTen(i)))*( j * powerOfTen(i)); //find value of z

           rst += j * powerOfTen(i);     // find sum of a
           j = 1.0;
       }
  }
  for(i = 0 ; i >= 0 - max ; i--)
  {
      if(z - (( 2 * rst ) + ( j * powerOfTen(i)))*( j * powerOfTen(i)) >= 0)
      {
           while( z - (( 2 * rst ) + ( j * powerOfTen(i)))*( j * powerOfTen(i)) >= 0)
           {
               j++;
           }
           j--;
           z -= (( 2 * rst ) + ( j * powerOfTen(i)))*( j * powerOfTen(i)); //find value of z
          rst += j * powerOfTen(i);     // find sum of a
          j = 1.0;
      }
  }
  return rst;
}

void drawline(int x0, int y0, int x1, int y1, int colr)
{
   int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
   int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
   int err = (dx>dy ? dx : -dy)/2, e2;
   for(;;) {
           plot(x0,y0,colr);
       if (x0 == x1 && y0 == y1) break;
       e2 = err;
       if (e2 >-dx) { err -= dy; x0 += sx; }
       if (e2 < dy) { err += dx; y0 += sy; }
   }
}

fillcircle(int cx, int cy, int r, int colr)
{
   int r2 = r * r;
   int dx, dy,y,x;

   for (x = r; x < 0; --x)
   {
      y = sqrt(r2 - x*x);
      dx = cx - x;
      drawline(dx, cy-y, dx, cy+y, colr);
      dx = cx + x;
      drawline(dx, cy-y, dx, cy+y, colr);
   }
}

void drawcircle(int x0, int y0, int radius, int colr)
{
    int x = radius;
    int y = 0;
    int err = 0;

    while (x >= y)
    {
        plot(x0 + x, y0 + y, colr);
        plot(x0 + y, y0 + x, colr);
        plot(x0 - y, y0 + x, colr);
        plot(x0 - x, y0 + y, colr);
        plot(x0 - x, y0 - y, colr);
        plot(x0 - y, y0 - x, colr);
        plot(x0 + y, y0 - x, colr);
        plot(x0 + x, y0 - y, colr);

        if (err <= 0)
        {
            y += 1;
            err += 2*y + 1;
        }
        if (err > 0)
        {
            x -= 1;
            err -= 2*x + 1;
        }
    }
}

void
circle(
   int xcent,
   int ycent,
   int rad  ,
   int colr )
{
   int x, y, d;
   int dE, dSE;

   x = 0;
   y = rad;

   d = 1 - rad;
   dE = 3;
   dSE = -(rad << 1) + 5;

   plot(xcent-x, ycent-y, colr);
   plot(xcent-x, ycent+y, colr);
   plot(xcent+y, ycent-x, colr);
   plot(xcent-y, ycent+x, colr);

   while (y > x)
    {
       if (d < 0) {
           d += dE;
            dE += 2;
            dSE +=2;
            ++x;
       }
       else {
           d += dSE;
            dE += 2;
            dSE += 4;
            ++x;
            --y;
       }

       plot(xcent+x, ycent-y, colr);
       plot(xcent-x, ycent+y, colr);
       plot(xcent-x, ycent-y, colr);
       plot(xcent+x, ycent+y, colr);
           plot(xcent+y, ycent-x, colr);
       plot(xcent-y, ycent+x, colr);
       plot(xcent-y, ycent-x, colr);
       plot(xcent+y, ycent+x, colr);
   }
}
 

Technipion

Erfahrenes Mitglied
Hallo jkallup,
vor fillcircle(...) fehlt ein void, oder?
Ich glaube das Problem ist deine for-Schleife:
C++:
for (x = r; x < 0; --x)
   {
   ...
Müsste eigentlich
C++:
for (x = r; x > 0; --x)
   {
   ...
heißen.
Apropos Circle-Rendering, es gibt einige ziemlich praktische Algorithmen dafür, die die Symmetrien ausnutzen um Rechenzeit zu sparen. (Z.B. https://en.wikipedia.org/wiki/Midpoint_circle_algorithm)
Vielleicht willst du in einer späteren Version ja den Algo noch etwas verbessern. ;)

Gruß Technipion

EDIT: Ich habe gerade gesehen, dass du für den Kreis bereits die Oktanten-Methode benutzt. Sorry, mein Fehler :oops:.
Für den gefüllten Kreis würde ich dir allerdings auch zu einer schnelleren Methode raten. Gibt einen ganz interessanten Stackoverflow Eintrag dazu: http://stackoverflow.com/a/1237519.
 
Zuletzt bearbeitet:

jkallup

Erfahrenes Mitglied
Danke Techni,

habe dazu auch was interessantes gefunden, angeglchen und ich bin erstaunt, wie schnell sich Kreise
zeichnen lassen. Und man muss dazu sagen, in einer VM, wo das ganze um den Faktor 3x langsamer
abgeht. Das erinnert mich an die AMIGA 500 Zeiten - eine Diskette - 1 Spiel und Sound.
Wenn man das wieder auf einen PC umlagert, da müsste doch noch mehr rauszuholen sein?
Aber das geht ja nicht so einfach, da ja schon der Bootlader und der switch in PM 1 MB braucht.

Jetzt wäre es noch schön, irgendwie Fenster-Modus zu haben....

Code:
fillcircle(int x, int y, int r, int colr)
{
   int ox = x;
   int oy = y;

   for(y = -r; y <= r; y++)
    for(x = -r; x <= r; x++)
    if(x*x+y*y <= r*r)
    plot(ox+x,oy+y,colr);
}