seraphim/main.c
2025-01-11 06:05:10 -05:00

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;
}