Copy Link
Add to Bookmark

FULLROT - Realtime Full screen (320x200x256) image rotation - Made Easy!

DrWatson's profile picture
Published in 
 · 25 Nov 2023

Please note that I'm a lousy writer. :)

FULLROT is Copyright 1994 by Scott Deming. All Rights Reserved!

The above copyright is probably pretty pointless, because I hearby place this source code in the public domain. Anybody can use it for anything they want. If you feel you can become rich by selling this source code, then be my guest.But realize the guilt you will feel if you ever see me drive by in me old dying Cavalier. :)

This is the second rendition of what I call FULLROT. Basically it's just a simple routine to take a full screen image and rotate it by any given angle. The angles used in fullrot range from 0 to 255, and are stored in a lookup table using fixed point numbers.

This is easily compiled using DJ GPP. I recommend using this compiler for any kind of DOS based development.It provides a very nice DPMI compliant 32 bit DOS extender.Contrary to popular belief, you can distribute and even *SELL* program you write using DJ GPP.It's optimizations, from what I can tell, out do that of any other DOS compiler. Although I haven't seen Watcom 10.0 in action as of yet. DJGPP can be obtained from any simtel mirror including OAK.OAKLAND.EDU in the "/pub/msdos/djgpp" directory. Get the file "readme.1st".

On my 386/40 I'm able to get a full screen frame rate of 8.43 frames per second. On a Vesa or PCI Local bus system I'm sure the frame rate is substantially higher.

In the first release of FULLROT I demonstrated image scaling as well as rotating, but in this release I left the scaling out. There is really no reason other than lazyness.

That's all for now. Enjoy the source code and I hope it helps. If you have any further questions (the source code is really easy to follow, I think) don't hesitate to ask. I can be reached via email. I'll accept any comments, questions, criticism (constructive or not), or complaints. However, I cannot be held responsible for any monitors you fry.

I would have written a decent explanation on how it works, but I'm too lazy, and it's been done a thousand times before. Even once by me, in my last release of Fullrot.

Scott Deming

Pin it


#include <conio.h> 
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <pc.h>
#include <go32.h>
#include <sys/types.h>

#include "gfxmisc.h"

void gfxNewRotate(char *image, char *screen, int angle)
register long rowU, rowV, dU, dV;
unsigned long y, x, u, v, tx, ty;

dU = sinTab[angle];
dV = cosTab[angle];

rowU = startingPointU[angle];
rowV = startingPointV[angle];

while (y--) {
u = rowU;
v = rowV;

x = 320;
while (x--) {
ty = (v >> 10);
tx = (u >> 10);

if (ty<=199 && tx<=319) {
*screen = *(image + yTab[ty] + tx);


rowU -= dU;
rowV += dV;

void main(void)
int angle = 0;
int aDir = 4;
char *scrImage;
char *screen;
long frames = 0L;
u_short convSel = _go32_conventional_mem_selector();
u_short mySel = _go32_my_ds();
unsigned scrOffset;
unsigned imgOffset;
double start_time, end_time;

screen = (char *) malloc(64000);
if (screen == NULL) {
printf("Error allocating screen memory.\n");
memset(screen, 0, 64000);
scrOffset = (unsigned) screen;



scrImage = gfxLoadPCX("test.pcx", NULL);
imgOffset = (unsigned) scrImage;

movedata(mySel, imgOffset, convSel, 0xA0000, 64000);

start_time = clock();
while (!kbhit()) {
gfxNewRotate(scrImage, screen, angle);

movedata(mySel, scrOffset, convSel, 0xA0000, 64000);
memset(screen, 0, 64000);

angle += aDir;
if (angle >= 255) {
angle = 255;
aDir = -4;
} else if (angle < 1) {
angle = 0;
aDir = 4;

end_time = clock();



printf("scrImage: %p, %x (%p, %x)\n", scrImage, (unsigned) scrImage, &scrImage, (unsigned) &scrImage);
printf(" DOS: %x\n", convSel);



printf(" Time: %3.2f\n", (double) ((end_time-start_time) / 1000000));
printf("Frames: %lu\n", frames);
printf(" FPS: %3.2f\n", (double) (double) frames / (double) ((end_time-start_time) / 1000000));


#include <conio.h> 
#include <dos.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <pc.h>

#include "gfxmisc.h"

long *cosTab;
long *sinTab;
long *scaleTab;
long *yTab;
long startingPointU[256];
long startingPointV[256];

gfxSetMode(int mode)
union REGS r; = mode;

int86(0x10, &r, &r);

outportb(0x3C2, 0xE3);

gfxSetPalette(unsigned char *palette)
int i;

outportb(0x03c8, 0);

i = 256;
while (i--) {
outportb(0x03c9, *palette++);
outportb(0x03c9, *palette++);
outportb(0x03c9, *palette++);

char *
gfxLoadPCX(char *pcxFName, char *image)
FILE *fp;
int size, i;
unsigned char PCX_byte, RLE_byte;
unsigned char *buf_ptr;
unsigned char *end_of_buf;
unsigned char palette[768];

fp = fopen(pcxFName, "rb");

if (fp == NULL) {
return NULL;
} else {
fseek(fp, 8, SEEK_SET);
size = 64000;

image = (char *) malloc(size);
buf_ptr = image;
end_of_buf = buf_ptr + size;

fseek(fp, -768, SEEK_END);
fread(palette, 1, 768, fp);
for (i=0; i < 768; i++) {
palette[i] = palette[i] >> 2;

fseek(fp, 128, SEEK_SET);

while (buf_ptr < end_of_buf) {
fread(&PCX_byte, 1, 1, fp);

if (PCX_byte < 192) {
*buf_ptr++ = PCX_byte;
} else {
PCX_byte = PCX_byte & 0x3F;
fread(&RLE_byte, 1, 1, fp);
memset(buf_ptr, RLE_byte, PCX_byte);
buf_ptr += PCX_byte;



return image;


FILE *fp;
int i;
unsigned long dU, dV;

sinTab = (long *) malloc(256 * 4);
cosTab = (long *) malloc(256 * 4);
scaleTab = (long *) malloc(128 * 4);
yTab = (long *) malloc(200 * 4);

fp = fopen("sintab.dat", "rb");
fread(sinTab, 4, 256, fp);
fread(cosTab, 4, 256, fp);

for (i=0; i < 200; i++) {
yTab[i] = i * 320;

for (i=0; i < 128; i++) {
scaleTab[i] = (i << 4);

for (i=0; i<255; i++) {
dU = sinTab[i];
dV = cosTab[i];
startingPointU[i] = ((-160 * dV) + (100 * dU)) + (160 << 10);
startingPointV[i] = ((-160 * dU) - (100 * dV)) + (100 << 10);



#ifndef _gfxmisc_h_ 
#define _gfxmisc_h_

void gfxSetMode(int mode);
void gfxExtendMode(void);

void gfxSetPalette(unsigned char *palette);
char *gfxLoadPCX(char *pcxFName, char *image);

extern long *cosTab;
extern long *sinTab;
extern long *scaleTab;
extern long *yTab;
extern long startingPointU[256];
extern long startingPointV[256];

void gfxInitTables(void);
void gfxCleanup(void);


← previous
next →
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.