core/src/memory.c

#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <stdbool.h>
#include <SDL3/SDL.h>
#include <sys/mman.h>
#include "main.h"
#include "procfs.h"
#include "hash.h"
#include "curve.h"
#include "panic.h"
#include "memory.h"

uintptr_t to_addr(int x, int y) {
	unsigned int ux, uy;
	memcpy(&ux, &x, sizeof(unsigned int));
	memcpy(&uy, &y, sizeof(unsigned int));
	uintptr_t page = zorder(ux / PAGE_WIDTH, uy / PAGE_HEIGHT);
	int offset = x % PAGE_WIDTH / CHAR_BIT * PAGE_HEIGHT + y % PAGE_HEIGHT;
	return page * PAGE_SIZE + offset;
}

void to_pos(uintptr_t addr, int *x, int *y) {
	int px, py;
	unzorder(addr / PAGE_SIZE, &px, &py);
	int offset = addr % PAGE_SIZE;
	int ux = px * PAGE_WIDTH + offset % PAGE_WIDTH;
	int uy = py * PAGE_HEIGHT + offset / PAGE_WIDTH;
	memcpy(x, &ux, sizeof(int));
	memcpy(y, &uy, sizeof(int));
}

int init_page_list(page_list *l, int fd) {
	l->fd = fd;
}

static void free_page(page *p) {
	if (p->tex) {
		SDL_DestroyTexture(p->tex);
	}
	free(p);
}

void free_page_list(page_list *l) {
	page *p = l->first;
	while (p) {
		page *next = p->next;
		free_page(p);
		p = next;
	}
	l->fd = -1;
	l->first = NULL;
}

void free_unused_pages(page_list *l) {
	page **prev_link = &l->first;
	page *p = l->first;
	while (p) {
		page *next = p->next;
		if (!p->in_use) {
			*prev_link = next;
			free_page(p);
		} else {
			prev_link = &p->next;
			p->in_use = false;
		}
		p = next;
	}
}

page *get_page(page_list *l, uintptr_t addr) {
	addr = addr - addr % PAGE_SIZE;
	for (page *p = l->first; p; p = p->next) {
		if (p->address == addr) {
			return p;
		}
	}
	page *p = calloc(1, sizeof(page));
	if (!p) panic("out of memory");
	p->address = addr;
	p->l = l;
	p->next = l->first;
	l->first = p;
	return p;
}

static SDL_Palette *texture_palette() {
	SDL_Color colors[] = {{0x00, 0x00, 0x00, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}};
	static SDL_Palette *palette = NULL;
	if (!palette) {
		palette = must(SDL_CreatePalette(2));
		must(SDL_SetPaletteColors(palette, colors, 0, 2));
	}
	return palette;
}

SDL_Texture *get_texture(page *p) {
	static char data[PAGE_SIZE];
	if (read_mem(p->l->fd, p->address, data, PAGE_SIZE) != 0) {
		return NULL;
	}
	uint32_t hash = fnv(data, PAGE_SIZE);
	if (p->hash != hash) {
		static char pixels[PAGE_SIZE];
		for (int col = 0; col < PAGE_WIDTH / 8; col++) {
			for (int row = 0; row < PAGE_HEIGHT; row++) {
				pixels[row * PAGE_WIDTH / 8 + col] =
					data[col * PAGE_HEIGHT + row];
			}
		}
		if (p->tex) SDL_DestroyTexture(p->tex);
		SDL_Surface *surface = must(SDL_CreateSurfaceFrom(
			PAGE_WIDTH, PAGE_HEIGHT, SDL_PIXELFORMAT_INDEX1LSB, pixels,
			PAGE_WIDTH / 8
		));
		must(SDL_SetSurfacePalette(surface, texture_palette()));
		p->tex = must(SDL_CreateTextureFromSurface(renderer, surface));
		SDL_DestroySurface(surface);
	}
	p->hash = hash;
	p->in_use = true;
	return p->tex;
}

static void write_bit(int fd, int x, int y, bool bit) {
	uintptr_t addr = to_addr(x, y);
	int bit_offs = x % CHAR_BIT;
	uint8_t byte;
	if (read_mem(fd, addr, &byte, 1) == -1) return;
	byte = (byte & ~(1 << bit_offs)) | (bit << bit_offs);
	write_mem(fd, addr, &byte, 1);
}

void draw_line(page_list *l,
		double x1, double y1, double x2, double y2, bool bit) {
	double dx = (x2 - x1);
	double dy = (y2 - y1);
	int step = abs(dx) >= abs(dy) ? abs(dx) : abs(dy);
	dx = dx / step; dy = dy / step;
	double x = x1; double y = y1;
	for (int i = 0; i <= step; i++) {
		write_bit(l->fd, x, y, bit);
		x = x + dx;
		y = y + dy;
	}
}