This is an archived, read-only copy of the United-TI subforum , including posts and topic from May 2003 to April 2012. If you would like to discuss any of the topics in this forum, you can visit Cemetech's z80 & ez80 Assembly subforum. Some of these topics may also be directly-linked to active Cemetech topics. If you are a Cemetech member with a linked United-TI account, you can link United-TI topics here with your current Cemetech topics.

This forum is locked: you cannot post, reply to, or edit topics. Z80 & 68k Assembly => z80 & ez80 Assembly
Author Message
bwang


Member


Joined: 15 Mar 2009
Posts: 128

Posted: 16 Apr 2010 07:35:08 pm    Post subject:

So I recently added variable height walls to my Nspire raycaster (Omnimaga thread here) and as a result, I have to make the Z-Buffer 2D so I can clip sprites properly.
Unfortunately, the following code crashes (in case you can't figure out yourself, the problem variable is the 320x240 float array zbuf)

Code:

#include <os.h>
#include "utils.h"
#include "raymath.h"
#include "sort.h"
#include "sprites.h"
#include "config.h"

asm(".string \"PRG\"\n");

int main(void)
{
  *(volatile unsigned*) 0x900B0000 = 0x00000002;
  *(volatile unsigned*) 0x900B000C = 4;

  struct Sprite sprite[numSprites] =
  {
    {12, 12},
    {13, 12},
    {14, 12},
    {15, 12},
  };

  int map[mapWidth][mapHeight]=
  {
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,2,2,2,2,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,2,2,2,2,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  };

  float height[mapWidth][mapHeight]=
  {
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
  {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,2,1,2,1,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1,0},
  {0,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,2,1,2,1,2,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,1,0.8,0.6,0.4,0.2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  };

  char* scrbuf = (char*) malloc(SCREEN_BYTES_SIZE);
  int tex[texW][texH]; 
  int u, v;
  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      tex[u][v] = 6;
    }
  }

  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      if (u % 4 == 3 || (v % 8 == 2 && ((u / 4) % 2) == 0) ||
         (v % 8 == 6 && ((u / 4) % 2) == 1)) {
        tex[u][v] = 15;
      }
    }
  }

  int tex2[texW][texH];
  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      tex2[u][v] = (u ^ v) / 2;
    }
  }

  int texSpr[texW][texH];
  for (u = 0; u < texW; u++) {
    for (v = 0; v < texH; v++) {
      int dsq = (u - 16) * (u - 16) + (v - 16) * (v - 16);
      if (dsq > 196 && dsq < 256) {
        texSpr[u][v] = 0;
      } else if (dsq < 196 && dsq > 64) {
        texSpr[u][v] = 8;
      } else if (dsq < 64 && dsq > 49) {
        texSpr[u][v] = 0;
      } else {
        texSpr[u][v] = -1;
      }
    }
  }
 
  float posX = 12, posY = 12; 
  float dirX = -1, dirY = 0;
  float planeX = 0, planeY = 0.66;
  int w = 320, h = 240;
  int res = 1;
  int x, y;
  float zbuf[w][h];
  int spriteOrder[numSprites];
  float spriteDistance[numSprites];
  int redraw;
  float heye = 0.5;

  while (!isKeyPressed(KEY_NSPIRE_ESC)) {
    redraw = 0;
    if (isKeyPressed(KEY_NSPIRE_LEFT)) {
      float oldDirX = dirX;
      dirX = dirX * cos1 - dirY * sin1;
      dirY = oldDirX * sin1 + dirY * cos1;
      float oldPlaneX = planeX;
      planeX = planeX * cos1 - planeY * sin1;
      planeY = oldPlaneX * sin1 + planeY * cos1;
      redraw = 1;
    }
    if (isKeyPressed(KEY_NSPIRE_RIGHT)) {
      float oldDirX = dirX;
      dirX = dirX * cos1 + dirY * sin1;
      dirY = -oldDirX * sin1 + dirY * cos1;
      float oldPlaneX = planeX;
      planeX = planeX * cos1 + planeY * sin1;
      planeY = -oldPlaneX * sin1 + planeY * cos1;
      redraw = 1;
    }
    if (isKeyPressed(KEY_NSPIRE_UP)) {
      if (map[(int) (posX + stpsize * dirX)][(int) (posY)] == 0)
        posX += stpsize * dirX;
      if (map[(int) (posX)][(int) (posY + stpsize * dirY)] == 0)
        posY += stpsize * dirY;
      if (posX < 0) posX = 0;
      if (posX > 24) posX = 24;
      if (posY < 0) posY = 0;
      if (posY > 24) posY = 24;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_DOWN)) {
      if (map[(int) (posX - stpsize * dirX)][(int) (posY)] == 0)
        posX -= stpsize * dirX;
      if (map[(int) (posX)][(int) (posY - stpsize * dirY)] == 0)
        posY -= stpsize * dirY;
      if (posX < 0) posX = 0;
      if (posX > 24) posX = 24;
      if (posY < 0) posY = 0;
      if (posY > 24) posY = 24;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_MINUS)) {
      heye -= 0.1;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_PLUS)) {
      heye += 0.1;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_DIVIDE)) {
      w = (int) (w * 1.1);
      if (w > 320) w = 320;
      h = (int) (h * 1.1);
      if (h > 240) h = 240;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_MULTIPLY)) {
      w = (int) (w * 0.9);
      h = (int) (h * 0.9);
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_H)) {
      res = 1;
      redraw = true;
    }
    if (isKeyPressed(KEY_NSPIRE_L)) {
      res = 2;
      redraw = true;
    }
    if (redraw) {
      memset(scrbuf, 0x00, SCREEN_BYTES_SIZE);
      for(x = 0; x < w; x += res) {
        float rayPosX = posX;
        float rayPosY = posY;
        int mapX = (int) rayPosX;
        int mapY = (int) rayPosY;
        float cameraX = 2 * x / (float) (w) - 1;
        float raydx = dirX + planeX * cameraX;
        float raydy = dirY + planeY * cameraX;       
        float sideDistX;
        float sideDistY;
        float raydx2 = raydx * raydx, raydy2 = raydy * raydy;
        float deltaDistX = sqrt(1 + raydy2 / raydx2);
        float deltaDistY = sqrt(1 + raydx2 / raydy2);
        float perpWallDist;
           
        int stepX;
        int stepY;
        int side;
        float curH = 0;
        int scrtop = h - 1;
     
        if (raydx < 0) {
          stepX = -1;
          sideDistX = (rayPosX - mapX) * deltaDistX;
        } else {
          stepX = 1;
          sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX;
        }
        if (raydy < 0) {
          stepY = -1;
          sideDistY = (rayPosY - mapY) * deltaDistY;
        } else {
          stepY = 1;
          sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY;
        }
        int first = true;
        while (mapX > 0 && mapX < mapWidth && mapY > 0 && mapY < mapHeight ) {
          if (sideDistX < sideDistY) {
            sideDistX += deltaDistX;
            mapX += stepX;
            side = 0;
          } else {
            sideDistY += deltaDistY;
            mapY += stepY;
            side = 1;
          }
          if (!map[mapX][mapY]) continue;
          if (scrtop < 0) break;
          float hwall = height[mapX][mapY];

          if (side == 0) {
            perpWallDist = fabs((mapX - rayPosX + (1 - stepX) / 2) / raydx);
          } else {
            perpWallDist = fabs((mapY - rayPosY + (1 - stepY) / 2) / raydy);
          }

          int nextX;
          int nextY;
          int nextSide;
          if (sideDistX < sideDistY) {
            nextX = mapX + stepX;
            nextSide = 0;
          } else {
            nextY = mapY + stepY;
            nextSide = 1;
          }
           
          float nextD;
          if (nextSide == 0) {
            nextD = fabs((nextX - rayPosX + (1 - stepX) / 2) / raydx);
          } else {
            nextD = fabs((nextY - rayPosY + (1 - stepY) / 2) / raydy);
          }

          int lo = (int) (h * (0.5 - heye / perpWallDist));
          int lw = (int) (h * hwall / perpWallDist);
         
          int nlo = (int) (h * (0.5 - heye / nextD));
          int nlw = (int) (h * hwall / nextD);

          int fstart = h - nlo - nlw;
          int fend = h - lo - lw - 1;
          int wstart = h - lo - lw;
          int wend = h - lo - 1;

          float wallX;
          if (side == 1) {
            wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / raydy) * raydx;
          } else {       
            wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / raydx) * raydy;
          }
          wallX -= (int) wallX;
          int texX = (int) (wallX * (float) (texW));
          if(side == 0 && raydx > 0) texX = texW - texX - 1;
          if(side == 1 && raydy < 0) texX = texW - texX - 1;
         
          //floor loop
          int y;
          int sfstart = max(fstart, 0);
          int sfend = min(fend, scrtop);
          for (y = sfstart; y <= sfend; y++) {
            setPixelBuf(scrbuf, x, y, 8);
            //very large value since sprites always cover floors
            zbuf[x][y] = 1000;
            if (res == 2 && x < w - 1) {
              setPixelBuf(scrbuf, x + 1, y, 8);
              zbuf[x + 1][y] = 1000;
            }
          }
          //wall loop
          float texY = 0;
          float texD = ((float)texH) / ((float)lw);
          int swstart = max(wstart, 0);
          if (swstart == 0) texY += (-wstart) * texD;
          int swend = min(wend, scrtop);
          for (y = swstart; y <= swend; y++) {
            int color = tex[(int) texY][texX];
            if (side == 1) color = color / 2;
            setPixelBuf(scrbuf, x, y, color);
            //set the zbuffer
            zbuf[x][y] = perpWallDist;
            if (res == 2 && x < w - 1) {
              setPixelBuf(scrbuf, x + 1, y, color);
              zbuf[x + 1][y] = perpWallDist;
            }
            texY += texD;
          }
          int oldtop = scrtop;
          scrtop = fstart - 1;
          if (scrtop > wstart - 1) scrtop = wstart;
          if (scrtop > oldtop) scrtop = oldtop;
          curH = hwall;
        }
      }
      int i;
      for(i = 0; i < numSprites; i++)
      {
        spriteOrder[i] = i;
        spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x)
                           + (posY - sprite[i].y) * (posY - sprite[i].y));
      }
      combSort(spriteOrder, spriteDistance, numSprites);
       
      for(i = 0; i < numSprites; i++)
      {
        float spriteX = sprite[spriteOrder[i]].x - posX;
        float spriteY = sprite[spriteOrder[i]].y - posY;
           
        float invDet = 1.0 / (planeX * dirY - dirX * planeY);
       
        float transformX = invDet * (dirY * spriteX - dirX * spriteY);
        float transformY = invDet * (-planeY * spriteX + planeX * spriteY);       
             
        int spriteScreenX = (int) ((w / 2) * (1 + transformX / transformY));
       
        #define uDiv 1
        #define vDiv 1
        #define vMove 0.0

        int vMoveScreen = (int) (vMove / transformY);
       
        int spriteHeight = abs((int) (h / transformY)) / vDiv;
        int lo = (int) (h * (0.5 - heye / transformY));
        int drawStartY = h - lo - spriteHeight;
        int drawEndY = h - lo;
        if (drawEndY > h - 1) drawEndY = h - 1;
        int texOff = 0;
        if (drawStartY < 0) {
          texOff = -drawStartY;
          drawStartY = 0;
        }

        int spriteWidth = abs((int) (h / (transformY))) / uDiv;
        int drawStartX = -spriteWidth / 2 + spriteScreenX;
        if(drawStartX < 0) drawStartX = 0;
        int drawEndX = spriteWidth / 2 + spriteScreenX;
        if(drawEndX >= w) drawEndX = w - 1;
       
        int stripe;
        float texD = ((float)texH) / ((float)spriteHeight);
        texOff *= texD;
        for(stripe = drawStartX; stripe < drawEndX; stripe += res)
        {
          int texX = (int) (256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texW / spriteWidth) / 256;
          float texY = texOff;
          if(transformY > 0 && stripe > 0 && stripe < w) {
            for(y = drawStartY; y < drawEndY; y++)
            {
              if (y >= 0 && y <= h - 1) {
                if (transformY >= zbuf[stripe][y]) continue;
                int color = texSpr[(int)texY][texX];
                if (color != -1) {
                  setPixelBuf(scrbuf, stripe, y, color);
                  if (res == 2 && stripe < w - 1) {
                    setPixelBuf(scrbuf, stripe + 1, y, color);
                  }
                }
              }
              texY += texD;
            }
          }
        }
      }
      refresh(scrbuf);
    }
  }

  *(volatile unsigned*) 0x900B0000 = 0x00141002;
  *(volatile unsigned*) 0x900B000C = 4;
  return 0;
}

