utils¶
Types¶
- type FnPtr = proc () {.nimcall.}¶
Function pointer, used for interrupt handlers etc.
Lists¶
- type List[N: static[int], T] = object¶
A list with a max capacity.
Field
Type
A List
is an array-like container with a length field. Adding new items will increase the length, but it cannot grow beyond its maximum capacity.
Lists have predictable performance, as they can be allocated statically, unlike Nim’s built-in seq
which is heap-allocated and can grow indefinitely.
Tip
When compiling with --boundChecks:on
the game will panic when the list grows too big or an out-of-bounds access occurs. To debug this situation, see using a debugger.
Procs¶
- proc `[]`[N, T](list: var List[N,T], i: int): var T¶
Get the element at the given index (mutable version).
- proc `[]=`[N, T](list: var List[N,T], i: int, val: T)¶
Set the element at the given index to a certain value.
- template cap[N, T](list: List[N,T]): int¶
The maximum capacity of the list.
- proc add[N, T](list: var List[N,T], item: sink T)¶
Add an item to the end of the list.
- proc del[N, T](list: var List[N,T], i: int)¶
Remove an element by index, putting the last element in its place.
- proc delete[N, T](list: var List[N,T], i: int)¶
Remove an element by index, shifting all other elements down.
- proc insert[N, T](list: var List[N,T], item: sink T, i = 0.Natural)¶
Insert an item into the list at the given index, shifting later elements along to make space.
- proc clear[N, T](list: var List[N,T])¶
Empty the list.
- proc isFull[N, T](list: List[N,T]): bool¶
Returns true if the list is at its maximum capacity.
- proc contains[N, T](list: List[N,T], val: T): bool¶
Check whether a value exists in the list.
This can also be invoked using the
in
ornotin
keywords.Example:
var nums: List[10, int] # ... if 123 in nums: printf("123 is in the list.")
Unsafe procs¶
These may be useful for optimisation, but they come with caveats which make them unsafe.
- proc qcreate[N, T](list: var List[N,T]): ptr T¶
Increase the length of the list and return a pointer to the new element.
The new element is uninitialised, so be sure to completely reset it.
- proc qdel[N, T](list: var List[N,T], i: int)¶
Remove an element by index, quicker version.
The last element won’t be deinitialised, so only do this if you know there’ll be no resource leakage.
- proc qclear[N, T](list: var List[N,T])¶
Empty the list, quicker version.
Elements won’t be deinitialised, so only do this if you know there’ll be no resource leakage.
Iterators¶
- iterator items[N, T](list: List[N,T]): lent T¶
Loop over each element in the list.
- iterator mitems[N, T](list: var List[N,T]): var T¶
- iterator pairs[N, T](list: List[N,T]): tuple[key: int, val: lent T]¶
Loop over each element in the list along with its index.
- iterator mpairs[N, T](list: var List[N,T]): tuple[key: int, val: var T]¶
Memory peek & poke¶
These are like volatileLoad and volatileStore from the Nim standard library, except they work even at the top level (and have nicer names).
- proc peek[T](address: ptr T): T¶
Read a value directly from some memory location.
- proc poke[T](address: ptr T, value: T)¶
Write a value directly to a memory location.
Memory copy / fill¶
- proc memset16(dst: pointer, hw: uint16, hwcount: SomeInteger)¶
Fastfill for halfwords, analogous to memset()
Uses
memset32()
ifhwcount > 5
- Dst:
Destination address.
- Hw:
Source halfword (not address).
- Hwcount:
Number of halfwords to fill.
Note
dst
must be halfword aligned.r0
returns asdst + hwcount*2
.
- proc memcpy16(dst: pointer, src: pointer, hwcount: SomeInteger)¶
Copy for halfwords.
Uses
memcpy32()
ifhwcount > 6
andsrc
anddst
are aligned equally.- Dst:
Destination address.
- Src:
Source address.
- Hwcount:
Number of halfwords to fill.
Note
dst
andsrc
must be halfword aligned.r0
andr1
return asdst + hwcount*2
andsrc + hwcount*2
.
- proc memset32(dst: pointer, wd: uint32, wcount: SomeInteger)¶
Fast-fill by words, analogous to memset()
Like CpuFastSet(), only without the requirement of 32byte chunks and no awkward store-value-in-memory-first issue.
- Dst:
Destination address.
- Wd:
Fill word (not address).
- Wcount:
Number of words to fill.
Note
dst
must be word aligned.r0
returns asdst + wcount*4
.
- proc memcpy32(dst: pointer, src: pointer, wcount: SomeInteger)¶
Fast-copy by words.
Like
CpuFastFill
, only without the requirement of 32byte chunks- Dst:
Destination address.
- Src:
Source address.
- Wcount:
Number of words.
Note
src
anddst
must be word aligned.r0
andr1
return asdst + wcount*4
andsrc + wcount*4
.
Bit packing and duplication¶
These take a hex-value and duplicate it to all fields, like 0x88
-> 0x88888888
.
- func dup8(x: uint8): uint16¶
Duplicate a byte to form a halfword:
0x12 -> 0x1212
.
- func dup16(x: uint16): uint32¶
Duplicate a halfword to form a word:
0x1234 -> 0x12341234
.
- func quad8(x: uint8): uint32¶
Quadruple a byte to form a word:
0x12 -> 0x12121212
.
- func octup(x: uint8): uint32¶
Octuple a nybble to form a word:
0x1 -> 0x11111111
- func bytes2hword(b0, b1: uint8): uint16¶
Pack 2 bytes into a word. Little-endian order.
- func bytes2word(b0, b1, b2, b3: uint8): uint32¶
Pack 4 bytes into a word. Little-endian order.
- func hword2word(h0, h1: uint16): uint32¶
Pack 2 halfwords into a word. Little-endian order.
Math helpers¶
- func isPowerOfTwo(n: SomeInteger): bool¶
Return true if
n
is a power of two.
- func logPowerOfTwo(n: uint): uint¶
Given that
n
is a power of two, return the power.
- proc octant(x, y: cint): cuint¶
Get the octant that (
x
,y
) is in.This function divides the circle in 8 parts. The angle starts at the
y=0
line and then moves in the direction of thex=0
line. On the screen, this would be like starting at the 3 o’clock position and moving clockwise.
- proc octantRot(x0, y0: cint): cuint¶
Get the rotated octant that (
x
,y
) is in.Like
octant()
but with a twist. The 0-octant starts 22.5° earlier so that 3 o’clock falls in the middle of octant 0, instead of at its start. This can be useful for 8 directional pointing.
Random numbers¶
Uses a simple XorShift algorithm, which is adequate for most games.
Warning
Any range supplied to these procs should be less than 2^16 (65536
).
For example, the following will all give inadequate results:
rand(max=999999)
rand(fp(-300)..fp(300))
rand(Natural)
If this is a problem, you could try rand() mod n
instead (but that’s expensive and leads to an uneven distribution of numbers). In the fixed-point case, you could first get a random integer and then convert it to fixed, e.g. rand(-300..300).fp
.
- proc seed(seed: uint32)¶
Seed the random number generator.
- proc rand(): uint32¶
Get a random 32-bit value.
- proc rand[T: Fixed|SomeInteger](max: T): T¶
Get a random integer in the range
0..max
.Note
max
must be less than2^16
orfp(256)
.
- proc rand[T: Ordinal](a, b: T): T¶
Get a random value between
a
andb
inclusive.Note
a - b
must be less than2^16
, to avoid overflow.
- proc rand[T: Ordinal](s: Slice[T]): T¶
Get a random value from a slice.
Example:
let n = rand(0..100)
- proc rand[T: Ordinal](t: typedesc[T]): T¶
Get a random value of the given type.
- proc pickRandom[T](arr: openArray[T]): T¶
Get a random item from an array.
- proc pickRandom[T](arr: ptr UncheckedArray[T], len: SomeInteger): T¶
Get a random item from an unchecked array with a given length.
Compile-time helpers¶
- template readBin(path: static string): untyped¶
Read a binary file at compile-time as an array of bytes.
If assigned to a top-level
let
variable, this data will be placed in ROM.e.g.
let shipPal = readBin("ship.pal.bin")