582 lines
14 KiB
C
582 lines
14 KiB
C
/*********************************************
|
|
* Description - Seraphim, Baremetal x86
|
|
* topdown shooter
|
|
* Author - Vilyaem
|
|
* Date - May 14 2024
|
|
* *******************************************/
|
|
|
|
__asm__ (".code16gcc\n"
|
|
"call dosmain\n"
|
|
"mov $0x4C,%ah\n"
|
|
"int $0x21\n");
|
|
|
|
/*----------PREPROCESSOR----------*/
|
|
|
|
#define COM1_PORT 0x3F8
|
|
#define VGA_RESX 320
|
|
#define VGA_RESY 200
|
|
#define VGA_MEM 0x9E6E0 /*This standards-wise should be 0xA0000*/
|
|
#define VGA_END VGA_MEM+VGA_RESX*VGA_RESY
|
|
#define FB_MEM VGA_END+32 /*Framebuffer location*/
|
|
#define FB_END FB_MEM+VGA_RESX*VGA_RESY
|
|
#define FG_COL 15
|
|
#define BG_COL 9
|
|
#define DMNS 4
|
|
#define BULLETS 64
|
|
#define SERAPH_MVSPEED 4
|
|
#define DMN_MVSPEED 3
|
|
#define KBDDVORAK 1
|
|
#define SONGTICKRT 3
|
|
|
|
/*monochromatic graphics*/
|
|
#include "seraph.xbm"
|
|
#include "dmn.xbm"
|
|
|
|
/*the song*/
|
|
#include "caesar.spk"
|
|
|
|
/*Typedef sucks*/
|
|
typedef char i8;
|
|
typedef int i16;
|
|
typedef unsigned char u8;
|
|
typedef unsigned int u16;
|
|
typedef void vo;
|
|
|
|
/*----------DATA STRUCTURES----------*/
|
|
|
|
typedef struct{
|
|
i16 x;
|
|
i16 y;
|
|
}Entity;
|
|
|
|
/*----------GLOBALS----------*/
|
|
|
|
i16 i,j;
|
|
Entity dmns[DMNS];
|
|
Entity fballs[BULLETS];
|
|
i16 fballptr = 0;
|
|
i16 sx = VGA_RESX/2-80;
|
|
i16 sy = VGA_RESY-100;
|
|
u8 frame = 0;
|
|
i16 songtick = 0;
|
|
|
|
/*keyboard scancode tables*/
|
|
#ifdef KBDQWERTY
|
|
u8 kbdmap[128]={0,27,'1','2','3','4','5','6','7',
|
|
'8','9','0','-','=','\b','\t','q','w','e','r','t','y','u','i','o','p','[',']',
|
|
'\n',0,'a','s','d','f','g','h','j','k','l',';','\'','`',0,'\\','z','x',
|
|
'c','v','b','n','m',',','.','/',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,0,0,0};
|
|
#endif
|
|
#ifdef KBDDVORAK
|
|
u8 kbdmap[128]={0,27,'1','2','3','4','5','6','7',
|
|
'8','9','0','[',']','\b','\t','\'',',','.','p','y','f','g','c','r','l','/','=',
|
|
'\n',0,'a','o','e','u','i','d','h','t','n','s','-','`',0,'\\',';','q',
|
|
'j','k','x','b','m','w','v','z',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,0,0,0};
|
|
#endif
|
|
|
|
/*----------FUNCTIONS----------*/
|
|
|
|
/*********************************************
|
|
* Description - Outb, write byte to IO port
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo OutB(u8 value, i16 port){
|
|
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Inb, read a byte from IO port
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline u8 InB(i16 port){
|
|
u8 value;
|
|
__asm__ volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
|
|
return value;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Put string text
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo Puts(i8 *string){
|
|
__asm__ volatile ("mov $0xe0,%%bl\n"
|
|
"mov $0x09, %%ah\n"
|
|
"int $0x21\n"
|
|
: /* no output */
|
|
: "d"(string)
|
|
: "ah");
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Put a character
|
|
* Author - Vilyaem
|
|
* Date - May 16 2024
|
|
* *******************************************/
|
|
static inline vo PutChar(i16 c){
|
|
__asm__ volatile (/*"movl %0,%%ax\n"*/
|
|
"mov $0x02, %%ah\n"
|
|
"int $0x21\n"
|
|
: /*no output*/
|
|
: "r"(c)
|
|
: "eax");
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Start the PC Speaker
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline i16 Beep(i16 freq){
|
|
i16 div;
|
|
u8 tmp;
|
|
|
|
/*Set the PIT to the desired frequency*/
|
|
div = 1193180 / (u8)freq;
|
|
OutB(0x43, 0xb6);
|
|
OutB(0x42, (u8) (div) );
|
|
OutB(0x42, (u8) (div >> 8));
|
|
|
|
/*And play the sound using the PC speaker*/
|
|
tmp = InB(0x61);
|
|
if (tmp != (tmp | 3)) {
|
|
OutB(0x61, tmp | 3);
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Stop the PC Speaker
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo BeepOff(vo){
|
|
__asm__ volatile("in $0x61,%al\n"
|
|
"and $0xfc,%al\n"
|
|
"out %al,$0x61\n"
|
|
);
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Clear the screen in text mode,
|
|
* then move the cursor to the top right
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo TextClear(vo){
|
|
/*Clear the screen*/
|
|
__asm__ volatile("mov $0x06,%ah\n"
|
|
"mov $0,%al\n"
|
|
"mov $0x07,%bh\n"
|
|
"mov $0,%cx\n"
|
|
"mov $0x184F,%dx\n"
|
|
"int $0x10\n"
|
|
|
|
/*Set the cursor to the top left*/
|
|
"mov $0x02,%ah\n"
|
|
"mov $0x00,%bh\n"
|
|
"mov $0x00,%dh\n"
|
|
"mov $0x00,%dl\n"
|
|
"int $0x10\n"
|
|
);
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Set Text Cursor Position
|
|
* TODO
|
|
* Author - Vilyaem
|
|
* Date - May 14 2024
|
|
* *******************************************/
|
|
static inline vo SetCursorPos(i16 x, i16 y){
|
|
/*Set X and Y*/
|
|
__asm__ volatile(
|
|
"mov $0x02,%ah\n"
|
|
"mov $0x00,%bh\n"
|
|
"mov $0x00,%dh\n"
|
|
"mov $0x00,%dl\n"
|
|
"int $0x10\n"
|
|
);
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Set text colours
|
|
* TODO
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo SetTextColour(i16 fg, i16 bg){
|
|
__asm__ volatile ("mov $0x9, %%ah\n"
|
|
"mov $9,%%bl\n"
|
|
"mov $9,%%cx\n"
|
|
"int $0x10\n"
|
|
: /*no output*/
|
|
: "r"(fg),"r"(bg)
|
|
: );
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Turn on VGA mode
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo Vga(vo){
|
|
__asm__ volatile(
|
|
"mov $0x0013,%ax\n"
|
|
"int $0x10\n"
|
|
);
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Turn off VGA mode
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo VgaOff(vo){
|
|
__asm__ volatile(
|
|
"mov $0x0003,%ax\n"
|
|
"int $0x10\n"
|
|
);
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Plot a pixel in VGA
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo VgaPutPx(i16 x, i16 y, u8 vgacol){
|
|
u8* location = (u8*)VGA_MEM + VGA_RESX * y + x;
|
|
*location = vgacol;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Shut the machine down via
|
|
* reset vector or triple fault
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo Shutdown(vo){
|
|
__asm__ volatile("jmp 0xFFFF");
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Get a key press
|
|
* Reads straight from the IO port then
|
|
* picks from scancode table
|
|
* Author - Vilyaem
|
|
* Date - May 14 2024
|
|
* *******************************************/
|
|
static inline i8 GetKey(vo){
|
|
i8 c;
|
|
c = kbdmap[InB(0x60)];
|
|
return c;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Wipe the VGA screen
|
|
* Author - Vilyaem
|
|
* Date - May 14 2024
|
|
* *******************************************/
|
|
static inline vo VgaClear(i16 col){
|
|
u8* loc = (u8*)VGA_MEM;
|
|
u8* dest = (u8*)VGA_END;
|
|
while((u8*)loc != (u8*)dest){
|
|
*loc=col;
|
|
++loc;
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Our Oracle
|
|
* Author - Vilyaem
|
|
* Date - May 14 2024
|
|
* *******************************************/
|
|
static inline i16 GetOracle(vo){
|
|
u16 oracle;
|
|
__asm__ volatile("rdtsc\nmov %%eax,%0" : "=a" (oracle));
|
|
return oracle;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Sleep for an amount of time
|
|
*
|
|
* 1000 is one second
|
|
* this is a nasty hacktogether
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
static inline vo Sleep(i16 time){
|
|
i16 nexttime = GetOracle() + time;
|
|
while(GetOracle() <= nexttime){
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Convert i16egers into strings
|
|
* Author - Vilyaem
|
|
* Date - May 14 2024
|
|
* *******************************************/
|
|
static inline i8* Int2Str(i16 num, i8* string){
|
|
if(num == 0){
|
|
*string = '0';
|
|
*(string+1) = 0;
|
|
return string;
|
|
}
|
|
i8* start = string;
|
|
if(num < 0 ){
|
|
*start = '-';
|
|
num *= -1;
|
|
++start;
|
|
}
|
|
while(num > 0 ){
|
|
*start = '0' + num % 10;
|
|
num /= 10;
|
|
++start;
|
|
}
|
|
*start = 0;
|
|
i8* end = start - 1;
|
|
start = string + (*string == '-');
|
|
while(end > start ){
|
|
i8 t = *start;
|
|
*start = *end;
|
|
*end = t;
|
|
|
|
++start;
|
|
end--;
|
|
}
|
|
return string;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Output data to serial port
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo SerialWrite(u8 data){
|
|
OutB(data,COM1_PORT);
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Output text to serial port
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo SerialPuts(u8 msg[]){
|
|
for(i = 0; msg[i] != '$';++i){
|
|
SerialWrite(msg[i]);
|
|
}
|
|
SerialWrite('\n');
|
|
SerialWrite('\r');
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Paint the framebuffer to VGA
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo GrFlip(){
|
|
u8* fbloc = (u8*)FB_MEM;
|
|
u8* loc = (u8*)VGA_MEM;
|
|
u8* dest = (u8*)VGA_END;
|
|
while((u8*)loc != (u8*)dest){
|
|
*loc=*fbloc;
|
|
++loc;
|
|
++fbloc;
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Put a pixel on the framebuffer
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo GrPutPx(i16 x, i16 y, u8 col){
|
|
if(x <= VGA_RESX && y <= VGA_RESY && x >= 0 && y >= 0){
|
|
*(u8*)(FB_MEM + VGA_RESX * y + x) = col;
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Clear the framebuffer
|
|
* given a colour
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo GrClear(u8 col){
|
|
u8* loc = (u8*)FB_MEM;
|
|
u8* dest = (u8*)FB_END;
|
|
while((u8*)loc != (u8*)dest){
|
|
*loc=col;
|
|
++loc;
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Draw an XBM image
|
|
* Author - Vilyaem
|
|
* Date - May 15 2024
|
|
* *******************************************/
|
|
static inline vo GrDrawXBM(u8 xbm[], i16 xpos, i16 ypos, i16 width, i16 height){
|
|
i16 bwidth = (width + 7) / 8;
|
|
for (i16 y = 0; y != height; ++y) {
|
|
for (i16 x = 0; x != width; ++x) {
|
|
i16 pix = (i16)xbm[y * bwidth + x / 8];
|
|
i16 mask = (i16)(1 << (x % 8));
|
|
if((pix & mask)){
|
|
GrPutPx(x+xpos,y+ypos,FG_COL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Get 16-bit random integer
|
|
* in range
|
|
* Author - Vilyaem
|
|
* Date - May 17 2024
|
|
* *******************************************/
|
|
static inline i16 I16RandRange(i16 min, i16 max){
|
|
i16 low,high;
|
|
__asm__ volatile ("rdtsc" : "=a" (low), "=d" (high));
|
|
return (i16)((high << 4) | low) % max + min;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Cheap 2D distance via Chebyshev
|
|
* Author - William King
|
|
* Date - Mar 12 2024
|
|
* *******************************************/
|
|
static inline i16 DistCheb(int x0, int y0, int x1, int y1){
|
|
x0 = x1 > x0 ? x1 - x0 : x0 - x1;
|
|
y0 = y1 > y0 ? y1 - y0 : y0 - y1;
|
|
return x0 > y0 ? x0 : y0;
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Fire a fireball
|
|
* Author - Vilyaem
|
|
* Date - May 17 2024
|
|
* *******************************************/
|
|
static inline vo FireBall(){
|
|
fballs[fballptr].x = sx + 75;
|
|
fballs[fballptr].y = sy + 20;
|
|
++fballptr;
|
|
if(fballptr >= BULLETS){
|
|
fballptr = 0;
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Draw a fireball
|
|
* Author - Vilyaem
|
|
* Date - May 19 2024
|
|
* *******************************************/
|
|
static inline vo DrawFireball(int x, int y){
|
|
i16 size = y / 2;
|
|
if(size >= 1){
|
|
for(j = 0; j != 64;++j){
|
|
GrPutPx(x+I16RandRange(0,size),y+I16RandRange(0,size),FG_COL);
|
|
GrPutPx(x-I16RandRange(0,size),y+I16RandRange(0,size),FG_COL);
|
|
GrPutPx(x+I16RandRange(0,size),y-I16RandRange(0,size),FG_COL);
|
|
GrPutPx(x-I16RandRange(0,size),y-I16RandRange(0,size),FG_COL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Description - Main
|
|
* Author - Vilyaem
|
|
* Date - May 12 2024
|
|
* *******************************************/
|
|
i16 dosmain(vo){
|
|
|
|
|
|
u8 sstr[] = { 'V','I','L','Y','A','E','M','$'};
|
|
SerialPuts(sstr);
|
|
|
|
/*Init*/
|
|
Vga();
|
|
|
|
for(i = 0; i != DMNS;++i){
|
|
dmns[i].x = I16RandRange(10,220);
|
|
dmns[i].y = -96-I16RandRange(10,50);
|
|
}
|
|
|
|
for(i = 0; i != BULLETS;++i){
|
|
fballs[i].x = 0;
|
|
fballs[i].y = -40;
|
|
}
|
|
|
|
while(1){
|
|
|
|
GrClear(BG_COL);
|
|
|
|
|
|
/*Handle and draw demons*/
|
|
for(i = 0; i != DMNS;++i){
|
|
dmns[i].y += DMN_MVSPEED;
|
|
if(dmns[i].y > 600){
|
|
dmns[i].y = -96-I16RandRange(10,50);
|
|
dmns[i].x = I16RandRange(10,220);
|
|
}
|
|
for(j = 0; j != DMNS;++j){
|
|
if(DistCheb(dmns[i].x,dmns[i].y,fballs[j].x,fballs[i].y) <= 80){
|
|
dmns[i].y = -96-I16RandRange(10,50);
|
|
dmns[i].x = I16RandRange(10,220);
|
|
}
|
|
}
|
|
if(dmns[i].y >= -40){
|
|
GrDrawXBM(dmn_bits,dmns[i].x,dmns[i].y,dmn_width,dmn_height);
|
|
}
|
|
}
|
|
|
|
/*Handle and draw fireballs*/
|
|
for(i = 0; i != fballptr;++i){
|
|
if(fballs[i].y >= -40){
|
|
fballs[i].y -= DMN_MVSPEED;
|
|
DrawFireball(fballs[i].x,fballs[i].y);
|
|
}
|
|
}
|
|
|
|
/*Draw player*/
|
|
GrDrawXBM(seraph_bits,sx,sy,seraph_width,seraph_height);
|
|
|
|
|
|
/*Paint to VGA Memory*/
|
|
GrFlip();
|
|
|
|
/*Input*/
|
|
switch(GetKey()){
|
|
case ',': sy -= SERAPH_MVSPEED;break;
|
|
case 'a': sx -= SERAPH_MVSPEED;break;
|
|
case 'e': sx += SERAPH_MVSPEED;break;
|
|
case 'o': sy += SERAPH_MVSPEED;break;
|
|
case ' ': FireBall();break;
|
|
case 'x': Shutdown();break;
|
|
default:Beep(100);BeepOff();break;
|
|
}
|
|
|
|
DrawFireball(VGA_RESX/2,VGA_RESY/2);
|
|
|
|
/*Keep the player in bounds
|
|
like a magnet, they can obviously
|
|
still fly away*/
|
|
if(sy < 0-3){
|
|
sy += 3;
|
|
}
|
|
else if(sy > VGA_RESY-64){
|
|
sy -= 3;
|
|
}
|
|
else if(sx < 0-3){
|
|
sx += 3;
|
|
}
|
|
else if(sx > VGA_RESY+3){
|
|
sx -= 3;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|