Mirai's Miscellaneous Misadventures

M22 / engines / x86.c

// copyright 2022 zamfofex
// license: AGPLv3 or later

#include <mimimi/allocators.h>
#include <mimimi/test.h>
#include <mimimi/engines.h>
#include <mimimi/geometry.h>
#include <mimimi/assets.h>
#include <mimimi/chapters.h>
#include <mimimi/behaviors.h>

struct mimimi_vbe_screen
{
	unsigned int *buffer;
	unsigned short int width;
	unsigned short int height;
	unsigned short int bytes_per_line;
	unsigned short int bytes_per_pixel;
};

extern struct mimimi_vbe_screen mimimi_vbe_screen;

void mimimi_x86_exit(void);

extern unsigned char mimimi_x86_heap_start;
static unsigned char *mimimi_x86_heap = &mimimi_x86_heap_start;

static unsigned char mimimi_x86_blocks[8192] = {};

static void *mimimi_x86_allocate(unsigned int size)
{
	int blocks = (size - 1) / 1024 + 1;
	
	for (int i = 0 ; i < 8192 ; i++)
	{
		for (int j = 0 ; j < blocks ; j++)
		{
			if (mimimi_x86_blocks[i + j] != 0)
				goto next;
		}
		
		mimimi_x86_blocks[i] = 1;
		for (int j = 1 ; j < blocks ; j++)
			mimimi_x86_blocks[i + j] = 2;
		
		unsigned char *data = mimimi_x86_heap + i * 1024;
		return data;
		
		next:;
	}
	
	mimimi_x86_exit();
}

static void mimimi_x86_deallocate(void *data0)
{
	unsigned char *data = data0;
	int block = (data - mimimi_x86_heap) / 1024;
	
	mimimi_x86_blocks[block] = 0;
	for (int i = 1 ; mimimi_x86_blocks[block + i] == 2 ; i++)
		mimimi_x86_blocks[block + i] = 0;
}

static void *mimimi_x86_reallocate(void *data0, unsigned int size)
{
	if (data0 == 0) return mimimi_x86_allocate(size);
	
	unsigned char *data = data0;
	int block = (data - mimimi_x86_heap) / 1024;
	
	int current_count = 1;
	while (mimimi_x86_blocks[block + current_count] == 2) current_count++;
	
	int target_count = (size - 1) / 1024 + 1;
	
	if (target_count <= current_count)
	{
		for (int i = target_count ; i < current_count ; i++)
			mimimi_x86_blocks[block + i] = 0;
		return data;
	}
	
	for (int i = current_count ; i < target_count ; i++)
	{
		if (mimimi_x86_blocks[block + i] != 0)
		{
			unsigned char *new = mimimi_x86_allocate(size);
			for (unsigned int i = 0 ; i < current_count * 1024 ; i++)
				new[i] = data[i];
			mimimi_x86_deallocate(data);
			return new;
		}
	}
	
	for (int i = current_count ; i < target_count ; i++)
		mimimi_x86_blocks[block + i] = 2;
	return data;
}

static struct mimimi_allocator mimimi_x86_allocator_value = {&mimimi_x86_allocate, &mimimi_x86_reallocate, &mimimi_x86_deallocate, 0};
static struct mimimi_allocator *mimimi_x86_allocator = &mimimi_x86_allocator_value;

static unsigned char *mimimi_x86_buffer;
static unsigned char *mimimi_x86_out_buffer;

static void mimimi_x86_stamp(void *data, int x1, int y1, struct mimimi_image *image)
{
	int x0 = 0;
	int y0 = 0;
	
	if (x1 < 0) x0 = -x1;
	if (y1 < 0) y0 = -y1;
	
	int width = image->width;
	int height = image->height;
	
	if (width + x1 > 512) width = 512 - x1;
	if (height + y1 > 256) height = 256 - y1;
	
	for (int y = y0 ; y < height ; y++)
	for (int x = x0 ; x < width ; x++)
	{
		unsigned char color = image->colors[x + y * image->width];
		if (color == 0) continue;
		
		int x2 = x + x1;
		int y2 = y + y1;
		
		mimimi_x86_buffer[x2 + y2 * 512] = color;
	}
}

static struct mimimi_chapter *chapter;

void mimimi_x86_start(void)
{
	mimimi_x86_buffer = mimimi_x86_allocate(512 * 256);
	mimimi_x86_out_buffer = mimimi_x86_allocate(512 * 256 * 4);
	
	static struct mimimi_size size = {512, 256};
	chapter = mimimi_test(mimimi_x86_allocator);
	chapter->engine->size = &size;
	chapter->engine->stamp = &mimimi_x86_stamp;
}

void mimimi_x86_step(void)
{
	extern unsigned char mimimi_x86_left;
	extern unsigned char mimimi_x86_right;
	
	chapter->left = mimimi_x86_left;
	chapter->right = mimimi_x86_right;
	
	(*chapter->behavior->behave)(chapter->behavior->data);
	
	static unsigned char rch[] = {0x11, 0x44, 0x77, 0x99, 0xCC, 0xFF};
	static unsigned char gch[] = {0x11, 0x33, 0x55, 0x77, 0x99, 0xBB, 0xDD, 0xFF};
	static unsigned char bch[] = {0x22, 0x55, 0x88, 0xBB, 0xEE};
	
	unsigned int width = 512;
	unsigned int height = 256;
	
	unsigned int x0 = (mimimi_vbe_screen.width - width) / 2;
	unsigned int y0 = (mimimi_vbe_screen.height - height) / 2;
	
	if (mimimi_vbe_screen.width < width) x0 = 0, width = mimimi_vbe_screen.width;
	if (mimimi_vbe_screen.height < height) y0 = 0, height = mimimi_vbe_screen.height;
	
	for (unsigned int y = 0 ; y < height ; y++)
	for (unsigned int x = 0 ; x < width ; x++)
	{
		unsigned char color = mimimi_x86_buffer[x + y * 512];
		unsigned int i = (x + y * 512) * 4;
		
		if (color < 0x10)
		{
			unsigned char ch = color * 0x11;
			mimimi_x86_out_buffer[i + 0] = ch;
			mimimi_x86_out_buffer[i + 1] = ch;
			mimimi_x86_out_buffer[i + 2] = ch;
		}
		else
		{
			color -= 0x10;
			unsigned char r, g, b;
			r = rch[(color / 40) % 6];
			g = gch[(color / 5) % 8];
			b = bch[(color / 1) % 5];
			
			mimimi_x86_out_buffer[i + 0] = b;
			mimimi_x86_out_buffer[i + 1] = g;
			mimimi_x86_out_buffer[i + 2] = r;
		}
	}
	
	unsigned int *buffer = (void *)mimimi_x86_out_buffer;
	
	unsigned int o = x0 + y0 * mimimi_vbe_screen.bytes_per_line / 4;
	for (unsigned int y = 0 ; y < height ; y++)
	for (unsigned int x = 0 ; x < width ; x++)
	{
		unsigned int i = x + y * 512;
		unsigned int j = x + y * mimimi_vbe_screen.bytes_per_line / 4;
		mimimi_vbe_screen.buffer[j + o] = buffer[i];
	}
}