mirror of https://gitlab.com/nakst/essence
uxn port
This commit is contained in:
parent
574f07d49f
commit
5bd78070c3
|
@ -174,9 +174,8 @@ int CanvasCallback(EsElement *element, EsMessage *message) {
|
||||||
message->animate.complete = false;
|
message->animate.complete = false;
|
||||||
timeMs += message->animate.deltaMs;
|
timeMs += message->animate.deltaMs;
|
||||||
|
|
||||||
int width, height;
|
|
||||||
EsElementGetSize(element, &width, &height);
|
EsRectangle imageBounds = EsRectangleCenter(EsElementGetInsetBounds(element), ES_RECT_2S(IMAGE_WIDTH, IMAGE_HEIGHT));
|
||||||
EsRectangle imageBounds = EsRectangleCenter(ES_RECT_2S(width, height), ES_RECT_2S(IMAGE_WIDTH, IMAGE_HEIGHT));
|
|
||||||
EsElementRepaint(element, &imageBounds);
|
EsElementRepaint(element, &imageBounds);
|
||||||
return ES_HANDLED;
|
return ES_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1681,7 +1681,6 @@ void ProcessAnimations() {
|
||||||
}
|
}
|
||||||
|
|
||||||
element->lastTimeStamp += m.animate.deltaMs * api.startupInformation->timeStampTicksPerMs;
|
element->lastTimeStamp += m.animate.deltaMs * api.startupInformation->timeStampTicksPerMs;
|
||||||
UIWindowNeedsUpdate(element->window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitMs > 0) {
|
if (waitMs > 0) {
|
||||||
|
@ -5594,6 +5593,13 @@ EsRectangle EsWindowGetBounds(EsWindow *window) {
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EsRectangle EsElementGetInsetBounds(EsElement *element) {
|
||||||
|
EsMessageMutexCheck();
|
||||||
|
EsRectangle insets = element->currentStyle->insets;
|
||||||
|
return ES_RECT_4(insets.l, element->width - insets.r,
|
||||||
|
insets.t, element->height - insets.b);
|
||||||
|
}
|
||||||
|
|
||||||
EsRectangle EsElementGetInsetSize(EsElement *element) {
|
EsRectangle EsElementGetInsetSize(EsElement *element) {
|
||||||
EsMessageMutexCheck();
|
EsMessageMutexCheck();
|
||||||
|
|
||||||
|
|
|
@ -2344,6 +2344,7 @@ function void EsElementRelayout(EsElement *element);
|
||||||
function void EsElementSetCellRange(EsElement *element, int xFrom, int yFrom, int xTo = -1, int yTo = -1); // Use only if the parent is a ES_PANEL_TABLE.
|
function void EsElementSetCellRange(EsElement *element, int xFrom, int yFrom, int xTo = -1, int yTo = -1); // Use only if the parent is a ES_PANEL_TABLE.
|
||||||
function EsRectangle EsElementGetInsets(EsElement *element);
|
function EsRectangle EsElementGetInsets(EsElement *element);
|
||||||
function EsRectangle EsElementGetInsetSize(EsElement *element); // Get the size of the element, minus the insets.
|
function EsRectangle EsElementGetInsetSize(EsElement *element); // Get the size of the element, minus the insets.
|
||||||
|
function EsRectangle EsElementGetInsetBounds(EsElement *element);
|
||||||
function EsThemeMetrics EsElementGetMetrics(EsElement *element); // Returns the *scaled* metrics; the metrics given in the EsStyle passed to element creation functions should *not* be scaled.
|
function EsThemeMetrics EsElementGetMetrics(EsElement *element); // Returns the *scaled* metrics; the metrics given in the EsStyle passed to element creation functions should *not* be scaled.
|
||||||
function float EsElementGetScaleFactor(EsElement *element);
|
function float EsElementGetScaleFactor(EsElement *element);
|
||||||
function EsRectangle EsElementGetPreferredSize(EsElement *element);
|
function EsRectangle EsElementGetPreferredSize(EsElement *element);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) Devine Lu Linvega
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,354 @@
|
||||||
|
// Ported by nakst.
|
||||||
|
// TODO Keyboard support.
|
||||||
|
// TODO Audio support.
|
||||||
|
// TODO Time and date support.
|
||||||
|
|
||||||
|
#include <essence.h>
|
||||||
|
|
||||||
|
#define PPW (sizeof(unsigned int) * 2)
|
||||||
|
#define realloc EsCRTrealloc
|
||||||
|
#define Uint32 uint32_t
|
||||||
|
|
||||||
|
typedef struct Ppu {
|
||||||
|
unsigned short width, height;
|
||||||
|
unsigned int *dat, stride;
|
||||||
|
} Ppu;
|
||||||
|
|
||||||
|
#ifdef DEBUG_BUILD
|
||||||
|
#include "../../bin/uxn/src/uxn.c"
|
||||||
|
#else
|
||||||
|
#include "../../bin/uxn/src/uxn-fast.c"
|
||||||
|
#endif
|
||||||
|
#include "../../bin/uxn/src/devices/ppu.c"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 2021 Devine Lu Linvega
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static Ppu ppu;
|
||||||
|
static Uxn u;
|
||||||
|
static Device *devsystem, *devscreen, *devmouse, *devctrl;
|
||||||
|
static Uint32 palette[16];
|
||||||
|
|
||||||
|
static unsigned int reqdraw = 0;
|
||||||
|
|
||||||
|
static int
|
||||||
|
clamp(int val, int min, int max)
|
||||||
|
{
|
||||||
|
return (val >= min) ? (val <= max) ? val : max : min;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
error(char *msg, const char *err)
|
||||||
|
{
|
||||||
|
EsPrint("%z: %z\n", msg, err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
domouse(int mx, int my, bool pressed, bool released, bool right)
|
||||||
|
{
|
||||||
|
Uint8 flag = 0x00;
|
||||||
|
Uint16 x = clamp(mx, 0, ppu.width - 1);
|
||||||
|
Uint16 y = clamp(my, 0, ppu.height - 1);
|
||||||
|
poke16(devmouse->dat, 0x2, x);
|
||||||
|
poke16(devmouse->dat, 0x4, y);
|
||||||
|
flag = right ? 0x10 : 0x01;
|
||||||
|
if (pressed) devmouse->dat[6] |= flag;
|
||||||
|
if (released) devmouse->dat[6] &= ~flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint8
|
||||||
|
get_pixel(int x, int y)
|
||||||
|
{
|
||||||
|
unsigned int i = x / PPW + y * ppu.stride, shift = x % PPW * 4;
|
||||||
|
return (ppu.dat[i] >> shift) & 0xf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_palette(Uint8 *addr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 4; ++i) {
|
||||||
|
Uint8
|
||||||
|
r = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f,
|
||||||
|
g = (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f,
|
||||||
|
b = (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f;
|
||||||
|
palette[i] = 0xff000000 | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b;
|
||||||
|
}
|
||||||
|
for(i = 4; i < 16; ++i)
|
||||||
|
palette[i] = palette[i / 4];
|
||||||
|
reqdraw = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
system_talk(Device *d, Uint8 b0, Uint8 w)
|
||||||
|
{
|
||||||
|
if(!w) { /* read */
|
||||||
|
switch(b0) {
|
||||||
|
case 0x2: d->dat[0x2] = d->u->wst.ptr; break;
|
||||||
|
case 0x3: d->dat[0x3] = d->u->rst.ptr; break;
|
||||||
|
}
|
||||||
|
} else { /* write */
|
||||||
|
switch(b0) {
|
||||||
|
case 0x2: d->u->wst.ptr = d->dat[0x2]; break;
|
||||||
|
case 0x3: d->u->rst.ptr = d->dat[0x3]; break;
|
||||||
|
case 0xf: return 0;
|
||||||
|
}
|
||||||
|
if(b0 > 0x7 && b0 < 0xe)
|
||||||
|
update_palette(&d->dat[0x8]);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
console_talk(Device *d, Uint8 b0, Uint8 w)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
screen_talk(Device *d, Uint8 b0, Uint8 w)
|
||||||
|
{
|
||||||
|
if(!w) switch(b0) {
|
||||||
|
case 0x2: d->dat[0x2] = ppu.width >> 8; break;
|
||||||
|
case 0x3: d->dat[0x3] = ppu.width; break;
|
||||||
|
case 0x4: d->dat[0x4] = ppu.height >> 8; break;
|
||||||
|
case 0x5: d->dat[0x5] = ppu.height; break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
switch(b0) {
|
||||||
|
case 0x5:
|
||||||
|
ppu_set_size(&ppu, peek16(d->dat, 0x2), peek16(d->dat, 0x4));
|
||||||
|
break;
|
||||||
|
case 0xe: {
|
||||||
|
Uint16 x = peek16(d->dat, 0x8);
|
||||||
|
Uint16 y = peek16(d->dat, 0xa);
|
||||||
|
Uint8 layer = d->dat[0xe] & 0x40;
|
||||||
|
reqdraw |= ppu_pixel(&ppu, layer, x, y, d->dat[0xe] & 0x3);
|
||||||
|
if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 1); /* auto x+1 */
|
||||||
|
if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 1); /* auto y+1 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xf: {
|
||||||
|
Uint16 x = peek16(d->dat, 0x8);
|
||||||
|
Uint16 y = peek16(d->dat, 0xa);
|
||||||
|
Uint8 layer = d->dat[0xf] & 0x40;
|
||||||
|
Uint8 *addr = &d->mem[peek16(d->dat, 0xc)];
|
||||||
|
if(d->dat[0xf] & 0x80) {
|
||||||
|
reqdraw |= ppu_2bpp(&ppu, layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20);
|
||||||
|
if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 16); /* auto addr+16 */
|
||||||
|
} else {
|
||||||
|
reqdraw |= ppu_1bpp(&ppu, layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20);
|
||||||
|
if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 8); /* auto addr+8 */
|
||||||
|
}
|
||||||
|
if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 8); /* auto x+8 */
|
||||||
|
if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 8); /* auto y+8 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
file_talk(Device *d, Uint8 b0, Uint8 w)
|
||||||
|
{
|
||||||
|
Uint8 read = b0 == 0xd;
|
||||||
|
if(w && (read || b0 == 0xf)) {
|
||||||
|
char *name = (char *)&d->mem[peek16(d->dat, 0x8)];
|
||||||
|
Uint16 result = 0, length = peek16(d->dat, 0xa);
|
||||||
|
long offset = (peek16(d->dat, 0x4) << 16) + peek16(d->dat, 0x6);
|
||||||
|
Uint16 addr = peek16(d->dat, b0 - 1);
|
||||||
|
|
||||||
|
size_t pathBytes;
|
||||||
|
char *path = EsStringAllocateAndFormat(&pathBytes, "|Settings:/%z", name);
|
||||||
|
EsFileInformation file = EsFileOpen(path, pathBytes, read ? (ES_FILE_READ | ES_NODE_FAIL_IF_NOT_FOUND) : (ES_FILE_WRITE));
|
||||||
|
EsHeapFree(path, 0, NULL);
|
||||||
|
|
||||||
|
if (file.error != ES_SUCCESS) {
|
||||||
|
result = 0;
|
||||||
|
} else if (read) {
|
||||||
|
result = EsFileReadSync(file.handle, offset, length, &d->mem[addr]);
|
||||||
|
EsHandleClose(file.handle);
|
||||||
|
} else {
|
||||||
|
if (!offset) EsFileResize(file.handle, length);
|
||||||
|
result = EsFileWriteSync(file.handle, offset, length, &d->mem[addr]);
|
||||||
|
EsHandleClose(file.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
poke16(d->dat, 0x2, result);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
datetime_talk(Device *d, Uint8 b0, Uint8 w)
|
||||||
|
{
|
||||||
|
poke16(d->dat, 0x0, 0);
|
||||||
|
d->dat[0x2] = 0;
|
||||||
|
d->dat[0x3] = 0;
|
||||||
|
d->dat[0x4] = 0;
|
||||||
|
d->dat[0x5] = 0;
|
||||||
|
d->dat[0x6] = 0;
|
||||||
|
d->dat[0x7] = 0;
|
||||||
|
poke16(d->dat, 0x08, 0);
|
||||||
|
d->dat[0xa] = 0;
|
||||||
|
(void)b0;
|
||||||
|
(void)w;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nil_talk(Device *d, Uint8 b0, Uint8 w)
|
||||||
|
{
|
||||||
|
(void)d;
|
||||||
|
(void)b0;
|
||||||
|
(void)w;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *errors[] = {"underflow", "overflow", "division by zero"};
|
||||||
|
|
||||||
|
int
|
||||||
|
uxn_halt(Uxn *u, Uint8 error, char *name, int id)
|
||||||
|
{
|
||||||
|
EsPrint("Halted: %z %z#%x, at 0x%x\n", name, errors[error - 1], id, u->ram.ptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Launch(const void *rom, size_t romBytes) {
|
||||||
|
if (romBytes > sizeof(u.ram.dat) - PAGE_PROGRAM) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uxn_boot(&u)) return false;
|
||||||
|
EsMemoryCopy(u.ram.dat + PAGE_PROGRAM, rom, romBytes);
|
||||||
|
reqdraw = 1;
|
||||||
|
const Uint16 width = 64 * 8, height = 40 * 8;
|
||||||
|
ppu_set_size(&ppu, width, height);
|
||||||
|
|
||||||
|
/* system */ devsystem = uxn_port(&u, 0x0, system_talk);
|
||||||
|
/* console */ uxn_port(&u, 0x1, console_talk);
|
||||||
|
/* screen */ devscreen = uxn_port(&u, 0x2, screen_talk);
|
||||||
|
/* audio0 */ uxn_port(&u, 0x3, nil_talk);
|
||||||
|
/* audio1 */ uxn_port(&u, 0x4, nil_talk);
|
||||||
|
/* audio2 */ uxn_port(&u, 0x5, nil_talk);
|
||||||
|
/* audio3 */ uxn_port(&u, 0x6, nil_talk);
|
||||||
|
/* unused */ uxn_port(&u, 0x7, nil_talk);
|
||||||
|
/* control */ devctrl = uxn_port(&u, 0x8, nil_talk);
|
||||||
|
/* mouse */ devmouse = uxn_port(&u, 0x9, nil_talk);
|
||||||
|
/* file */ uxn_port(&u, 0xa, file_talk);
|
||||||
|
/* datetime */ uxn_port(&u, 0xb, datetime_talk);
|
||||||
|
/* unused */ uxn_port(&u, 0xc, nil_talk);
|
||||||
|
/* unused */ uxn_port(&u, 0xd, nil_talk);
|
||||||
|
/* unused */ uxn_port(&u, 0xe, nil_talk);
|
||||||
|
/* unused */ uxn_port(&u, 0xf, nil_talk);
|
||||||
|
|
||||||
|
uxn_eval(&u, PAGE_PROGRAM);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EsElement *canvas;
|
||||||
|
uint32_t imageWidth, imageHeight;
|
||||||
|
uint32_t *imageBits;
|
||||||
|
uint16_t timeDeltaMs;
|
||||||
|
bool inImageBounds;
|
||||||
|
|
||||||
|
EsRectangle GetImageBounds(EsRectangle bounds /* canvas bounds */) {
|
||||||
|
if (!imageWidth || !imageHeight) return ES_RECT_1(0);
|
||||||
|
int zoomX = ES_RECT_WIDTH(bounds) / imageWidth, zoomY = ES_RECT_HEIGHT(bounds) / imageHeight;
|
||||||
|
int zoom = zoomX < 1 || zoomY < 1 ? 1 : zoomX > zoomY ? zoomY : zoomX;
|
||||||
|
return EsRectangleCenter(bounds, ES_RECT_2S(imageWidth * zoom, imageHeight * zoom));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MouseEvent(bool pressed, bool released, bool right) {
|
||||||
|
if (!imageWidth) return;
|
||||||
|
EsPoint point = EsMouseGetPosition(canvas);
|
||||||
|
EsRectangle imageBounds = GetImageBounds(EsElementGetInsetBounds(canvas));
|
||||||
|
inImageBounds = EsRectangleContains(imageBounds, point.x, point.y);
|
||||||
|
int zoom = ES_RECT_WIDTH(imageBounds) / imageWidth;
|
||||||
|
domouse((point.x - imageBounds.l) / zoom, (point.y - imageBounds.t) / zoom, pressed, released, right);
|
||||||
|
uxn_eval(&u, peek16(devmouse->dat, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int CanvasMessage(EsElement *element, EsMessage *message) {
|
||||||
|
if (message->type == ES_MSG_PAINT) {
|
||||||
|
EsRectangle bounds = EsPainterBoundsInset(message->painter);
|
||||||
|
EsRectangle imageBounds = GetImageBounds(bounds);
|
||||||
|
EsDrawBitmapScaled(message->painter, imageBounds, ES_RECT_2S(imageWidth, imageHeight), imageBits, imageWidth * 4, ES_DRAW_BITMAP_OPAQUE);
|
||||||
|
EsDrawBlock(message->painter, ES_RECT_4(bounds.l, imageBounds.l, bounds.t, bounds.b), 0xFF000000);
|
||||||
|
EsDrawBlock(message->painter, ES_RECT_4(imageBounds.r, bounds.r, bounds.t, bounds.b), 0xFF000000);
|
||||||
|
EsDrawBlock(message->painter, ES_RECT_4(imageBounds.l, imageBounds.r, bounds.t, imageBounds.t), 0xFF000000);
|
||||||
|
EsDrawBlock(message->painter, ES_RECT_4(imageBounds.l, imageBounds.r, imageBounds.b, bounds.b), 0xFF000000);
|
||||||
|
} else if (message->type == ES_MSG_MOUSE_MOVED || message->type == ES_MSG_MOUSE_LEFT_DRAG || message->type == ES_MSG_MOUSE_RIGHT_DRAG) {
|
||||||
|
MouseEvent(false, false, false);
|
||||||
|
} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) {
|
||||||
|
MouseEvent(true, false, false);
|
||||||
|
} else if (message->type == ES_MSG_MOUSE_LEFT_UP) {
|
||||||
|
MouseEvent(false, true, false);
|
||||||
|
} else if (message->type == ES_MSG_MOUSE_RIGHT_DOWN) {
|
||||||
|
MouseEvent(true, false, true);
|
||||||
|
} else if (message->type == ES_MSG_MOUSE_RIGHT_UP) {
|
||||||
|
MouseEvent(false, true, false);
|
||||||
|
} else if (message->type == ES_MSG_GET_CURSOR && inImageBounds) {
|
||||||
|
message->cursorStyle = ES_CURSOR_BLANK;
|
||||||
|
} else if (message->type == ES_MSG_ANIMATE) {
|
||||||
|
message->animate.complete = false;
|
||||||
|
message->animate.waitMs = 16;
|
||||||
|
timeDeltaMs += message->animate.deltaMs;
|
||||||
|
bool needsRedraw = reqdraw;
|
||||||
|
|
||||||
|
while (timeDeltaMs > 16) {
|
||||||
|
uxn_eval(&u, peek16(devscreen->dat, 0));
|
||||||
|
if(devsystem->dat[0xe]) needsRedraw = true;
|
||||||
|
timeDeltaMs -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsRedraw) {
|
||||||
|
if (imageWidth != ppu.width || imageHeight != ppu.height) {
|
||||||
|
imageWidth = ppu.width, imageHeight = ppu.height;
|
||||||
|
imageBits = (uint32_t *) EsHeapReallocate(imageBits, imageWidth * imageHeight * 4, false, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t y = 0; y < ppu.height; y++) {
|
||||||
|
for (uint16_t x = 0; x < ppu.width; x++) {
|
||||||
|
imageBits[x + y * ppu.width] = palette[get_pixel(x, y)] | 0xFF000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EsRectangle imageBounds = GetImageBounds(EsElementGetInsetBounds(element));
|
||||||
|
EsElementRepaint(element, &imageBounds);
|
||||||
|
reqdraw = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ES_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _start() {
|
||||||
|
while (true) {
|
||||||
|
EsMessage *message = EsMessageReceive();
|
||||||
|
|
||||||
|
if (message->type == ES_MSG_INSTANCE_CREATE) {
|
||||||
|
EsInstance *instance = EsInstanceCreate(message, "Uxn Emulator", -1);
|
||||||
|
canvas = EsCustomElementCreate(instance->window, ES_CELL_FILL, ES_STYLE_PANEL_WINDOW_DIVIDER);
|
||||||
|
canvas->messageUser = (EsUICallback) CanvasMessage;
|
||||||
|
EsElementStartAnimating(canvas);
|
||||||
|
} else if (message->type == ES_MSG_INSTANCE_OPEN) {
|
||||||
|
size_t romBytes;
|
||||||
|
void *rom = EsFileStoreReadAll(message->instanceOpen.file, &romBytes);
|
||||||
|
EsInstanceOpenComplete(message, rom && Launch(rom, romBytes), NULL, 0);
|
||||||
|
EsHeapFree(rom, 0, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
[general]
|
||||||
|
name=Uxn Emulator
|
||||||
|
|
||||||
|
[build]
|
||||||
|
source=ports/uxn/emulator.c
|
||||||
|
compile_flags=-Wno-unknown-pragmas -Wno-unused-parameter
|
||||||
|
|
||||||
|
[@handler]
|
||||||
|
extension=uxn
|
||||||
|
action=open
|
||||||
|
|
||||||
|
[@file_type]
|
||||||
|
extension=uxn
|
||||||
|
name=Uxn ROM
|
||||||
|
icon=icon_unknown
|
|
@ -0,0 +1,8 @@
|
||||||
|
set -e
|
||||||
|
rm -rf bin/uxn bin/noodle
|
||||||
|
git clone --depth=1 https://git.sr.ht/~rabbits/uxn bin/uxn
|
||||||
|
git clone --depth=1 https://git.sr.ht/~rabbits/noodle bin/noodle
|
||||||
|
cc -DNDEBUG -Os -g0 -s bin/uxn/src/uxnasm.c -o bin/uxnasm
|
||||||
|
bin/uxnasm bin/noodle/src/main.tal bin/noodle.rom
|
||||||
|
echo > bin/uxn/src/devices/ppu.h
|
||||||
|
x86_64-essence-gcc -DNDEBUG -Os -g0 -s ports/uxn/emulator.c -ffreestanding -nostdlib -lgcc -z max-page-size=0x1000 -o bin/uxnemu
|
|
@ -460,3 +460,4 @@ EsTextboxEnableSmartQuotes=458
|
||||||
EsBufferWriteInt8=459
|
EsBufferWriteInt8=459
|
||||||
EsInstanceGetStartupRequest=460
|
EsInstanceGetStartupRequest=460
|
||||||
EsImageDisplayPaint=461
|
EsImageDisplayPaint=461
|
||||||
|
EsElementGetInsetBounds=462
|
||||||
|
|
Loading…
Reference in New Issue