Created
May 25, 2025 21:16
-
-
Save charlesastaylor/6df1edccbbfa16a06e30f398d03d36dc to your computer and use it in GitHub Desktop.
A silly example of using inline arm assembler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Do some drawing with assembly! A small host program, that sets up graphics and memory then calls update_and_render | |
// which is implemented with inline assembly to do whatever it's heart desires. | |
// | |
// @TODO(Charles): Do something more interesting than the scrolling noise! | |
#if IS_CROSS_COMPILING { | |
#run,host { | |
#import "Basic"; | |
#import "Arm_Assembler"; | |
} | |
} | |
update_and_render :: no_inline (frame_buffer: *Frame_Buffer, memory: *u8) { | |
// x0: context | |
// x1: frame_buffer | |
// x2: memory | |
// x0-x15 should be volatile, but maybe better to be safe and not touch x0,x1. | |
#insert #run,host -> string { | |
return assemble_arm64(#string ARM64 | |
mov x14, x2 // @Hack I only added the memory param after I was already using w2 register! Just put it somewhere else. | |
ldr w2, [x1] // width | |
ldr w3, [x1, #4] // height | |
ldr x4, [x1, #8] | |
// Load our color | |
ldr w6, [x14] | |
mov x8, 0 // j | |
outer: | |
mov x9, 0 // i | |
// Modify the color on every row for some whack output | |
add w6, w6, 1 | |
orr w6, w6, 0xFF000000 // But make sure we are never transparent! | |
inner: | |
// x10 = 4(width * j + i). | |
mul x10, x8, x2 | |
add x10, x10, x9 | |
str w6, [x4, x10, lsl 2] | |
add x9, x9, 1 | |
cmp x9, x2 | |
blt inner | |
add x8, x8, 1 | |
cmp x8, x3 | |
blt outer | |
str w6, [x14] | |
ARM64); | |
}; | |
} | |
// | |
// "Host" program. | |
// | |
my_window: Window_Type; | |
window_width, window_height: s32; | |
// NOTE(Charles): I'm using Simp (and so opengl) to render to screen. But maybe it would be better to do the software_render.jai | |
// thing? Could actually somewhat reasonably make the entire program assembly at that point... | |
Frame_Buffer :: struct { | |
width, height: s32; | |
memory: *u8; | |
} | |
main :: () { | |
window_width = 1280; | |
window_height = 720; | |
my_window = create_window(window_width, window_height, "Game"); | |
set_render_target(my_window, .LEFT_HANDED); | |
window_width, window_height = get_render_dimensions(my_window); | |
BYTES_PER_PIXEL ::4; | |
WIDTH :: 600; | |
HEIGHT :: 600; | |
screen_bitmap: Bitmap; | |
screen_texture: Texture; | |
bitmap_alloc(*screen_bitmap, WIDTH, HEIGHT, .RGBA8); | |
frame_buffer: Frame_Buffer; | |
{ | |
using frame_buffer; | |
width = 600; | |
height = 600; | |
memory = screen_bitmap.data.data; | |
} | |
// When assembly my x64 code to a library I stored everything in data section. That's not really possible while | |
// using inline assembly, as we only extract the text section. You can embed constant data in code and branch over it | |
// but the memory will be read only afaict. So instead, just allocated some memory for the assembly to use as it | |
// pleases! | |
game_memory := cast(*u8, alloc(1024)); // 1Kb is enough memory for anyone! | |
memset(game_memory, 0, 1024); | |
// cast(*u32, game_memory).* = 0xFF00FF00; | |
quit := false; | |
while !quit { | |
update_window_events(); | |
for get_window_resizes() { | |
update_window(it.window); | |
if it.window == my_window { | |
should_reinit := (it.width != window_width) || (it.height != window_height); | |
window_width = it.width; | |
window_height = it.height; | |
} | |
} | |
for events_this_frame { | |
if it.type == .QUIT then quit = true; | |
} | |
update_and_render(*frame_buffer, game_memory); | |
texture_updated := texture_load_from_bitmap(*screen_texture, *screen_bitmap); | |
assert(texture_updated); | |
clear_render_target(0.12, 0.12, 0.12, 1); | |
{ | |
set_shader_for_images(*screen_texture); | |
buffer_ratio := cast(float) screen_bitmap.width / screen_bitmap.height; | |
window_ratio := cast(float) window_width / window_height; | |
x, y, width, height: float; | |
if buffer_ratio > window_ratio { | |
// window is tall and skinny | |
width = xx window_width; | |
height = width / buffer_ratio; | |
x = 0; | |
y = (window_height - height) / 2.; | |
} else { | |
height = xx window_height; | |
width = height * buffer_ratio; | |
x = (window_width - width) / 2.; | |
y = 0; | |
} | |
// @TODO: This will stretch and blur our image, if want to maintain pixels need to set the gl | |
// filter or whatever it is. | |
immediate_quad(x, y, x + width, y + height, .{1, 1, 1, 1}); | |
} | |
swap_buffers(my_window); | |
reset_temporary_storage(); | |
sleep_milliseconds(10); | |
} | |
} | |
#if OS == .ANDROID #import "Android"()(main, USE_GLUE_NATIVE_PORT = true); | |
#import "Basic"; | |
#import "Simp"; | |
#import "Input"; // Have to do input events for android to not complain about app not responding... | |
#import "Window_Creation"; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment