.{memory, math} := @use("../../../libraries/stn/src/lib.hb"); .{ColorBGRA, blend} := @use("rel:color.hb") FB_WIDTH := 1024 FB_HEIGHT := 768 FB_PIXELS := FB_WIDTH * FB_HEIGHT FB_BYTES := FB_PIXELS << 2 // actual enforced max copy size is 0xFFFF, but this was faster MAX_COPY_SIZE := 0x1800 COPY_PIXELS := math.min(MAX_COPY_SIZE, FB_BYTES) >> 2 PARTITIONS := FB_PIXELS / COPY_PIXELS TOTAL_PAGES := 1 + FB_BYTES >> 12 Buffer := struct {write: ^ColorBGRA, copy: ^[ColorBGRA; COPY_PIXELS]} Point := struct {x: int, y: int} Transform := struct {width: int, height: int} front_buffer_ptr := @as(^ColorBGRA, @bitcast(0xFFFF8000C0000000)) front_buffer_copy := @as(^[ColorBGRA; COPY_PIXELS], @bitcast(front_buffer_ptr)) get_front_buffer := fn(): Buffer { // trying to return front_buffer_ptr or front_buffer_copy causes reg id leak buffer := Buffer.{write: front_buffer_ptr, copy: front_buffer_copy} return buffer } /* this is separate to create_raw_buffer because returning a Buffer from create_raw_buffer causes reg id leak */ create_buffer := fn(): Buffer { ptr := @inline(create_raw_buffer) buffer := Buffer.{write: ptr, copy: @as(^[ColorBGRA; COPY_PIXELS], @bitcast(ptr))} return buffer } create_raw_buffer := fn(): ^ColorBGRA { if TOTAL_PAGES <= 0xFF { return @bitcast(@inline(memory.request_page, TOTAL_PAGES)) } ptr := @inline(memory.request_page, 255) remaining := TOTAL_PAGES - 0xFF loop if remaining <= 0 break else { if remaining < 0xFF { memory.request_page(remaining) } else { memory.request_page(0xFF) } remaining -= 0xFF } return @bitcast(ptr) } // sets the buffer to the color. very fast. clear := fn(buffer: Buffer, color: ColorBGRA): void { n := 0 // write the first pixel chunk loop if n >= COPY_PIXELS break else { *(buffer.write + n) = color n += 1 } n = 1 // copy that pixel chunk through the buffer, taking advantage of memory copying loop if n >= PARTITIONS break else { *(buffer.copy + n) = *buffer.copy n += 1 } return } // only required to be called when using a back buffer. if using single-buffered rendering, do not call this. present := fn(buffer: Buffer): void { n := 0 // copy chunks of the read buffer to the front buffer loop if n >= PARTITIONS break else { *(front_buffer_copy + n) = *(buffer.copy + n) n += 1 } return } // composites the contents of buffer1 into buffer2, accounting for alpha transparency // i dont know if it works. i have not tested it. it probably doesnt work composite := fn(buffer1: Buffer, buffer2: Buffer): void { n := 0 loop if n == FB_PIXELS break else { bg := *(buffer2.write + n); *(buffer2.write + n) = blend(*(buffer1.write + n), bg) n += 1 } return } screenidx := fn(pos: Point): int { return pos.x + FB_WIDTH * pos.y }