1#![allow(dead_code, unused_macros)]
3#![allow(clippy::missing_safety_doc)]
4
5use std::ffi::CString;
6
7pub use sys::MouseInput;
8
9pub const WIDTH: i32 = 240;
11pub const HEIGHT: i32 = 136;
12
13pub const FRAMEBUFFER: *mut [u8; 16320] = 0x00000 as *mut [u8; 16320];
16pub const PALETTE: *mut [u8; 48] = 0x03FC0 as *mut [u8; 48];
17pub const PALETTE_MAP: *mut [u8; 8] = 0x03FF0 as *mut [u8; 8];
18pub const BORDER_COLOR: *mut u8 = 0x03FF8 as *mut u8;
19pub const SCREEN_OFFSET_X: *mut u8 = 0x03FF9 as *mut u8;
20pub const SCREEN_OFFSET_Y: *mut u8 = 0x03FFA as *mut u8;
21pub const MOUSE_CURSOR: *mut u8 = 0x03FFB as *mut u8;
22pub const BLIT_SEGMENT: *mut u8 = 0x03FFC as *mut u8;
23pub const TILES: *mut [u8; 8192] = 0x04000 as *mut [u8; 8192];
24pub const SPRITES: *mut [u8; 8192] = 0x06000 as *mut [u8; 8192];
25pub const MAP: *mut [u8; 32640] = 0x08000 as *mut [u8; 32640];
26pub const GAMEPADS: *mut [u8; 4] = 0x0FF80 as *mut [u8; 4];
27pub const MOUSE: *mut [u8; 4] = 0x0FF84 as *mut [u8; 4];
28pub const KEYBOARD: *mut [u8; 4] = 0x0FF88 as *mut [u8; 4];
29pub const SFX_STATE: *mut [u8; 16] = 0x0FF8C as *mut [u8; 16];
30pub const SOUND_REGISTERS: *mut [u8; 72] = 0x0FF9C as *mut [u8; 72];
31pub const WAVEFORMS: *mut [u8; 256] = 0x0FFE4 as *mut [u8; 256];
32pub const SFX: *mut [u8; 4224] = 0x100E4 as *mut [u8; 4224];
33pub const MUSIC_PATTERNS: *mut [u8; 11520] = 0x11164 as *mut [u8; 11520];
34pub const MUSIC_TRACKS: *mut [u8; 408] = 0x13E64 as *mut [u8; 408];
35pub const SOUND_STATE: *mut [u8; 4] = 0x13FFC as *mut [u8; 4];
36pub const STEREO_VOLUME: *mut [u8; 4] = 0x14000 as *mut [u8; 4];
37pub const PERSISTENT_RAM: *mut [u8; 1024] = 0x14004 as *mut [u8; 1024];
38pub const SPRITE_FLAGS: *mut [u8; 512] = 0x14404 as *mut [u8; 512];
39pub const SYSTEM_FONT: *mut [u8; 2048] = 0x14604 as *mut [u8; 2048];
40
41#[cfg(target_family = "wasm")]
42macro_rules! iface {
43 ($(pub fn $ident:ident($($arg:ident : $ty:ty),*$(,)?) $(-> $ret:ty)?);*$(;)?) => {
44 extern "C" {
45 $(
46 pub fn $ident(
47 $(
48 $arg: $ty
49 ),*
50 ) $(-> $ret)*;
51 )*
52 }
53 };
54}
55
56#[cfg(not(target_family = "wasm"))]
57macro_rules! iface {
58 ($(pub fn $ident:ident($($arg:ident : $ty:ty),*$(,)?) $(-> $ret:ty)?);*$(;)?) => {
59 $(
60 #[allow(unused,clippy::too_many_arguments)]
63 pub unsafe fn $ident(
64 $(
65 #[allow(unused)]
66 $arg: $ty
67 ),*
68 ) $(-> $ret)* {
69 panic!("Attempted to call TIC80: {} which is not supported in this target", stringify!($ident));
70 }
71 )*
72 };
73}
74
75pub mod sys {
78 #[derive(Default)]
79 #[repr(C)]
80 pub struct MouseInput {
81 pub x: i16,
82 pub y: i16,
83 pub scroll_x: i8,
84 pub scroll_y: i8,
85 pub left: bool,
86 pub middle: bool,
87 pub right: bool,
88 }
89
90 iface! {
91 pub fn btn(index: i32) -> i32;
92 pub fn btnp(index: i32, hold: i32, period: i32) -> bool;
93 pub fn clip(x: i32, y: i32, width: i32, height: i32);
94 pub fn cls(color: u8);
95 pub fn circ(x: i32, y: i32, radius: i32, color: u8);
96 pub fn circb(x: i32, y: i32, radius: i32, color: u8);
97 pub fn elli(x: i32, y: i32, a: i32, b: i32, color: u8);
98 pub fn ellib(x: i32, y: i32, a: i32, b: i32, color: u8);
99 pub fn exit();
100 pub fn fget(sprite_index: i32, flag: i8) -> bool;
101 pub fn fset(sprite_index: i32, flag: i8, value: bool);
102 pub fn font(
103 text: *const u8,
104 x: i32,
105 y: i32,
106 trans_colors: *const u8,
107 trans_count: i8,
108 char_width: i8,
109 char_height: i8,
110 fixed: bool,
111 scale: i32,
112 alt: bool,
113 ) -> i32;
114 pub fn key(index: i32) -> bool;
115 pub fn keyp(index: i8, hold: i32, period: i32) -> bool;
116 pub fn line(x0: f32, y0: f32, x1: f32, y1: f32, color: u8);
117 pub fn map(
119 x: i32,
120 y: i32,
121 w: i32,
122 h: i32,
123 sx: i32,
124 sy: i32,
125 trans_colors: *const u8,
126 color_count: i8,
127 scale: i8,
128 remap: i32,
129 );
130 pub fn mget(x: i32, y: i32) -> i32;
134 pub fn mset(x: i32, y: i32, value: i32);
135 pub fn mouse(mouse: *mut MouseInput);
136 pub fn music(
137 track: i32,
138 frame: i32,
139 row: i32,
140 repeat: bool,
141 sustain: bool,
142 tempo: i32,
143 speed: i32,
144 );
145 pub fn pix(x: i32, y: i32, color: i8) -> u8;
146 pub fn peek(address: i32, bits: u8) -> u8;
147 pub fn peek4(address: i32) -> u8;
148 pub fn peek2(address: i32) -> u8;
149 pub fn peek1(address: i32) -> u8;
150 pub fn pmem(address: i32, value: i64) -> i32;
151 pub fn poke(address: i32, value: u8, bits: u8);
152 pub fn poke4(address: i32, value: u8);
153 pub fn poke2(address: i32, value: u8);
154 pub fn poke1(address: i32, value: u8);
155 pub fn print(
156 text: *const u8,
157 x: i32,
158 y: i32,
159 color: i32,
160 fixed: bool,
161 scale: i32,
162 alt: bool,
163 ) -> i32;
164 pub fn rect(x: i32, y: i32, w: i32, h: i32, color: u8);
165 pub fn rectb(x: i32, y: i32, w: i32, h: i32, color: u8);
166 pub fn sfx(
167 sfx_id: i32,
168 note: i32,
169 octave: i32,
170 duration: i32,
171 channel: i32,
172 volume_left: i32,
173 volume_right: i32,
174 speed: i32,
175 );
176 pub fn spr(
177 id: i32,
178 x: i32,
179 y: i32,
180 trans_colors: *const u8,
181 color_count: i8,
182 scale: i32,
183 flip: i32,
184 rotate: i32,
185 w: i32,
186 h: i32,
187 );
188 pub fn sync(mask: i32, bank: u8, to_cart: bool);
189 pub fn time() -> f32;
190 pub fn tstamp() -> u32;
191 pub fn trace(text: *const u8, color: u8);
192 pub fn tri(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32, color: u8);
193 pub fn trib(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32, color: u8);
194 pub fn ttri(
195 x1: f32,
196 y1: f32,
197 x2: f32,
198 y2: f32,
199 x3: f32,
200 y3: f32,
201 u1: f32,
202 v1: f32,
203 u2: f32,
204 v2: f32,
205 u3: f32,
206 v3: f32,
207 tex_src: i32,
208 trans_colors: *const u8,
209 color_count: i8,
210 z1: f32,
211 z2: f32,
212 z3: f32,
213 depth: bool,
214 );
215 pub fn vbank(bank: u8) -> u8;
216 }
217}
218
219pub fn btn(index: i32) -> bool {
222 unsafe { sys::btn(index) != 0 }
223}
224
225pub fn btn_bits() -> u32 {
226 unsafe { sys::btn(-1) as u32 }
227}
228
229pub fn btnp(index: i32, hold: i32, period: i32) -> bool {
230 unsafe { sys::btnp(index, hold, period) }
231}
232
233pub fn key(index: i32) -> bool {
234 unsafe { sys::key(index) }
235}
236
237pub fn keyp(index: i32, hold: i32, period: i32) -> bool {
238 unsafe { sys::keyp(i8::try_from(index).unwrap(), hold, period) }
239}
240
241pub fn mouse() -> MouseInput {
242 let mut input = MouseInput::default();
243 unsafe {
244 sys::mouse(&mut input as *mut _);
245 }
246 input
247}
248
249pub struct MusicOptions {
252 pub frame: i32,
253 pub row: i32,
254 pub repeat: bool,
255 pub sustain: bool,
256 pub tempo: i32,
257 pub speed: i32,
258}
259
260impl Default for MusicOptions {
261 fn default() -> Self {
262 Self {
263 frame: -1,
264 row: -1,
265 repeat: true,
266 sustain: false,
267 tempo: -1,
268 speed: -1,
269 }
270 }
271}
272
273pub fn music(track: i32, opts: MusicOptions) {
274 unsafe {
275 sys::music(
276 track,
277 opts.frame,
278 opts.row,
279 opts.repeat,
280 opts.sustain,
281 opts.tempo,
282 opts.speed,
283 )
284 }
285}
286
287pub struct SfxOptions {
288 pub note: i32,
289 pub octave: i32,
290 pub duration: i32,
291 pub channel: i32,
292 pub volume_left: i32,
293 pub volume_right: i32,
294 pub speed: i32,
295}
296
297impl Default for SfxOptions {
298 fn default() -> Self {
299 Self {
300 note: -1,
301 octave: -1,
302 duration: -1,
303 channel: 0,
304 volume_left: 15,
305 volume_right: 15,
306 speed: 0,
307 }
308 }
309}
310
311pub fn sfx(sfx_id: i32, opts: SfxOptions) {
312 unsafe {
313 sys::sfx(
314 sfx_id,
315 opts.note,
316 opts.octave,
317 opts.duration,
318 opts.channel,
319 opts.volume_left,
320 opts.volume_right,
321 opts.speed,
322 )
323 }
324}
325
326pub fn cls(color: u8) {
329 unsafe { sys::cls(color) }
330}
331
332pub fn clip(x: i32, y: i32, width: i32, height: i32) {
333 unsafe { sys::clip(x, y, width, height) }
334}
335
336pub fn circ(x: i32, y: i32, radius: i32, color: u8) {
337 unsafe { sys::circ(x, y, radius, color) }
338}
339
340pub fn circb(x: i32, y: i32, radius: i32, color: u8) {
341 unsafe { sys::circb(x, y, radius, color) }
342}
343
344pub fn elli(x: i32, y: i32, a: i32, b: i32, color: u8) {
345 unsafe { sys::elli(x, y, a, b, color) }
346}
347
348pub fn ellib(x: i32, y: i32, a: i32, b: i32, color: u8) {
349 unsafe { sys::ellib(x, y, a, b, color) }
350}
351
352pub fn line(x0: f32, y0: f32, x1: f32, y1: f32, color: u8) {
353 unsafe { sys::line(x0, y0, x1, y1, color) }
354}
355
356pub fn pix(x: i32, y: i32, color: u8) {
357 unsafe {
358 sys::pix(x, y, color as i8);
359 }
360}
361
362pub fn get_pix(x: i32, y: i32) -> u8 {
363 unsafe { sys::pix(x, y, -1) }
364}
365
366pub fn rect(x: i32, y: i32, w: i32, h: i32, color: u8) {
367 unsafe { sys::rect(x, y, w, h, color) }
368}
369
370pub fn rectb(x: i32, y: i32, w: i32, h: i32, color: u8) {
371 unsafe { sys::rectb(x, y, w, h, color) }
372}
373
374pub fn tri(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32, color: u8) {
375 unsafe { sys::tri(x1, y1, x2, y2, x3, y3, color) }
376}
377
378pub fn trib(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32, color: u8) {
379 unsafe { sys::trib(x1, y1, x2, y2, x3, y3, color) }
380}
381
382pub enum TextureSource {
383 Tiles,
384 Map,
385 VBank1,
386}
387
388pub struct TTriOptions<'a> {
389 pub texture_src: TextureSource,
390 pub transparent: &'a [u8],
391 pub z1: f32,
392 pub z2: f32,
393 pub z3: f32,
394 pub depth: bool,
395}
396
397impl Default for TTriOptions<'_> {
398 fn default() -> Self {
399 Self {
400 texture_src: TextureSource::Tiles,
401 transparent: &[],
402 z1: 0.0,
403 z2: 0.0,
404 z3: 0.0,
405 depth: false,
406 }
407 }
408}
409
410#[allow(clippy::too_many_arguments)]
411pub fn ttri(
412 x1: f32,
413 y1: f32,
414 x2: f32,
415 y2: f32,
416 x3: f32,
417 y3: f32,
418 u1: f32,
419 v1: f32,
420 u2: f32,
421 v2: f32,
422 u3: f32,
423 v3: f32,
424 opts: TTriOptions,
425) {
426 unsafe {
427 sys::ttri(
428 x1,
429 y1,
430 x2,
431 y2,
432 x3,
433 y3,
434 u1,
435 v1,
436 u2,
437 v2,
438 u3,
439 v3,
440 opts.texture_src as i32,
441 opts.transparent.as_ptr(),
442 opts.transparent.len() as i8,
443 opts.z1,
444 opts.z2,
445 opts.z3,
446 opts.depth,
447 )
448 }
449}
450
451pub struct MapOptions<'a> {
452 pub x: i32,
453 pub y: i32,
454 pub w: i32,
455 pub h: i32,
456 pub sx: i32,
457 pub sy: i32,
458 pub transparent: &'a [u8],
459 pub scale: i8,
460}
461
462impl Default for MapOptions<'_> {
463 fn default() -> Self {
464 Self {
465 x: 0,
466 y: 0,
467 w: 30,
468 h: 17,
469 sx: 0,
470 sy: 0,
471 transparent: &[],
472 scale: 1,
473 }
474 }
475}
476
477pub fn map(opts: MapOptions) {
478 unsafe {
479 sys::map(
480 opts.x,
481 opts.y,
482 opts.w,
483 opts.h,
484 opts.sx,
485 opts.sy,
486 opts.transparent.as_ptr(),
487 opts.transparent.len() as i8,
488 opts.scale,
489 0,
490 )
491 }
492}
493
494pub fn mget(x: i32, y: i32) -> i32 {
495 unsafe { sys::mget(x, y) }
496}
497
498pub fn mset(x: i32, y: i32, value: i32) {
499 unsafe { sys::mset(x, y, value) }
500}
501
502#[derive(Copy, Clone)]
503#[repr(i32)]
504pub enum Flip {
505 None = 0,
506 Horizontal = 1,
507 Vertical = 2,
508 Both = 3,
509}
510
511#[derive(Copy, Clone)]
512#[repr(i32)]
513pub enum Rotate {
514 None = 0,
515 By90 = 1,
516 By180 = 2,
517 By270 = 3,
518}
519
520pub struct SpriteOptions<'a> {
521 pub transparent: &'a [u8],
522 pub scale: i32,
523 pub flip: Flip,
524 pub rotate: Rotate,
525 pub w: i32,
526 pub h: i32,
527}
528
529impl Default for SpriteOptions<'_> {
530 fn default() -> Self {
531 Self {
532 transparent: &[],
533 scale: 1,
534 flip: Flip::None,
535 rotate: Rotate::None,
536 w: 1,
537 h: 1,
538 }
539 }
540}
541
542pub fn spr(id: i32, x: i32, y: i32, opts: SpriteOptions) {
543 unsafe {
544 sys::spr(
545 id,
546 x,
547 y,
548 opts.transparent.as_ptr(),
549 opts.transparent.len() as i8,
550 opts.scale,
551 opts.flip as i32,
552 opts.rotate as i32,
553 opts.w,
554 opts.h,
555 )
556 }
557}
558
559pub fn fget(sprite_index: i32, flag: i8) -> bool {
560 unsafe { sys::fget(sprite_index, flag) }
561}
562
563pub fn fset(sprite_index: i32, flag: i8, value: bool) {
564 unsafe { sys::fset(sprite_index, flag, value) }
565}
566
567pub struct PrintOptions {
573 color: i32,
574 fixed: bool,
575 scale: i32,
576 small_font: bool,
577}
578
579impl Default for PrintOptions {
580 fn default() -> Self {
581 Self {
582 color: 15,
583 fixed: false,
584 scale: 1,
585 small_font: false,
586 }
587 }
588}
589
590pub fn print_raw(text: &str, x: i32, y: i32, opts: PrintOptions) -> i32 {
591 unsafe {
592 sys::print(
593 text.as_ptr(),
594 x,
595 y,
596 opts.color,
597 opts.fixed,
598 opts.scale,
599 opts.small_font,
600 )
601 }
602}
603
604pub fn print_alloc(text: impl AsRef<str>, x: i32, y: i32, opts: PrintOptions) -> i32 {
605 let text = CString::new(text.as_ref()).unwrap();
606 unsafe {
607 sys::print(
608 text.as_ptr() as *const u8,
609 x,
610 y,
611 opts.color,
612 opts.fixed,
613 opts.scale,
614 opts.small_font,
615 )
616 }
617}
618
619#[macro_export]
621macro_rules! print {
622 ($text: literal, $($args: expr), *) => {
623 $crate::tic80::print_raw(concat!($text, "\0"), $($args), *);
624 };
625 ($text: expr, $($args: expr), *) => {
626 $crate::tic80::print_alloc($text, $($args), *);
627 };
628}
629
630pub struct FontOptions<'a> {
631 transparent: &'a [u8],
632 char_width: i8,
633 char_height: i8,
634 fixed: bool,
635 scale: i32,
636 alt_font: bool,
637}
638
639impl Default for FontOptions<'_> {
640 fn default() -> Self {
641 Self {
642 transparent: &[],
643 char_width: 8,
644 char_height: 8,
645 fixed: false,
646 scale: 1,
647 alt_font: false,
648 }
649 }
650}
651
652pub fn font_raw(text: &str, x: i32, y: i32, opts: FontOptions) -> i32 {
653 unsafe {
654 sys::font(
655 text.as_ptr(),
656 x,
657 y,
658 opts.transparent.as_ptr(),
659 opts.transparent.len() as i8,
660 opts.char_width,
661 opts.char_height,
662 opts.fixed,
663 opts.scale,
664 opts.alt_font,
665 )
666 }
667}
668
669pub fn font_alloc(text: impl AsRef<str>, x: i32, y: i32, opts: FontOptions) -> i32 {
670 let text = CString::new(text.as_ref()).unwrap();
671 unsafe {
672 sys::font(
673 text.as_ptr() as *const u8,
674 x,
675 y,
676 opts.transparent.as_ptr(),
677 opts.transparent.len() as i8,
678 opts.char_width,
679 opts.char_height,
680 opts.fixed,
681 opts.scale,
682 opts.alt_font,
683 )
684 }
685}
686
687pub fn trace_alloc(text: impl AsRef<str>, color: u8) {
700 let text = CString::new(text.as_ref()).unwrap();
701 unsafe { sys::trace(text.as_ptr() as *const u8, color) }
702}
703
704pub unsafe fn memcpy(dest: i32, src: i32, length: usize) {
719 core::ptr::copy(src as *const u8, dest as *mut u8, length)
720}
721
722pub unsafe fn memset(address: i32, value: u8, length: usize) {
723 core::ptr::write_bytes(address as *mut u8, value, length)
724}
725
726pub unsafe fn peek(address: i32) -> u8 {
727 sys::peek(address, 8)
728}
729
730pub unsafe fn peek4(address: i32) -> u8 {
731 sys::peek4(address)
732}
733
734pub unsafe fn peek2(address: i32) -> u8 {
735 sys::peek2(address)
736}
737
738pub unsafe fn peek1(address: i32) -> u8 {
739 sys::peek1(address)
740}
741
742pub unsafe fn poke(address: i32, value: u8) {
743 sys::poke(address, value, 8);
744}
745
746pub unsafe fn poke4(address: i32, value: u8) {
747 sys::poke4(address, value);
748}
749
750pub unsafe fn poke2(address: i32, value: u8) {
751 sys::poke2(address, value);
752}
753
754pub unsafe fn poke1(address: i32, value: u8) {
755 sys::poke1(address, value);
756}
757
758pub unsafe fn sync(mask: i32, bank: u8, to_cart: bool) {
759 sys::sync(mask, bank, to_cart);
760}
761
762pub unsafe fn vbank(bank: u8) {
763 sys::vbank(bank);
764}
765
766pub fn pmem_set(address: i32, value: i32) {
767 unsafe {
768 sys::pmem(address, value as i64);
769 }
770}
771
772pub fn pmem_get(address: i32) -> i32 {
773 unsafe { sys::pmem(address, -1) }
774}
775
776pub fn exit() {
778 unsafe { sys::exit() }
779}
780
781pub fn time() -> f32 {
782 unsafe { sys::time() }
783}
784
785pub fn tstamp() -> u32 {
786 unsafe { sys::tstamp() }
787}