···11+# Nim Starter Project Template
22+33+This is a Nim / TIC-80 starter template. To build wasm binary and import it into
44+the 'cart.tic' file, ensure you have a Nim compiler, Wasi-SDK and Tic-80 on your system and run:
55+66+```
77+nimble buildcart
88+```
99+1010+Run `nimble tasks` command to see more build options.
+38
templates/nim/cart.nimble
···11+# Package
22+33+version = "0.1.0"
44+author = "archargelod"
55+description = "tic80 wasm template for Nim language"
66+license = "MIT"
77+srcDir = "src"
88+99+1010+# Dependencies
1111+1212+requires "nim >= 2.0.0"
1313+1414+# Tasks
1515+import std/strformat
1616+1717+let pwd = getCurrentDir()
1818+1919+task wasmbuild, "Build wasm binary (debug)":
2020+ exec("nim c -o:cart.wasm src/cart")
2121+2222+task wasmrelease, "Build wasm binary (release)":
2323+ exec("nim c -d:release -o:cart.wasm src/cart")
2424+2525+task buildcart, "Build optimized wasm binary and import it to tic80 cart":
2626+ rmFile("cart.tic")
2727+ exec("nim c -d:release -o:cart.wasm src/cart")
2828+ exec(&"tic80 --cli --fs=\"{pwd}\" --cmd=\"load src/cart.tic & import binary cart.wasm & save cart.tic & exit\"")
2929+3030+task runcart, "Build optimized wasm binary and run it with tic80":
3131+ rmFile("cart.tic")
3232+ exec("nim c -d:release -o:cart.wasm src/cart")
3333+ exec(&"tic80 --skip --fs=\"{pwd}\" --cmd=\"load src/cart.tic & import binary cart.wasm & save cart.tic & run\"")
3434+3535+task debugcart, "Build wasm binary in debug mode and run it with tic80":
3636+ rmFile("cart.tic")
3737+ exec("nim c -o:cart.wasm src/cart")
3838+ exec(&"tic80 --skip --fs=\"{pwd}\" --cmd=\"load src/cart.tic & import binary cart.wasm & save cart.tic & run\"")
+52
templates/nim/config.nims
···11+import std/os
22+33+let wasi = getEnv("WASI_SDK_PATH")
44+if wasi == "" and not defined(nimsuggest):
55+ echo ""
66+ echo "Error:"
77+ echo "Download the WASI SDK (https://github.com/WebAssembly/wasi-sdk) and set the $WASI_SDK_PATH environment variable!"
88+ echo ""
99+ quit(-1)
1010+1111+switch("cpu", "wasm32")
1212+switch("cc", "clang")
1313+switch("threads", "off")
1414+1515+# ARC is much more embedded-friendly
1616+switch("gc", "arc")
1717+# Treats defects as errors, results in smaller binary size
1818+switch("panics", "on")
1919+2020+# Use the common ANSI C target
2121+switch("os", "any")
2222+# Needed for os:any
2323+switch("define", "posix")
2424+2525+# WASM has no signal handlers
2626+switch("define", "noSignalHandler")
2727+# The only entrypoints are start and update
2828+switch("noMain")
2929+# Use malloc instead of Nim's memory allocator, makes binary size much smaller
3030+switch("define", "useMalloc")
3131+3232+switch("clang.exe", wasi / "bin" / "clang")
3333+switch("clang.linkerexe", wasi / "bin" / "clang")
3434+3535+switch("passC", "--sysroot=" & (wasi / "share" / "wasi-sysroot"))
3636+3737+switch("passL", "-Wl,-zstack-size=8192,--no-entry,--import-memory -mexec-model=reactor -Wl,--initial-memory=262144,--max-memory=262144,--global-base=98304")
3838+3939+when not defined(release):
4040+ switch("assertions", "off")
4141+ switch("checks", "off")
4242+ switch("stackTrace", "off")
4343+ switch("lineTrace", "off")
4444+ switch("lineDir", "off")
4545+ switch("debugger", "native")
4646+ # This is needed to remove all unused Nim functions that will in turn import
4747+ # WASI stuff that is not available in WASM-4
4848+ switch("passL", "-Wl,--gc-sections")
4949+else:
5050+ switch("opt", "size")
5151+ switch("passC", "-flto")
5252+ switch("passL", "-Wl,--strip-all,--gc-sections,--lto-O3")
···11+import cart/tic80
22+33+var
44+ t = 1
55+ x = 96
66+ y = 24
77+88+proc TIC {.exportWasm.} =
99+ cls(Color13)
1010+1111+ if btn(P1_Up): dec y
1212+ if btn(P1_Down): inc y
1313+ if btn(P1_Left): dec x
1414+ if btn(P1_Right): inc x
1515+1616+ spr(1 + t mod 60 div 30 * 2, x, y, transColor=Color14, scale=3, w=2, h=2)
1717+ print("Hello from Nim!", 84, 84)
templates/nim/src/cart.tic
This is a binary file and will not be displayed.
+286
templates/nim/src/cart/internal.nim
···11+const
22+ TileSize* = 8
33+ Width* = 240
44+ Height* = 136
55+ WidthTiles* = Width div TileSize
66+ HeightTiles* = Height div TileSize
77+ Bpp* = 4 ## bits per pixel
88+99+type
1010+ Key* = enum
1111+ Key_Null, Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, Key_H, Key_I, Key_J,
1212+ Key_K, Key_L, Key_M, Key_N, Key_O, Key_P, Key_Q, Key_R, Key_S, Key_T, Key_U,
1313+ Key_V, Key_W, Key_X, Key_Y, Key_Z, Key_0, Key_1, Key_2, Key_3, Key_4, Key_5,
1414+ Key_6, Key_7, Key_8, Key_9, Key_Minus, Key_Equals, Key_Leftbracket,
1515+ Key_Rightbracket, Key_Backslash, Key_Semicolon, Key_Apostrophe, Key_Grave,
1616+ Key_Comma, Key_Period, Key_Slash, Key_Space, Key_Tab, Key_Return, Key_Backspace,
1717+ Key_Delete, Key_Insert, Key_Pageup, Key_Pagedown, Key_Home, Key_End, Key_Up,
1818+ Key_Down, Key_Left, Key_Right, Key_Capslock, Key_Ctrl, Key_Shift, Key_Alt
1919+2020+ Button* = enum
2121+ P1_Up, P1_Down, P1_Left, P1_Right, P1_A, P1_B, P1_X, P1_Y,
2222+ P2_Up, P2_Down, P2_Left, P2_Right, P2_A, P2_B, P2_X, P2_Y,
2323+ P3_Up, P3_Down, P3_Left, P3_Right, P3_A, P3_B, P3_X, P3_Y,
2424+ P4_Up, P4_Down, P4_Left, P4_Right, P4_A, P4_B, P4_X, P4_Y
2525+2626+ Color* = enum
2727+ Null = -1'i8, Color0, Color1, Color2, Color3, Color4, Color5, Color6, Color7,
2828+ Color8, Color9, Color10, Color11, Color12, Color13, Color14, Color15,
2929+3030+ VRAM* {.bycopy.} = object
3131+ screen*: array[Width * Height * Bpp div 8, uint8]
3232+ palette*: array[48, uint8] ## 16 colors.
3333+ palletteMap*: array[8, uint8] ## 16 indices.
3434+ borderColorAndOvrTransparency*: uint8 ## Bank 0 is border color, bank 1 is OVR transparency.
3535+ screenOffsetX*: int8
3636+ screenOffsetY*: int8
3737+ mouseCursor*: int8
3838+ blitSegment*: uint8
3939+ reserved*: array[3, uint8]
4040+4141+ MousePos* {.bycopy.} = object
4242+ x*: int16
4343+ y*: int16
4444+ scrollx*: int8
4545+ scrolly*: int8
4646+ left*: bool
4747+ middle*: bool
4848+ right*: bool
4949+5050+5151+## ---------------------------
5252+## Pointers
5353+## ---------------------------
5454+5555+const
5656+ Framebuffer*: ptr Vram = cast[ptr Vram](0)
5757+ Tiles*: ptr uint8 = cast[ptr uint8](0x04000)
5858+ Sprites*: ptr uint8 = cast[ptr uint8](0x06000)
5959+ Map*: ptr uint8 = cast[ptr uint8](0x08000)
6060+ Gamepads*: ptr uint8 = cast[ptr uint8](0x0ff80)
6161+ Mouse*: ptr uint8 = cast[ptr uint8](0x0ff84)
6262+ Keyboard*: ptr uint8 = cast[ptr uint8](0x0ff88)
6363+ SfxState*: ptr uint8 = cast[ptr uint8](0x0ff8c)
6464+ SoundRegisters*: ptr uint8 = cast[ptr uint8](0x0ff9c)
6565+ Waveforms*: ptr uint8 = cast[ptr uint8](0x0ffe4)
6666+ Sfx*: ptr uint8 = cast[ptr uint8](0x100e4)
6767+ MusicPatterns*: ptr uint8 = cast[ptr uint8](0x11164)
6868+ MusicTracks*: ptr uint8 = cast[ptr uint8](0x13e64)
6969+ SoundState*: ptr uint8 = cast[ptr uint8](0x13ffc)
7070+ StereoVolume*: ptr uint8 = cast[ptr uint8](0x14000)
7171+ PersistentMemory*: ptr uint8 = cast[ptr uint8](0x14004)
7272+ SpriteFlags*: ptr uint8 = cast[ptr uint8](0x14404)
7373+ SystemFont*: ptr uint8 = cast[ptr uint8](0x14604)
7474+ WasmFreeRam*: ptr uint8 = cast[ptr uint8](0x18000)
7575+7676+## 160kb
7777+7878+7979+## ---------------------------
8080+## Constants
8181+## ---------------------------
8282+8383+const
8484+ TilesSize*: uint32 = 0x2000
8585+ SpritesSize*: uint32 = 0x2000
8686+ MapSize*: uint32 = 32640
8787+ GamepadsSize*: uint32 = 4
8888+ MouseSize*: uint32 = 4
8989+ KeyboardSize*: uint32 = 4
9090+ SfxStateSize*: uint32 = 16
9191+ SoundRegistersSize*: uint32 = 72
9292+ WaveformsSize*: uint32 = 256
9393+ SfxSize*: uint32 = 4224
9494+ MusicPatternsSize*: uint32 = 11520
9595+ MusicTracksSize*: uint32 = 408
9696+ SoundStateSize*: uint32 = 4
9797+ StereoVolumeSize*: uint32 = 4
9898+ PersistentMemorySize*: uint32 = 1024
9999+ SpriteFlagsSize*: uint32 = 512
100100+ SystemFontSize*: uint32 = 2048
101101+ WasmFreeRamSize*: uint32 = 163840
102102+103103+## 160kb
104104+105105+106106+{.push importc, codegenDecl: "__attribute__((import_name(\"$2\"))) $1 $2$3".}
107107+108108+## ---------------------------
109109+## Drawing Procedures
110110+## ---------------------------
111111+112112+proc circ*(x, y, radius: int32; color: Color)
113113+ ## Draw a filled circle.
114114+115115+proc circb*(x, y, radius: int32; color: Color)
116116+ ## Draw a circle border.
117117+118118+proc elli*(x, y, a, b: int32; color: Color)
119119+ ## Draw a filled ellipse.
120120+121121+proc ellib*(x, y, a, b: int32; color: Color)
122122+ ## Draw an ellipse border.
123123+124124+proc clip*(x, y, width, height: int32)
125125+ ## Set the screen clipping region.
126126+127127+proc cls*(color: Color = Color0)
128128+ ## Clear the screen.
129129+130130+proc font*(text: cstring; x, y: int32; trans_colors: ptr uint8; trans_count: int8,
131131+ char_width, char_height: int8; fixed: bool; scale: int8; alt: bool): int8
132132+ ## Print a string using foreground sprite data as the font.
133133+134134+proc line*(x0, y0, x1, y1: cfloat; color: Color)
135135+ ## Draw a straight line.
136136+137137+proc map*(x, y, w, h, sx, sy: int32; trans_colors: ptr uint8; colorCount: int8;
138138+ scale: int8; remap: int32)
139139+ ## Draw a map region.
140140+141141+proc pix*(x, y: int32; color: Color): uint8 {.discardable.}
142142+ ## Get or set the color of a single pixel.
143143+144144+proc print*(text: cstring; x, y: int32; color: Color = Color15; fixed = false;
145145+ scale: int32 = 1; alt = false): int32 {.discardable.}
146146+ ## Print a string using the system font.
147147+148148+proc rect*(x, y, w, h: int32; color: Color)
149149+ ## Draw a filled rectangle.
150150+151151+proc rectb*(x, y, w, h: int32; color: Color)
152152+ ## Draw a rectangle border.
153153+154154+proc spr*(id, x, y: int32; trans_colors: ptr uint8; color_count: int8;
155155+ scale, flip, rotate, w, h: int32)
156156+ ## Draw a sprite or composite sprite.
157157+158158+proc tri*(x1, y1, x2, y2, x3, y3: cfloat; color: Color)
159159+ ## Draw a filled triangle.
160160+161161+proc trib*(x1, y1, x2, y2, x3, y3: cfloat; color: Color)
162162+ ## Draw a triangle border.
163163+164164+proc ttri*(x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3: cfloat; texsrc: int32;
165165+ trans_colors: ptr uint8; color_count: int8; z1, z2, z3: cfloat; depth: bool)
166166+ ## Draw a triangle filled with texture.
167167+168168+169169+## ---------------------------
170170+## Input Functions
171171+## ---------------------------
172172+173173+proc btn*(button: Button): uint32
174174+ ## Get gamepad button state in current frame.
175175+176176+proc btnp*(button: Button; hold: int32 = -1; period: int32 = -1): uint32
177177+ ## Get gamepad button state according to previous frame.
178178+179179+proc key*(key: Key): bool
180180+ ## Get keyboard button state in current frame.
181181+182182+proc keyp*(key: Key; hold: int32 = -1; period: int32 = -1): bool
183183+ ## Get keyboard button state relative to previous frame.
184184+185185+proc mouse*(mouse_ptr_addy: ptr MousePos)
186186+ ## Get XY and press state of mouse/touch.
187187+188188+189189+## ---------------------------
190190+## Sound Functions
191191+## ---------------------------
192192+193193+proc music*(track, frame, row: int32; loop, sustain: bool; tempo, speed: int32)
194194+ ## Play or stop playing music.
195195+196196+proc sfx*(sfx_id, note, octave, duration, channel, volume_left, volume_right,
197197+ speed: int32)
198198+ ## Play or stop playing a given sound.
199199+200200+201201+## ---------------------------
202202+## Memory Functions
203203+## ---------------------------
204204+205205+proc pmem*(address: int32; value: int64): uint32 {.discardable.}
206206+ ## Access or update the persistent memory.
207207+208208+proc peek*(address: int32; bits: int8 = 8): int8
209209+ ## Read a byte from an address in RAM.
210210+211211+proc peek1*(address: int32): int8
212212+ ## Read a single bit from an address in RAM.
213213+214214+proc peek2*(address: int32): int8
215215+ ## Read two bit value from an address in RAM.
216216+217217+proc peek4*(address: int32): int8
218218+ ## Read a nibble value from an address.
219219+220220+proc poke*(address: int32; value: int8; bits: int8 = 8)
221221+ ## Write a byte value to an address in RAM.
222222+223223+proc poke1*(address: int32; value: int8)
224224+ ## Write a single bit to an address in RAM.
225225+226226+proc poke2*(address: int32; value: int8)
227227+ ## Write a two bit value to an address in RAM.
228228+229229+proc poke4*(address: int32; value: int8)
230230+ ## Write a nibble value to an address in RAM.
231231+232232+proc sync*(mask: int32 = 0; bank: int8 = 0; to_cart = false)
233233+ ## Copy banks of RAM (sprites, map, etc) to and from the cartridge.
234234+235235+proc vbank*(bank: int8): int8
236236+ ## Switch the 16kb of banked video RAM.
237237+238238+239239+## ---------------------------
240240+## Utility Functions
241241+## ---------------------------
242242+243243+proc fget*(sprite_index: int32; flag: int8): bool
244244+ ## Retrieve a sprite flag.
245245+246246+proc fset*(sprite_index: int32; flag: int8; value: bool): bool {.discardable.}
247247+ ## Update a sprite flag.
248248+249249+proc mget*(x, y: int32): int32
250250+ ## Retrieve a map tile at given coordinates.
251251+252252+proc mset*(x, y, value: int32)
253253+ ## Update a map tile at given coordinates.
254254+255255+256256+## ---------------------------
257257+## System Functions
258258+## ---------------------------
259259+260260+proc exit*()
261261+ ## Interrupt program and return to console.
262262+263263+proc tstamp*(): uint32
264264+ ## Returns the current Unix timestamp in seconds.
265265+266266+proc trace*(text: cstring; color: Color = Color15)
267267+ ## Print a string to the Console.
268268+269269+{.pop.}
270270+271271+# suffix fixes "unreachable code executed" error
272272+proc time2*(): cfloat {.importc,
273273+ codegenDecl: "__attribute__((import_name(\"time\"))) $1 $2$3".}
274274+ ## Returns how many milliseconds have passed since game started.
275275+276276+import std/macros
277277+278278+macro exportWasm*(def: untyped): untyped =
279279+ result = def
280280+ result[^3] = nnkPragma.newTree(
281281+ ident("exportc"),
282282+ nnkExprColonExpr.newTree(
283283+ ident("codegenDecl"),
284284+ newStrLitNode("__attribute__((export_name(\"$2\"))) $1 $2$3")
285285+ )
286286+ )
+78
templates/nim/src/cart/tic80.nim
···11+import internal except btn, btnp, trace
22+export internal except btn, btnp, trace
33+44+proc font*(text: cstring, x, y: int32, transColor, char_width, char_height: int8,
55+ fixed: bool, scale: int8, alt: bool): int8 {.discardable.} =
66+ ## Print a string using foreground sprite data as the font.
77+ if transColor >= 0:
88+ let trans_colors = transColor.uint8
99+ internal.font(text, x, y, addr trans_colors, 1, char_width, char_height, fixed,
1010+ scale, alt)
1111+ else:
1212+ internal.font(text, x, y, nil, 0, char_width, char_height, fixed, scale, alt)
1313+1414+proc map*(x: int32 = 0, y: int32 = 0, w: int32 = 30, h: int32 = 17,
1515+ sx: int32 = 0, sy: int32 = 0, transColor: Color = Null, scale: int8 = 1,
1616+ remap: int32 = -1) =
1717+ ## Draw a map region.
1818+ if transColor.int8 >= 0:
1919+ let trans_colors = transColor.uint8
2020+ internal.map(x, y, w, h, sx, sy, addr trans_colors, 1, scale, remap)
2121+ else:
2222+ internal.map(x, y, w, h, sx, sy, nil, 0, scale, remap)
2323+2424+proc spr*(id, x, y: int32, transColor: Color = Null, scale: int32 = 1,
2525+ flip: int32 = 0, rotate: int32 = 0, w: int32 = 1, h: int32 = 1) =
2626+ ## Draw a sprite or composite sprite.
2727+ if transColor.int8 >= 0:
2828+ let trans_colors = transColor.uint8
2929+ internal.spr(id, x, y, addr trans_colors, 1, scale, flip, rotate, w, h)
3030+ else:
3131+ internal.spr(id, x, y, nil, 0, scale, flip, rotate, w, h)
3232+3333+proc ttri*(x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3: cfloat,
3434+ texsrc: int32 = 0, transColor: Color = Null,
3535+ z1: cfloat = 0, z2: cfloat = 0, z3: cfloat = 0, depth = false) =
3636+ ## Draw a triangle filled with texture.
3737+ if transColor.int8 >= 0:
3838+ let trans_colors = transColor.uint8
3939+ internal.ttri(x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, texsrc,
4040+ addr trans_colors, 1, z1, z2, z3, depth)
4141+ else:
4242+ internal.ttri(x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, texsrc,
4343+ nil, 0, z1, z2, z3, depth)
4444+4545+proc btn*(index: Button): bool {.inline.} =
4646+ ## Get gamepad button state in current frame.
4747+ internal.btn(index) > 0
4848+4949+proc btnp*(index: Button, hold: int32 = -1, period: int32 = -1): bool {.inline.} =
5050+ ## Get gamepad button state according to previous frame.
5151+ internal.btnp(index, hold, period) > 0
5252+5353+proc mouse*(): MousePos {.inline.} = internal.mouse(addr result)
5454+ ## Get XY and press state of mouse/touch.
5555+5656+proc pmemset*(address: int32; value: int64) = pmem(address, value)
5757+ ## Update the persistent memory.
5858+proc pmemget*(address: int32): uint32 = pmem(address, -1)
5959+ ## Access the persistent memory.
6060+6161+proc pixset*(x, y: int32; color: Color) = pix(x, y, color)
6262+ ## Set the color of a single pixel.
6363+proc pixget*(x, y: int32): uint8 = pix(x, y, Null)
6464+ ## Get the color of a single pixel.
6565+6666+proc trace*(items: varargs[cstring, cstring]) =
6767+ ## Print a string to the Console.
6868+ for item in items: internal.trace(item)
6969+7070+proc print*(text: string; x, y: int32; color: Color = Color15; fixed = false;
7171+ scale: int32 = 1; alt = false): int32 {.discardable.} =
7272+ ## Print a string using the system font.
7373+ print(cstring(text), x, y, color, fixed, scale, alt)
7474+7575+proc NimMain() {.importc.}
7676+7777+proc BOOT() {.exportWasm.} =
7878+ NimMain()