Globals and start.asm #19
@@ -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
|
||||
<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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -1,18 +1,28 @@
|
||||
; Addresses of global variables
|
||||
|
Gelthor marked this conversation as resolved
PleegWat
commented
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
PleegWat
commented
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
|
||||
|
||||
@@ -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
PleegWat
commented
Since this function lives in 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.
|
||||
|
PleegWat
commented
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.
Gelthor
commented
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
Mutex
commented
The status is returned in 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
PleegWat
commented
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
PleegWat
commented
There is a bit trick for this: There is a bit trick for this: `!(v & (v - 1))`. Though that does not yield the log2.
Gelthor
commented
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
PleegWat
commented
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
PleegWat
commented
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
PleegWat
commented
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
|
||||
@@ -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
|
||||
Which errors can occur (if any) should probably have its own section here, probably called "Errors"