Anyone have any idea whats wrong?
Why don't these codeboxes have scroll bars on them?


Last edited by Guest on 16 Apr 2010 07:38:22 pm; edited 1 time in total
Back to top
Lionel Debroux


Member


Joined: 01 Aug 2009
Posts: 170

Posted: 17 Apr 2010 02:41:30 am    Post subject:

If you allocate zbuf with malloc, does the program still crash ?
(In other words, is there enough space on the stack for that auto array weighing 320*240*sizeof(float) bytes, i.e. four times the size of the "scrbuf" variable that you're malloc'ing, in addition to all the rest of your variables ?)


Several general remarks on the code:
* if you don't modify the "map" and "height" variables, you should declare them "const". This way, when static/global variables are supported, they can be put in the .rodata section;
* mixing variable declarations and code, a (mis-)feature of recent C dialects, tends to lower the readability of the code, and therefore its maintainability, code; Wink
* instead of unconditionally resetting the value of 0x900B0000 at the end of your program, you should read it at the very beginning, and restore it at that value at the end of the program.


Hope this helps Smile
Back to top
bwang


Member


Joined: 15 Mar 2009
Posts: 128

Posted: 17 Apr 2010 08:27:49 pm    Post subject:

If I allocate zbuf with malloc, how would I put floats in it?
Back to top
Lionel Debroux


