-
-
Save root42/8e147c5ec2427f42f2ac50a5aba52317 to your computer and use it in GitHub Desktop.
Let's Code: MS-DOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <alloc.h> | |
#include <conio.h> | |
#include <dos.h> | |
#include <stdio.h> | |
#include <string.h> | |
#define MAX_SCORE 5 | |
#define PADDLE_SPEED 8 | |
#define NUM_COLORS 256 | |
#define VIDEO_INT 0x10 | |
#define SET_MODE 0x00 | |
#define SET_CURSOR 0x02 | |
#define PRINT_CHAR 0x09 | |
#define PRINT_STRING 0x13 | |
#define TEXT_MODE 0x03 | |
#define VGA_256_COLOR_MODE 0x13 | |
#define SCREEN_HEIGHT 200 | |
#define SCREEN_WIDTH 320 | |
#define PALETTE_READ 0x3C7 | |
#define PALETTE_WRITE 0x3C8 | |
#define PALETTE_DATA 0x3C9 | |
#define INPUT_STATUS 0x3DA | |
#define VRETRACE_BIT 0x08 | |
typedef unsigned char byte; | |
typedef struct { | |
byte color; | |
int x, y; | |
int dx, dy; | |
int width; | |
int height; | |
int score; | |
byte *backup; | |
} player; | |
byte far *VGA=(byte far *)0xA0000000L; | |
#define SETPIX(x,y,c) *(VGA+(x)+(y)*SCREEN_WIDTH)=c | |
#define GETPIX(x,y) *(VGA+(x)+(y)*SCREEN_WIDTH) | |
#define MAX(x,y) ((x) > (y) ? (x) : (y)) | |
#define MIN(x,y) ((x) < (y) ? (x) : (y)) | |
void wait_for_retrace() | |
{ | |
while( inp( INPUT_STATUS ) & VRETRACE_BIT ); | |
while( ! (inp( INPUT_STATUS ) & VRETRACE_BIT) ); | |
} | |
void set_mode(byte mode) | |
{ | |
union REGS regs; | |
regs.h.ah = SET_MODE; | |
regs.h.al = mode; | |
int86( VIDEO_INT, ®s, ®s ); | |
} | |
void print( int x, int y, byte color, const char far *s ) | |
{ | |
struct REGPACK regs; | |
#if 1 | |
int i; | |
for( i = 0; i < strlen(s); ++i ) { | |
regs.r_ax = SET_CURSOR << 8 | 0x0; | |
regs.r_bx = 0x0; | |
regs.r_dx = y << 8 | x + i; | |
intr( VIDEO_INT, ®s); | |
regs.r_ax = PRINT_CHAR << 8 | s[ i ]; | |
regs.r_bx = 0x0 << 8 | color; | |
regs.r_cx = 1; | |
intr( VIDEO_INT, ®s); | |
} | |
#else | |
regs.r_ax = PRINT_STRING << 8 | 0x0; | |
regs.r_bx = 0x0 << 8 | color; | |
regs.r_cx = strlen( s ); /* "hello world\0" */ | |
regs.r_dx = y << 8 | x; | |
regs.r_es = FP_SEG( s ); | |
regs.r_bp = FP_OFF( s ); | |
intr( VIDEO_INT, ®s); | |
#endif | |
} | |
void draw_background() | |
{ | |
int x, y; | |
for( y = 0; y < SCREEN_HEIGHT; ++y ) { | |
for( x = 0; x < SCREEN_WIDTH; ++x ) { | |
SETPIX( x, y, y + 16 ); | |
} | |
} | |
} | |
byte *get_sky_palette() | |
{ | |
byte *pal; | |
int i; | |
pal = malloc( NUM_COLORS * 3 ); /* RGB */ | |
outp( PALETTE_READ, 0 ); | |
for( i = 0; i < 16; ++i ) { | |
pal[ i*3 + 0 ] = inp( PALETTE_DATA ); | |
pal[ i*3 + 1 ] = inp( PALETTE_DATA ); | |
pal[ i*3 + 2 ] = inp( PALETTE_DATA ); | |
} | |
for( i = 16; i < 116; ++i ) { | |
pal[ i*3 + 0 ] = MIN( 63, i ); /* RED */ | |
pal[ i*3 + 1 ] = MIN( 63, i ); /* GREEN */ | |
pal[ i*3 + 2 ] = 63; /* BLUE */ | |
} | |
for( i = 116; i < 216; ++i ) { | |
pal[ i*3 + 0 ] = 5; /* RED */ | |
pal[ i*3 + 1 ] = (i - 100) / 2; /* GREEN */ | |
pal[ i*3 + 2 ] = 5; /* BLUE */ | |
} | |
for( i = 217; i < 256; ++i ) { | |
pal[ i*3 + 0 ] = 63; /* RED */ | |
pal[ i*3 + 1 ] = 10; /* GREEN */ | |
pal[ i*3 + 2 ] = 10; /* BLUE */ | |
} | |
return pal; | |
} | |
void set_black_palette() | |
{ | |
int i; | |
outp( PALETTE_WRITE, 0 ); | |
for( i = 0; i < NUM_COLORS * 3; ++i ) { | |
outp( PALETTE_DATA, 0 ); | |
} | |
} | |
void set_palette(byte *palette) | |
{ | |
int i; | |
outp( PALETTE_WRITE, 0 ); | |
for( i = 0; i < NUM_COLORS * 3; ++i ) { | |
outp( PALETTE_DATA, palette[ i ] ); | |
} | |
} | |
void blit2vga( byte far *s, int x, int y, int w, int h ) | |
{ | |
int i; | |
byte far *src = s; | |
byte far *dst = VGA + x + y * SCREEN_WIDTH; | |
for( i = y; i < y + h; ++i ) { | |
movedata( | |
FP_SEG( src ), | |
FP_OFF( src ), | |
FP_SEG( dst ), | |
FP_OFF( dst ), | |
w | |
); | |
src += w; | |
dst += SCREEN_WIDTH; | |
} | |
} | |
void blit2mem( byte far *d, int x, int y, int w, int h ) | |
{ | |
int i; | |
byte far *src = VGA + x + y * SCREEN_WIDTH; | |
byte far *dst = d; | |
for( i = y; i < y + h; ++i ) { | |
movedata( | |
FP_SEG( src ), | |
FP_OFF( src ), | |
FP_SEG( dst ), | |
FP_OFF( dst ), | |
w | |
); | |
src += SCREEN_WIDTH; | |
dst += w; | |
} | |
} | |
void draw_rectangle( int x, int y, int w, int h, byte c ) | |
{ | |
int i, j; | |
for( j = y; j < y + h; ++j ) { | |
for( i = x; i < x + w; ++i ) { | |
byte far *dst = VGA + i + j * SCREEN_WIDTH; | |
*dst = c; | |
} | |
} | |
} | |
void store_player( player *p ) | |
{ | |
blit2mem( p->backup, p->x, p->y, p->width, p->height ); | |
} | |
void restore_player( player *p ) | |
{ | |
blit2vga( p->backup, p->x, p->y, p->width, p->height ); | |
} | |
void draw_player( player *p ) | |
{ | |
draw_rectangle( p->x, p->y, p->width, p->height, p->color ); | |
} | |
int does_ball_hit_player( player *b, player *p ) | |
{ | |
if( b->y > p->y && | |
b->y - b->height < p->y + p->height ) | |
{ | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
int handle_ball( player *b, player *p1, player *p2 ) | |
{ | |
int retval = 0; | |
b->x += b->dx; | |
b->y += b->dy; | |
if( b->x < 5 + p1->width ) { | |
if( does_ball_hit_player( b, p1 ) ) { | |
b->dx = - b->dx; | |
} else { | |
p2->score++; | |
b->x = SCREEN_WIDTH / 2; | |
b->y = SCREEN_HEIGHT / 2; | |
b->dx = 1; | |
b->dy = 1; | |
retval = 2; | |
} | |
} else if( b->x + b->width > SCREEN_WIDTH - 5 - p2->width ) { | |
if( does_ball_hit_player( b, p2 ) ) { | |
b->dx = - b->dx; | |
} else { | |
p1->score++; | |
b->x = SCREEN_WIDTH / 2; | |
b->y = SCREEN_HEIGHT / 2; | |
b->dx = -1; | |
b->dy = 1; | |
retval = 1; | |
} | |
} | |
if( b->y < 10 || b->y > SCREEN_HEIGHT - 10 ) { | |
b->dy = - b->dy; | |
} | |
return retval; | |
} | |
void fade_in_palette( byte *pal ) | |
{ | |
int i, j; | |
byte pal2[ 3 * NUM_COLORS ]; | |
memset( pal2, 0, 3 * NUM_COLORS ); | |
for( i = 0; i < 63; ++i ) { | |
wait_for_retrace(); | |
outp( PALETTE_WRITE, 0 ); | |
for( j = 0; j < NUM_COLORS * 3; ++j ) { | |
if( pal2[ j ] < pal[ j ] ) { | |
pal2[ j ]++; | |
} | |
outp( PALETTE_DATA, pal2[ j ] ); | |
} | |
} | |
} | |
void fade_out_palette( byte *pal ) | |
{ | |
int i, j; | |
byte pal2[ 3 * NUM_COLORS ]; | |
memcpy( pal2, pal, 3 * NUM_COLORS ); | |
for( i = 0; i < 63; ++i ) { | |
wait_for_retrace(); | |
outp( PALETTE_WRITE, 0 ); | |
for( j = 0; j < NUM_COLORS * 3; ++j ) { | |
if( pal2[ j ] > 0 ) { | |
pal2[ j ]--; | |
} | |
outp( PALETTE_DATA, pal2[ j ] ); | |
} | |
} | |
} | |
int handle_game( int new_game ) | |
{ | |
int kc = 0; | |
/* 0: game still going | |
* 1: player 1 has won | |
* 2: player 2 ha won | |
* 3: ESC pressed | |
*/ | |
static player p1, p2, ball; | |
const char *s = "Player 1 %03d %03d Player 2"; | |
static char far *out = NULL; | |
int update_score = 0; | |
if( out == NULL ) { | |
out = malloc( 256 ); | |
} | |
if( new_game ) { | |
p1.color = 255; | |
p1.height = 30; | |
p1.width = 5; | |
p1.x = 5; | |
p1.y = SCREEN_HEIGHT / 2 - p1.height / 2; | |
p1.score = 0; | |
p1.backup = malloc( p1.height * p1.width ); | |
memset( p1.backup, 0, p1.height * p1.width ); | |
p2.color = 255; | |
p2.height = 30; | |
p2.width = 5; | |
p2.x = SCREEN_WIDTH - p2.width - 5; | |
p2.y = SCREEN_HEIGHT / 2 - p2.height / 2; | |
p2.score = 0; | |
p2.backup = malloc( p2.height * p2.width ); | |
ball.color = 255; | |
ball.height = 5; | |
ball.width = 5; | |
ball.x = SCREEN_WIDTH / 2; | |
ball.y = SCREEN_HEIGHT / 2; | |
ball.dx = 1; | |
ball.dy = 1; | |
ball.score = 0; | |
ball.backup = malloc( ball.height * ball.width ); | |
sprintf( out, s, p1.score, p2.score ); | |
print( 0, 0, 0xf, out ); | |
store_player( &p1 ); | |
store_player( &p2 ); | |
store_player( &ball ); | |
} | |
/* restore whatever was beneath player sprites */ | |
restore_player( &p1 ); | |
restore_player( &p2 ); | |
restore_player( &ball ); | |
if(kbhit()) { | |
kc = getch(); | |
if( kc == (char)0 ) { | |
kc = getch() << 8; | |
} else if( kc == (char)0x1b ) { | |
return 3; | |
} | |
/* special key handling */ | |
switch( kc ) | |
{ | |
case 'w': /* up arrow */ | |
p2.y -= PADDLE_SPEED; | |
if( p2.y < 0 ) { | |
p2.y = 0; | |
} | |
break; | |
case 's': /* down arrow */ | |
p2.y += PADDLE_SPEED; | |
if( p2.y + p2.height > SCREEN_HEIGHT ) { | |
p2.y = SCREEN_HEIGHT - p2.height; | |
} | |
break; | |
case 0x4800: /* up arrow */ | |
p1.y -= PADDLE_SPEED; | |
if( p1.y < 0 ) { | |
p1.y = 0; | |
} | |
break; | |
case 0x5000: /* down arrow */ | |
p1.y += PADDLE_SPEED; | |
if( p1.y + p1.height > SCREEN_HEIGHT ) { | |
p1.y = SCREEN_HEIGHT - p1.height; | |
} | |
break; | |
default: /* other special keys */ | |
break; | |
} | |
} | |
update_score = handle_ball( &ball, &p1, &p2 ); | |
if( update_score ) { | |
sprintf( out, s, p1.score, p2.score ); | |
print( 0, 0, 0xf, out ); | |
} | |
store_player( &p1 ); | |
store_player( &p2 ); | |
store_player( &ball ); | |
draw_player( &p1 ); | |
draw_player( &p2 ); | |
draw_player( &ball ); | |
if( p1.score >= MAX_SCORE ) { | |
return 1; | |
} else if( p2.score >= MAX_SCORE ) { | |
return 2; | |
} else { | |
return 0; | |
} | |
} | |
void handle_game_over( int winner ) | |
{ | |
int kc = 0; | |
const char end1[] = "Congratulations Player %d!"; | |
const char end2[] = "<Press SPACE to EXIT game>"; | |
static char far *out = NULL; | |
const byte col = 11; | |
byte i = 0; | |
int di = 1; | |
if( winner < 3 ) { | |
sprintf( out, end1, winner ); | |
print( 7, 10, col, out ); | |
} | |
sprintf( out, end2 ); | |
print( 7, 11, col, out ); | |
while( 1 ) { | |
if( kbhit() ) { | |
kc = getch(); | |
if( kc == ' ' ) { | |
break; | |
} | |
} | |
wait_for_retrace(); | |
outp( PALETTE_WRITE, col ); | |
outp( PALETTE_DATA, i ); | |
outp( PALETTE_DATA, i ); | |
outp( PALETTE_DATA, i ); | |
i += di; | |
if( i < 1 || i > 62 ) { | |
di = -di; | |
} | |
} | |
} | |
int main() | |
{ | |
byte *pal; | |
int winner = 0; | |
set_mode( VGA_256_COLOR_MODE ); | |
pal = get_sky_palette(); | |
set_black_palette(); | |
clrscr(); | |
draw_background(); | |
fade_in_palette( pal ); | |
handle_game( 1 ); | |
while( winner == 0 ) { | |
/* breaks if one player won, or ESC pressed */ | |
winner = handle_game( 0 ); | |
wait_for_retrace(); | |
} | |
handle_game_over( winner ); | |
fade_out_palette( pal ); | |
set_mode( TEXT_MODE ); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment