Files
jvm_emulator/helpers.c
T
2026-03-30 19:20:11 +00:00

439 lines
14 KiB
C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "helpers.h"
#define HEAP_POOL_SIZE 4096
static Object pool[HEAP_POOL_SIZE];
static Object *free_list = NULL;
static int initialized = 0;
static void heap_init(void) {
for (int i = 0; i < HEAP_POOL_SIZE - 1; i++)
pool[i].next_free = &pool[i + 1];
pool[HEAP_POOL_SIZE - 1].next_free = NULL;
free_list = &pool[0];
initialized = 1;
}
Object *heap_alloc(CPEntry *cls, uint16_t fields_count) {
if (!initialized) heap_init();
if (!free_list) {
fprintf(stderr, "heap: out of memory\n");
return NULL;
}
Object *obj = free_list;
free_list = obj->next_free;
obj->next_free = NULL;
obj->cls = cls;
obj->fields_count = fields_count;
obj->fields = calloc(fields_count, sizeof(intptr_t));
return obj;
}
void heap_free(Object *obj) {
if (!obj) return;
free(obj->fields);
obj->fields = NULL;
obj->next_free = free_list;
free_list = obj;
}
void heap_destroy(void) {
for (int i = 0; i < HEAP_POOL_SIZE; i++)
if (pool[i].next_free == NULL && pool[i].fields)
free(pool[i].fields);
}
/* SDL2 globals */
static SDL_Window *g_win = NULL;
static SDL_Renderer *g_ren = NULL;
static TTF_Font *g_font = NULL;
extern int execute(ClassFile *cf, Method *m, intptr_t *args, int argc);
/* Fake "Display" object slot — just a sentinel int, not a real Object */
#define DISPLAY_SENTINEL 0xD15F1A7
void midp_sdl_init(int w, int h) {
SDL_Init(SDL_INIT_VIDEO);
if (TTF_Init() != 0) {
fprintf(stderr, "SDL_ttf init failed: %s\n", TTF_GetError());
}
g_win = SDL_CreateWindow("MIDlet", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, 0);
g_ren = SDL_CreateRenderer(g_win, -1, SDL_RENDERER_ACCELERATED);
if (!g_font) {
const char *font_candidates[] = {
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf",
"/usr/share/fonts/truetype/freefont/FreeSans.ttf"
};
for (size_t i = 0; i < sizeof(font_candidates) / sizeof(font_candidates[0]); i++) {
g_font = TTF_OpenFont(font_candidates[i], 16);
if (g_font) break;
}
if (!g_font) {
fprintf(stderr, "SDL_ttf font load failed: %s\n", TTF_GetError());
}
}
SDL_SetRenderDrawColor(g_ren, 0, 0, 0, 255);
SDL_RenderClear(g_ren);
SDL_RenderPresent(g_ren);
}
void midp_sdl_destroy(void) {
if (g_font) {
TTF_CloseFont(g_font);
g_font = NULL;
}
if (TTF_WasInit()) TTF_Quit();
SDL_DestroyRenderer(g_ren);
SDL_DestroyWindow(g_win);
SDL_Quit();
}
void midp_sdl_wait(int milliseconds) {
Uint32 start = SDL_GetTicks();
SDL_Event ev;
while ((int)(SDL_GetTicks() - start) < milliseconds) {
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT) {
return;
}
}
SDL_Delay(16);
}
}
/* helper: match class/name/desc triple */
static int is(const char *a, const char *b) { return strcmp(a, b) == 0; }
static void draw_text_ttf(const char *text, int x, int y) {
if (!text || !*text || !g_font || !g_ren) return;
Uint8 r = 255, g = 255, b = 255, a = 255;
SDL_GetRenderDrawColor(g_ren, &r, &g, &b, &a);
SDL_Color color = { r, g, b, a };
SDL_Surface *surface = TTF_RenderUTF8_Blended(g_font, text, color);
if (!surface) return;
SDL_Texture *texture = SDL_CreateTextureFromSurface(g_ren, surface);
if (!texture) {
SDL_FreeSurface(surface);
return;
}
SDL_Rect dst = { x, y, surface->w, surface->h };
SDL_FreeSurface(surface);
SDL_RenderCopy(g_ren, texture, NULL, &dst);
SDL_DestroyTexture(texture);
}
static void draw_arc_outline(int cx, int cy, int rx, int ry, int start_deg, int arc_deg) {
int end_deg = start_deg + arc_deg;
for (int deg = start_deg; deg <= end_deg; deg++) {
double rad = deg * 3.14159265358979323846 / 180.0;
int x = cx + (int)(rx * cos(rad));
int y = cy + (int)(ry * sin(rad));
SDL_RenderDrawPoint(g_ren, x, y);
}
}
static int invoke_canvas_paint(ClassFile *caller_cf, intptr_t canvas_slot) {
Object *canvas = SLOT_TO_OBJ(canvas_slot);
if (!canvas || !canvas->cls || canvas->cls->tag != CP_CLASS) return 0;
const char *canvas_class = cp_utf8(caller_cf, canvas->cls->cls_or_str.index);
if (!canvas_class || !canvas_class[0]) return 0;
char class_path[512];
snprintf(class_path, sizeof(class_path), "%s.class", canvas_class);
ClassFile *canvas_cf = classfile_load(class_path);
if (!canvas_cf) {
fprintf(stderr, "cannot load canvas class file: %s\n", class_path);
return 0;
}
Method *paint = classfile_find_method(canvas_cf, "paint",
"(Ljavax/microedition/lcdui/Graphics;)V");
if (!paint) {
classfile_free(canvas_cf);
return 0;
}
intptr_t args[2] = { canvas_slot, 0xC0FFEE };
int rc = execute(canvas_cf, paint, args, 2);
classfile_free(canvas_cf);
SDL_RenderPresent(g_ren);
return rc >= 0;
}
int midp_native_call(ClassFile *cf, const char *class_name, const char *name, const char *desc, intptr_t *stack, int *sp, int opcode __attribute__((unused)))
{
if (is(desc, "()I") && is(name, "getWidth")) {
int w, h;
(void)h;
SDL_GetWindowSize(g_win, &w, &h);
(*sp)--; /* pop 'this' */
stack[(*sp)++] = w;
return 1;
}
if (is(desc, "()I") && is(name, "getHeight")) {
int w, h;
(void)w;
SDL_GetWindowSize(g_win, &w, &h);
(*sp)--; /* pop 'this' */
stack[(*sp)++] = h;
return 1;
}
/* javax.microedition.lcdui.Display */
if (is(class_name, "javax/microedition/lcdui/Display")) {
/* Display.getDisplay(MIDlet) → Display */
if (is(name, "getDisplay") &&
is(desc, "(Ljavax/microedition/midlet/MIDlet;)Ljavax/microedition/lcdui/Display;")) {
(*sp)--; /* pop MIDlet ref */
stack[(*sp)++] = DISPLAY_SENTINEL;
return 1;
}
/* Display.setCurrent(Displayable) → void */
if (is(name, "setCurrent") &&
is(desc, "(Ljavax/microedition/lcdui/Displayable;)V")) {
intptr_t displayable = stack[--(*sp)];
intptr_t display_ref = stack[--(*sp)]; /* 'this' for virtual */
(void)displayable; (void)display_ref;
/* Clear and show a frame so emulator window is visibly active. */
SDL_SetRenderDrawColor(g_ren, 0, 0, 0, 255);
SDL_RenderClear(g_ren);
/* Try to run original Canvas.paint(Graphics). */
invoke_canvas_paint(cf, displayable);
SDL_RenderPresent(g_ren);
return 1;
}
}
/* javax.microedition.lcdui.Graphics ─ */
if (is(class_name, "javax/microedition/lcdui/Graphics")) {
/* setColor(int) */
if (is(name, "setColor") && is(desc, "(I)V")) {
int rgb = stack[--(*sp)];
(*sp)--; /* pop 'this' */
SDL_SetRenderDrawColor(g_ren,
(rgb >> 16) & 0xFF,
(rgb >> 8) & 0xFF,
rgb & 0xFF, 255);
return 1;
}
/* setColor(r, g, b) */
if (is(name, "setColor") && is(desc, "(III)V")) {
int b = stack[--(*sp)] & 0xFF;
int g = stack[--(*sp)] & 0xFF;
int r = stack[--(*sp)] & 0xFF;
(*sp)--; /* pop 'this' */
SDL_SetRenderDrawColor(g_ren, r, g, b, 255);
return 1;
}
/* fillRect(x, y, w, h) */
if (is(name, "fillRect") && is(desc, "(IIII)V")) {
int h2 = stack[--(*sp)];
int w2 = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
(*sp)--; /* pop 'this' */
SDL_Rect r = { x, y, w2, h2 };
SDL_RenderFillRect(g_ren, &r);
return 1;
}
/* drawRect(x, y, w, h) */
if (is(name, "drawRect") && is(desc, "(IIII)V")) {
int h2 = stack[--(*sp)];
int w2 = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
(*sp)--;
SDL_Rect r = { x, y, w2, h2 };
SDL_RenderDrawRect(g_ren, &r);
return 1;
}
/* drawLine(x1, y1, x2, y2) */
if (is(name, "drawLine") && is(desc, "(IIII)V")) {
int y2 = stack[--(*sp)];
int x2 = stack[--(*sp)];
int y1 = stack[--(*sp)];
int x1 = stack[--(*sp)];
(*sp)--;
SDL_RenderDrawLine(g_ren, x1, y1, x2, y2);
return 1;
}
/* drawString(String, x, y, anchor) - anchor ignored for now */
if (is(name, "drawString") && is(desc, "(Ljava/lang/String;III)V")) {
int anchor = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
int sidx = stack[--(*sp)];
(*sp)--;
(void)anchor;
if (sidx > 0 && sidx < cf->cp_count && cf->cp[sidx].tag == CP_STRING) {
const char *text = cp_utf8(cf, cf->cp[sidx].cls_or_str.index);
draw_text_ttf(text, x, y);
}
return 1;
}
if (is(name, "drawArc") && is(desc, "(IIIIII)V")) {
int arc_deg = stack[--(*sp)];
int start = stack[--(*sp)];
int h2 = stack[--(*sp)];
int w2 = stack[--(*sp)];
int y = stack[--(*sp)];
int x = stack[--(*sp)];
(*sp)--;
int cx = x + (w2 / 2);
int cy = y + (h2 / 2);
int rx = w2 / 2;
int ry = h2 / 2;
draw_arc_outline(cx, cy, rx, ry, start, arc_deg);
return 1;
}
/* flushGraphics / repaint stubs — just present */
if (is(name, "flushGraphics") || is(name, "repaint")) {
/* pop whatever args there are, then 'this' */
/* for safety just drain back to a known depth — caller handles */
SDL_RenderPresent(g_ren);
return 1;
}
}
/* javax.microedition.lcdui.Canvas */
if (is(class_name, "javax/microedition/lcdui/Canvas")) {
/* Canvas.repaint() */
if (is(name, "repaint") && is(desc, "()V")) {
(*sp)--; /* pop 'this' */
SDL_RenderPresent(g_ren);
return 1;
}
/* Canvas.getGraphics() → Graphics (stub: return sentinel) */
if (is(name, "getGraphics") &&
is(desc, "()Ljavax/microedition/lcdui/Graphics;")) {
(*sp)--; /* pop 'this' */
stack[(*sp)++] = 0xC0FFEE; /* fake Graphics handle */
return 1;
}
}
/* java.io.PrintStream ─ */
if (is(class_name, "java/io/PrintStream")) {
if (is(name, "println")) {
if (is(desc, "(I)V")) {
int val = stack[--(*sp)]; (*sp)--;
printf("%d\n", val);
return 1;
}
if (is(desc, "(Ljava/lang/String;)V")) {
int str_idx = stack[--(*sp)]; (*sp)--;
CPEntry *e = &cf->cp[str_idx];
if (e->tag == CP_STRING)
printf("%s\n", cp_utf8(cf, e->cls_or_str.index));
return 1;
}
if (is(desc, "(Z)V")) {
int val = stack[--(*sp)]; (*sp)--;
printf("%s\n", val ? "true" : "false");
return 1;
}
if (is(desc, "()V")) { (*sp)--; printf("\n"); return 1; }
}
if (is(name, "print")) {
if (is(desc, "(I)V")) {
int val = stack[--(*sp)]; (*sp)--;
printf("%d", val);
return 1;
}
if (is(desc, "(Ljava/lang/String;)V")) {
int str_idx = stack[--(*sp)]; (*sp)--;
CPEntry *e = &cf->cp[str_idx];
if (e->tag == CP_STRING)
printf("%s", cp_utf8(cf, e->cls_or_str.index));
return 1;
}
}
}
/* java.lang.Thread (minimal stubs) */
if (is(class_name, "java/lang/Thread")) {
if (is(name, "sleep")) {
long long ms = stack[--(*sp)];
(*sp)--; /* long takes 2 slots */
SDL_Delay((uint32_t)ms);
return 1;
}
if (is(name, "start") || is(name, "join")) {
(*sp)--;
return 1;
}
}
/* javax.microedition.midlet.MIDlet (common stubs) */
if (is(name, "getAppProperty") && is(desc, "(Ljava/lang/String;)Ljava/lang/String;")) {
/* this, key -> null */
(*sp) -= 2;
stack[(*sp)++] = 0;
return 1;
}
if (is(name, "notifyDestroyed") && is(desc, "()V")) {
(*sp)--; /* this */
return 1;
}
if (is(name, "notifyPaused") && is(desc, "()V")) {
(*sp)--; /* this */
return 1;
}
if (is(name, "platformRequest") && is(desc, "(Ljava/lang/String;)Z")) {
(*sp) -= 2; /* this, url */
stack[(*sp)++] = 0;
return 1;
}
if (is(name, "checkPermission") && is(desc, "(Ljava/lang/String;)I")) {
(*sp) -= 2; /* this, permission */
stack[(*sp)++] = 1; /* allowed */
return 1;
}
return 0; /* not handled */
}