Member


Joined: 01 Aug 2009
Posts: 170

Posted: 18 Apr 2010 02:01:26 am    Post subject:

I'd make zbuf a pointer to a float (float *), and replace "zbuf[x][y]" by "ZBUF(x,y)", where ZBUF is a macro that reads along the lines of

Code:
#define ZBUF(x,y) (zbuf[x + y * SCREEN_WIDTH_BYTES])

(SCREEN_WIDTH_BYTES being ultimately equal to 160, if I'm not mistaken, since there are 320 pixels on a line and a pixel is 4 bits)

In general, using macros to access your most important variables such as this one abstracts the way they're implemented, and thus makes it much easier, during the optimization phase, to switch between e.g. a 2D array and a 1D array (in my solution, zbuf is a 1D array).
Back to top
bwang


Member


Joined: 15 Mar 2009
Posts: 128

Posted: 18 Apr 2010 10:05:53 pm    Post subject:

It no longer crashes, but it doesn't quite work either.
Using the macro you posted, would, for example, ZBUF(x, y) = 1000 set the value in ZBUF at (x, y) to 1000, or would it do something strange?
Back to top
tr1p1ea


Elite


Joined: 03 Aug 2003
Posts: 870

Posted: 19 Apr 2010 07:18:13 am    Post subject:

I guess it would depend on what you set SCREEN_WIDTH_BYTES to.

Wouldnt the bpp be irrelevant as far as your z-buffer is concerned?

EDIT - Also isnt this thread a good indication that an Nspire programming section is needed? (Or even just update the title for the Z80 & 68K section to include Nspire flavours?).


Last edited by Guest on 19 Apr 2010 07:38:36 am; edited 1 time in total
Back to top
bwang


Member


Joined: 15 Mar 2009
Posts: 128

Posted: 19 Apr 2010 02:41:07 pm    Post subject:

Hmmm...why is it SCREEN_WIDTH_BYTES and not SCREEN_WIDTH? Don't I want one float per pixel, not one per byte the screen takes up?
Back to top
bwang


Member


Joined: 15 Mar 2009
Posts: 128

Posted: 19 Apr 2010 05:11:48 pm    Post subject:

Never mind, problem solved Smile
Back to top
tr1p1ea


Elite


Joined: 03 Aug 2003
Posts: 870

Posted: 19 Apr 2010 06:48:49 pm    Post subject:

Excellent, any chance of a screenie? Smile.

Also have you put any thought into using fixed-point math?
Back to top
bwang


Member


Joined: 15 Mar 2009
Posts: 128

Posted: 20 Apr 2010 06:05:33 pm    Post subject:

Screenies:


There are demos at Omnimaga in case anyone is interested.

I have thought many times about fixed-point math. I wonder if the resulting speedup would be worth the trouble?
Back to top
tr1p1ea


Elite


Joined: 03 Aug 2003
Posts: 870

Posted: 20 Apr 2010 06:14:43 pm    Post subject:

Oh yeah thats the stuff! Looking fantastic mate :>.

You should notice a decent performance increase when using fixed-point math, but then again if you're already running at a good enough speed it might be something to place on the back-burner.
Back to top
Display posts from previous:   
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
    »
» View previous topic :: View next topic  
Page 1 of 1 » All times are UTC - 5 Hours

 

Advertisement