Globals and start.asm #19

Merged
ShatteredMINT merged 10 commits from Gelthor/symphony_stdlib:graphics into main 2026-09-06 20:13:07 +02:00
6 changed files with 730 additions and 14 deletions
Showing only changes of commit 46abf8d893 - Show all commits
+12 -1
View File
@@ -16,10 +16,21 @@ All functions in the standard library should follow the following outline:
; Arguments: <which register contains what argument>
; Result: <what is the result, and where is it stored>
; Clobbers: <list of registers that are clobbered>
; Globals: <list of globals are accessed. OPTIONAL>
Gelthor marked this conversation as resolved
Review

Which errors can occur (if any) should probably have its own section here, probably called "Errors"

Which errors can occur (if any) should probably have its own section here, probably called "Errors"
<label>: <;SHOULD BE INLINED>
<CODE>
```
Functions should be in the appropriate asm file, if you are unsure where functionality fits make a seperate file and ask in the pull request
Functions that are provided for convenience/reference but should be inlined in production code should be marked with `;SHOULD BE INLINED` after their label
Functions that are provided for convenience/reference but should be inlined in production code should be marked with `;SHOULD BE INLINED` after their label.
If the function returns a status code in flags, the preamble should list all it might return.
## Globals
Any function that uses global variables should document this in the preamble comment, see above.
A file/module should check on intialisation that the address `globals.MAGIC_ADDRESS` contains the 16 bit value `globals.MAGIC_VALUE`, to ensure that the user has properly included [[src/start.asm]] and reservered the global variable area.
No opinion is offered on whether modules can assume globals variables are initialised to zero.
+7 -2
View File
@@ -3,7 +3,7 @@
This is a standard library for symphony.
It is both intended as a practical toolkit to develop more complex software as well as a teaching resource.
If you just want to use the standard library [[src/stdlib.asm]] is your main header, include it after your code.
If you just want to use the standard library [[src/stdlib.asm]] is your main header, include it after your code. Some modules also require [[src/start.asm]] at the very start of the program.
If you are using it as a learning resource have a look at the [teaching folder](teaching).
@@ -20,7 +20,7 @@ If you are intersted in contributing have a look at [[CONTRIBUTING.md]]
| preserved | sp, r8 - r12 |
| scratch | flags, r1 - r7 |
| arguments | r1 - r7 |
| result | r1-r7 |
| result | flags, r1 - r7 |
| return address | r13 |
Arguments not fitting into the 7 registers should be passed on the top of the stack, meaning they should be the last values pushed before the function call.
@@ -32,9 +32,14 @@ Should a function return more values than fit into the 7 registers, the caller h
This reduces the amount of argument registers to 6 and all arguments above that shall go on the stack, the pointer to the result stack shall **always** be passed in an argument register.
This register points at the highest available address for results, with the 8th result being stored there, the 9th below it and so on.
Some functions return a success/failure status code in flags. A set low bit will indicate some error condition, the function may return more than one possible value to report different errors. A flags value of 0 is set on success. No other even values are used. This can be checked with `je` or `jne` immediatly on return to the caller. All other functions clobber flags.
### Stack
Grows downwards from 0xXXFE_0000 (so top of memory -0x1_0000).
### Globals
Global variables are stored near the bottom of RAM in the address range 0x0010..0x0100. Programs should `include src/start` as the first line before their own code and before other includes, or otherwise reserve this space, the first instruction should be a jump to user code.
### Types
#### String
+3
View File
@@ -19,6 +19,9 @@ pub const OK = 0x0000
pub const SCREEN_INVALID_MODE = 0x0001
pub const SCREEN_INVALID_WIDTH = 0x0003
pub const SCREEN_FB_TOO_SMALL = 0x0005
pub const SCREEN_OUTSIDE_FB = 0x0007
pub const MAGIC_BAD = 0x8001
; Extended error codes, these would require loading a 32 bit value.
pub const EXT_OK = 0x00000000
+17 -7
View File
@@ -1,18 +1,28 @@
; Addresses of global variables
Gelthor marked this conversation as resolved
Review

I'd personally rather see reserved addresses put in hex, but I don't have a good reasoning for that.

I'd personally rather see reserved addresses put in hex, but I don't have a good reasoning for that.
; If not double buffering these point to the same buffer
; If double buffering they must the same size
pub const FB_DISPLAY_PTR = 16 ; U32 Currently displayed buffer
pub const FB_DRAW_PTR = 20 ; U32 Draw to this buffer
pub const FRAME_BUFFER_SIZE = 24 ; U32
pub const PIXEL_WIDTH = 28 ; U16
pub const PIXEL_HEIGHT = 30 ; U16
pub const FB_DISPLAY_STRIDE = 20 ; U32 Bytes in each row of the displayed buffer
pub const FB_DRAW_PTR = 24 ; U32 Draw to this buffer
pub const FB_DRAW_STRIDE = 28 ; U32 Bytes in each row of the draw buffer
pub const FB_SIZE_BYTE = 32 ; U32 In bytes
pub const FB_WIDTH_PX = 36 ; U16 Width of screen in pixels
pub const FB_HEIGHT_PX = 38 ; U16 Height of screen in pixels
; Log2 of width in pixels, e.g.
; * 10 => 1024 * 768
pub const LOG_WIDTH = 32 ; U8
; * 8 => 256 * 192
pub const FB_LOG_WIDTH = 40 ; U8
pub const FB_LOG_STRIDE = 41 ; U8 Log2 of FB_xxx_STRIDE in bytes
pub const BYTES_PER_PIXEL = 33 ; U8
pub const FB_BYTES_PER_PIXEL = 42 ; U8 Specialisations should hardcode this
; Log2 of bytes per pixel
; * 0 => 8 bits per pixel
; * 2 => 32 bits per pixel
pub const LOG_BPP = 34 ; U8
pub const FB_LOG_BPP = 43 ; U8 Specialisations should hardcode this
pub const MAGIC_ADDRESS_LONG = 12 ; U32 Location of the magic value
Gelthor marked this conversation as resolved Outdated
Outdated
Review

If we do not declare these addresses in order, I fear we will accidentally introduce double use of an address at some point.

If we do not declare these addresses in order, I fear we will accidentally introduce double use of an address at some point.
pub const MAGIC_VALUE_LONG = 0xb301534c ; U32 Full 32 bits of the magic value
pub const MAGIC_ADDRESS = 14 ; U16 Location of the low 16 bits of magic value
pub const MAGIC_VALUE = 0x534c ; U16 Low 16 bits of the magic value
+672
View File
@@ -0,0 +1,672 @@
; 32 bit graphics library for Symphony
include globals
include errno
const SCREEN_SETTING_MODE = 0 ; Use zr instead
const SCREEN_SETTING_DATA_OFFSET = 1
const SCREEN_SETTING_RESOLUTION = 2
const SCREEN_SETTING_FOREGROUND_COLOR = 2
const SCREEN_SETTING_BACKGROUND_COLOR = 3
const SCREEN_SETTING_FONT = 4
const SCREEN_MODE_TEXT_8 = 0
const SCREEN_MODE_TEXT_32 = 1
const SCREEN_MODE_GRAPHICS_8 = 2
const SCREEN_MODE_GRAPHICS_32 = 3
pub set_graphics_mode:
; u16 set_graphics_mode(void *FB_ptr, size_t FB_size, u8 bit_per_pixel, u16 width, bool clear, bool double_buf)
; Sets the screen to graphics mode, the resolution, and colour depth.
; Optionally clears the screen.
;
; Arguments:
; - r1: Point to allocated framebuffer
; - r2: Size of FB allocation, in bytes
; - r3: Bits per pixel: One of `8` or `32`
Gelthor marked this conversation as resolved Outdated
Outdated
Review

Since this function lives in graphics_32, I'd expect that to imply 32 bits per pixel. If the same function is intended to be used for both modes, it should live in a generic graphics file.

Since this function lives in `graphics_32`, I'd expect that to imply 32 bits per pixel. If the same function is intended to be used for both modes, it should live in a generic graphics file.
; - r4: Width - height is set to `width / 4 * 3`
; - r5: If non-zero, clears the current framebuffer.
Outdated
Review

If you are worried about register pressure, rather than having 2 bool arguments like this you could have a single u32 argument holding a bitwise OR of flags.

If you are worried about register pressure, rather than having 2 bool arguments like this you could have a single u32 argument holding a bitwise OR of flags.
Outdated
Review

I'm not sure register pressure is important for a rarely called initialisation function.

I'm not sure register pressure is important for a rarely called initialisation function.
; - r6: If non-zero, enable double buffering
; Returns:
; - r1: Status
Gelthor marked this conversation as resolved Outdated
Outdated
Review

The status is returned in flags, not r1. Errors should probably have their own dedicated section here

The status is returned in `flags`, not `r1`. Errors should probably have their own dedicated section here
; * 0 OK
; * SCREEN_INVALID_SIZE width invalid
; * SCREEN_INVALID_MODE bits_per_pixel invalid
; * SCREEN_FB_TOO_SMALL allocation too small
; Clobbers:
; - r2-r6, flags
; Globals written:
; - LOG_STRIDE
; - FB_WIDTH
; - FB_HEIGHT
; - BITS_PER_PIXEL
; Check the magic number
load_16 flags, [globals.MAGIC_ADDRESS]
cmp flags, globals.MAGIC_VALUE
je set_graphics_mode_magic_ok
mov flags, errno.MAGIC_BAD
jmp r13 ; return
set_graphics_mode_magic_ok:
push r8 ; LOG Bytes/pixel
push r9 ; Log width
push r10 ; Height
push r11 ; Size of FB in bytes/pixels
cmp r3, 32
jne not_32_bit_mode
mov r3, SCREEN_MODE_GRAPHICS_32
mov r8, 2
jmp mode_ok
not_32_bit_mode:
cmp r3, 8
jne invalid_mode
mov r3, SCREEN_MODE_GRAPHICS_8
mov r8, 0
jmp mode_ok
invalid_mode:
Gelthor marked this conversation as resolved Outdated
Outdated
Review

I'd recommend centralizing the function epilogue. Otherwise, if a future refactor changes which registers need to be saved and one of the return sites is missed that could lead to nasty errors.

I'd recommend centralizing the function epilogue. Otherwise, if a future refactor changes which registers need to be saved and one of the return sites is missed that could lead to nasty errors.
pop r11
pop r10
pop r9
pop r8
mov flags, errno.SCREEN_INVALID_MODE
jmp r13 ; return
mode_ok:
; Width must be between 4 and 1024
cmp r4, 1024 ; max width of screen
ja width_invalid
cmp r4, 4
jb width_invalid
; Is width a power of two?
Gelthor marked this conversation as resolved Outdated
Outdated
Review

There is a bit trick for this: !(v & (v - 1)). Though that does not yield the log2.

There is a bit trick for this: `!(v & (v - 1))`. Though that does not yield the log2.
Outdated
Review

Fixed the comment to describe what the code actually does.

Fixed the comment to describe what the code actually does.
mov r9, 10
mov r10, 1024
log_width_loop:
cmp r4, r10
je width_ok
lsr r10, r10, 1
sub r9, r9, 1
cmp r9, 4
jae log_width_loop
width_invalid:
pop r11
pop r10
pop r9
pop r8
mov flags, errno.SCREEN_INVALID_WIDTH
jmp r13 ; return
width_ok:
; Now r9 = log width
; height = width/4 * 3
lsr r4, r4, 2
lsl r10, r4, 1
add r10, r10, r4 ; height
; Compute size of FB
lsl r11, r10, r9 ; Size of FB in pixels
lsl r11, r11, r8 ; Size of FB in bytes
; If double buffering..
cmp r6, 0
je single_buff
; ..we need twice the space
lsl r11, r11, 1
single_buff:
cmp r2, r11
jae size_ok
pop r11
pop r10
pop r9
pop r8
mov flags, errno.SCREEN_FB_TOO_SMALL
jmp r13 ; return
size_ok:
; Set screen mode
screen zr, r3
; Set screen size
mov flags, SCREEN_SETTING_RESOLUTION
; Setting is width/4 - 1
sub r4, r4, 1
screen flags, r4
; Restore width
add r4, r4, 1
lsl r4, r4, 2
; Set FB pointer
; If double buffering..
cmp r6, 0
je save_fb_single_buff
; ..we want the size of each FB
lsr r11, r11, 1
store_32 [globals.FB_SIZE_BYTE], r11
store_32 [globals.FB_DISPLAY_PTR], r2
mov r3, SCREEN_SETTING_DATA_OFFSET
screen r3, r2
add r2, r2, r11
jmp save_fb_done
save_fb_single_buff:
store_32 [globals.FB_SIZE_BYTE], r11
store_32 [globals.FB_DISPLAY_PTR], r2
mov r3, SCREEN_SETTING_DATA_OFFSET
screen r3, r2
save_fb_done:
store_32 [globals.FB_DRAW_PTR], r2
store_16 [globals.FB_WIDTH_PX], r4
store_16 [globals.FB_HEIGHT_PX], r10
store_8 [globals.FB_LOG_WIDTH], r9
store_8 [globals.FB_LOG_BPP], r8
mov flags, 1
lsl r8, flags, r8
store_8 [globals.FB_BYTES_PER_PIXEL], r8
mov r1, r2 ; Return draw FB
mov r3, 0
mov r4, 0
; TODO call clear
nor r2, zr, 0xff ; Colour = White (32-bit)
pop r11
pop r10
pop r9
pop r8
mov flags, 0 ; OK
jmp r13 ; return
pub set_text_mode:
Gelthor marked this conversation as resolved Outdated
Outdated
Review

I think I'd rather see graphics mode and text mode code in separate files.

I think I'd rather see graphics mode and text mode code in separate files.
; u16 set_text_mode(void *FB_ptr, size_t FB_size, u8 bit_per_pixel, u8 font, bool clear, bool double_buf)
; Sets the screen to text mode, the font, and colour depth. Also sets fore ground and background colors.
; Optionally clears the screen.
; Arguments:
; - r1: Address of allocated framebuffer
; - r2: Size of FB allocation, in bytes
; - r3: Bits per pixel: One of `8` or `32`
; - r4: Font, one of {0,1,2,3}
; - r5: If non-zero, clears the current framebuffer.
; - r6: If non-zero, enable double buffering
; Returns:
; - r1: Status
; * 0 OK
; * SCREEN_INVALID_SIZE width invalid
; * SCREEN_INVALID_MODE bits_per_pixel invalid
; * SCREEN_FB_TOO_SMALL allocation too small
; Clobbers:
; - r2-r6, flags
; Globals written:
; - LOG_STRIDE
; - FB_WIDTH
; - FB_HEIGHT
; - BITS_PER_PIXEL
; Check the magic number
load_16 flags, [globals.MAGIC_ADDRESS]
cmp flags, globals.MAGIC_VALUE
je set_text_mode_magic_ok
mov flags, errno.MAGIC_BAD
jmp r13 ; return
set_text_mode_magic_ok:
;TODO: Sanity checks
;TODO: ?Clear screen
push r8 ; LOG Bytes/pixel
push r9 ; Log width
push r10 ; Height
push r11 ; Size of FB in bytes/pixels
cmp r3, 32
jne not_32_bit_text_mode
mov r3, SCREEN_MODE_TEXT_32
mov r8, 2
jmp text_mode_ok
not_32_bit_text_mode:
cmp r3, 8
jne invalid_text_mode
mov r3, SCREEN_MODE_TEXT_8
mov r8, 0
jmp text_mode_ok
invalid_text_mode:
pop r11
pop r10
pop r9
pop r8
mov flags, errno.SCREEN_INVALID_MODE
jmp r13 ; return
text_mode_ok:
; Set screen mode
screen zr, r3
; Set FB pointer
; If double buffering..
cmp r6, 0
je save_fb_single_text_buff
; ..we want the size of each FB
lsr r11, r11, 1
store_32 [globals.FB_SIZE_BYTE], r11
store_32 [globals.FB_DISPLAY_PTR], r2
mov r3, SCREEN_SETTING_DATA_OFFSET
screen r3, r2
add r2, r2, r11
jmp save_fb_text_done
save_fb_single_text_buff:
store_32 [globals.FB_SIZE_BYTE], r11
store_32 [globals.FB_DISPLAY_PTR], r2
mov r3, SCREEN_SETTING_DATA_OFFSET
screen r3, r2
save_fb_text_done:
store_32 [globals.FB_DRAW_PTR], r2
; Set screen font
; Only 0, 1, 2, 3 are valid
and r4, r4, 3
mov flags, SCREEN_SETTING_FONT
screen flags, r4
; Set foreground to white
mov flags, SCREEN_SETTING_FOREGROUND_COLOR
nor r4, zr, 0xff ; White
screen flags, r4
; Set background to black
mov flags, SCREEN_SETTING_BACKGROUND_COLOR
screen flags, zr
pop r11
pop r10
pop r9
pop r8
mov flags, 0 ; OK
jmp r13 ; return
pub set_text_mode_default:
; u16 set_text_mode_default(void *FB_ptr)
; Sets the screen to default text mode, the font, and colour depth. Also sets fore ground and background colors.
; Does not clear the screen.
; Size of allcation is not checked
;
; Arguments:
; - r1: Address of allocated framebuffer
; Returns:
; - flags: 0 ; ok, never fails
; Clobbers:
; - r1, flags
; Globals written:
; - FB_DISPLAY_PTR
; - FB_DRAW_PTR
; - FRAME_BUFFER_SIZE
; Set screen mode to default 8 bit text
screen zr, zr
; Set FB pointer
store_32 [globals.FB_DISPLAY_PTR], r1
store_32 [globals.FB_DRAW_PTR], r1
; Size is 96*40*1 byte
mov r1, 3840
store_32 [globals.FB_SIZE_BYTE], r1
mov flags, SCREEN_SETTING_DATA_OFFSET
screen flags, r1
; Set default screen font
mov flags, SCREEN_SETTING_FONT
screen flags, zr
; Set foreground to white
mov flags, SCREEN_SETTING_FOREGROUND_COLOR
nor r1, zr, 0xff ; White
screen flags, r1
; Set background to black
mov flags, SCREEN_SETTING_BACKGROUND_COLOR
screen flags, zr
mov flags, 0 ; OK
jmp r13
pub clear:
; void clear()
; Paints the whole screen black
;
; Arguments:
; - None
; Clobbers:
; - r1: Color
; - r2: fill_screen
; - r3: fill_screen
; - r4: fill_screen
; - flags
mov r1, 0 ; Black
; Fall through
pub fill_screen:
; void fill_screen(u32 color)
; Fills the whole screen in a single color.
;
; Arguments:
; - r1: Color, 32 bit RGB0
; Clobbers:
; - r2: Pixel pointer
; - r3: End address
; - r4: Stride
; - flags
; Reads globals:
; - FB_DRAW_PTR
; - LOG_BPP
; - LOG_WIDTH
; - PIXEL_HEIGHT
load_8 r2, [globals.FB_LOG_WIDTH]
load_16 r3, [globals.FB_HEIGHT_PX]
lsl r3, r3, r2
load_8 r4, [globals.FB_LOG_BPP]
lsl r3, r3, r4 ; Num bytes in FB
load_32 r2, [globals.FB_DRAW_PTR]
add r3, r3, r2 ; End of FB
load_8 r4, [globals.FB_BYTES_PER_PIXEL]
fill_screen_loop:
; Screen is always a multiple of 4*3
; we can unroll this 12 times.
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
store_32 [r2], r1
add r2, r2, r4
cmp r2, r3
jb fill_screen_loop
jmp r13 ; return
pub switch_buffers: ; SHOULD BE INLINED
; void switch_buffers()
; Swaps the display and draw buffers, and updates the screen to use the new display buffer.
;; Safe to run when single buffered.
;
; Arguments:
; - None
; Returns:
; - None
; Clobbers:
; - r1: Old display FB => new draw FB
Gelthor marked this conversation as resolved Outdated
Outdated
Review

Should we document these as returns instead? If any caller is writing to the buffer directly, they could then rely on this return and skip the extra load.

Should we document these as returns instead? If any caller is writing to the buffer directly, they could then rely on this return and skip the extra load.
; - r2: Old draw FB => new display FB
; - flags
; Globals written:
; - FB_DISPLAY_PTR
; - FB_DRAW_PTR
load_32 r1, [globals.FB_DISPLAY_PTR]
load_32 r2, [globals.FB_DRAW_PTR]
mov flags, SCREEN_SETTING_DATA_OFFSET
screen flags, r2
store_32 [globals.FB_DISPLAY_PTR], r2
store_32 [globals.FB_DRAW_PTR], r1
jmp r13 ; return
pub draw_pixel:
; void draw_pixel(u32 color, u16 x, u16 y)
; Draw pixel at the position, no checks on position, can potentally write to any memory.
; Framebuffer pointer is stored in FB_PTR.
;
; Arguments:
; - r1 Color 24 bit RGB0 PRESERVED
Gelthor marked this conversation as resolved Outdated
Outdated
Review

The function code does not match this argument list

The function code does not match this argument list
; - r2 X coord PRESERVED
; - r3 Y coord PRESERVED
; - r4-r7 ignored but PRESERVED
; Result:
; - None
; Clobbers:
; - flags
; Globals read:
; - FB_DRAW_PTR
; - LOG_WIDTH
; Screen is `1 << WIDTH pixels wide
load_8 flags, [globals.FB_LOG_WIDTH]
; Compute pixel address
lsl flags, r4, flags ; Y * width
add flags, flags, r3 ; Y * width + X
lsl flags, flags, 2 ; pixel to address
load_32 r1, [globals.FB_DRAW_PTR]
add flags, flags, r1 ; += frambuffer_ptr
; Set pixel value
store_32 [flags], r2
jmp r13 ; return
pub draw_line_h:
; void draw_line_h(u32 color, u16 x0, u16 y, u16 x1)
; Draws a straight horizontal line from (x0, y) to (x1, y) (inclusive) using `color`
; on to the current `FB_DRAW_PTR` buffer.
;
; Arguments:
; - r1: Color 24 bit RGB0 PRESERVED
; - r2: X0 coord
; - r3: Y coord PRESERVED
; - r4: X1 coord
; Returns:
; - r1: Color 24 bit RGB0 PRESERVED
; - r2: X1 coord
; - r3: Y coord PRESERVED
; Clobbers:
; - r7, flags
push r13
; if x0 > x1
; dx = -1
; else
; dx = +1
; endif
cmp r4, r2
or r7, flags, 0x02
lsl r7, r7, 29
asr r7, r7, 30
jmp draw_line_h_loop_in
draw_line_h_loop:
add r2, r2, r7
draw_line_h_loop_in:
qcall draw_pixel
cmp r2, r4
jne draw_line_h_loop
pop r13
jmp r13 ; return
pub draw_line_v:
; void draw_line_v(u32 color, u16 x, u16 _, u16 y0, u16 x1)
; Draws a straight verticle line from (x, y0) to (x, y1) (inclusive) using `color`
; on to the current `FB_DRAW_PTR` buffer.
;
; Arguments:
; - r1: Color 24 bit RGB0 PRESERVED
; - r2: X coord PRESERVED
; - r3: Y0 coord
; - r4: ignored but PRESERVED
; - r5: Y1 coord
; Returns:
; - r1: Color 24 bit RGB0 PRESERVED
; - r2: X coord PRESERVED
; - r3: Y1 coord
; Clobbers:
; - r7, flags
push r13
; if y0 > y1
; dy = -1
; else
; dy = +1
; endif
cmp r5, r3
or r7, flags, 0x02
lsl r7, r7, 29
asr r7, r7, 30
jmp draw_line_v_loop_in
draw_line_v_loop:
add r3, r3, r7
draw_line_v_loop_in:
qcall draw_pixel
cmp r3, r5
jne draw_line_v_loop
pop r13
jmp r13 ; return
pub draw_line:
; void draw_line(u32 color, u16 x0, u16 y0, u16 x1, u16 y1)
; Draws a straight line from (x0, y0) to (x1, y1) (inclusive) using `color`
; on to the current `FB_DRAW_PTR` buffer.
;
; Arguments:
; - r1: Color 24 bit RGB0 PRESERVED
; - r2: X0 coord
; - r3: Y0 coord
; - r4: X1 coord
; - r5: Y1 coord
; Returns:
; - r1: Color 24 bit RGB0 PRESERVED
; - r2: X1 coord
; - r3: Y1 coord
; Clobbers:
; - r4-r7, flags
; Globals read:
; - FB_DRAW_PTR
; - FB_DRAW_STRIDE
; - ...others I expect
; push r11 ; sy
; push r10 ; dy
; push r9 ; sx
; push r8 ; dx
; int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
cmp r2, r4
ja draw_line_move_left
jb draw_line_move_right
; if x0 == x1
jmp draw_line_v ; tail call
; elif x0 < x1
draw_line_move_right:
push r8 ; dx
push r9 ; sx
sub r8, r4, r2
mov r9, 1
jmp draw_line_move_right_done
; else x0 > x1
draw_line_move_left:
push r8 ; dx
push r9 ; sx
sub r8, r2, r4
nor r9, zr, zr ; sx = -1
; endif
draw_line_move_right_done:
; int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
cmp r3, r5
jb draw_line_move_down
ja draw_line_move_up
; if y0 == y0
; Restore r8, r9
pop r9
pop r8
jmp draw_line_h ; tail call
; elif y0 < y1
draw_line_move_down:
push r10 ; dy
push r11 ; sy
sub r10, r3, r5
mov r11, 1
jmp draw_line_move_down_done
; else y0 > y1
draw_line_move_up:
push r10 ; dy
push r11 ; sy
sub r10, r5, r3
nor r11, zr, zr ; sy = -1
; endif
draw_line_move_down_done:
push r12 ; err, err2
push r13 ; lr
; int err = dx+dy /* error value e_xy */
add r12, r8, r10
; loop while(true)
draw_line_loop:
qcall draw_pixel
; err2 = 2*err;
lsl r12, r12, 1
; if (err2 >= dy) { /* e_xy+e_x > 0 */
cmp r12, r10
lsr r12, r12, 1 ; restore err
jl draw_line_loop_skip_x
; if (x0 == x1) break;
cmp r2, r4
je draw_line_loop_done
; err += dy
add r12, r12, r10
; x0 += sx;
add r2, r2, r9
draw_line_loop_skip_x:
; err2 = 2*err;
lsl r12, r12, 1
; if (err2 <= dx) { /* e_xy+e_y < 0 */
cmp r12, r8
lsl r12, r12, 1 ; restore err
jl draw_line_loop_skip_y
; if (y0 == y1) break;
cmp r3, r5
je draw_line_loop_done
; err += dx
add r12, r12, r8
; y0 += sy
add r3, r3, r11
draw_line_loop_skip_y:
jmp draw_line_loop
; endloop
draw_line_loop_done:
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
jmp r13 ; return
+19 -4
View File
@@ -1,4 +1,19 @@
@0 ; Reserve space for globals
jmp 0x100 ; jump over them
@0x100 ; start of user code
;@0 ; Reserve space for globals
; First initialise the stack pointer
nor sp, zr, 0xffff
; Jump over the globals to user code
jmp 0x100
U32 0 ; 4 bytes
; MAGIC_VALUE
;@0x0c
U32 0xb301534c
; Pad with zeroes since the @addr feature is currently broken.
; * https://discord.com/channels/828292123936948244/1545010596246847568
;
U1920 0 ; 240 bytes
;@0x100 ; start of user code