# ============================================================================= # IO (FRAMEWORK FILE) VERSION 2026-07-12 # ============================================================================= # Hybrix programs interact with the virtual computer's hardware using # memory-mapped input/output (MMIO), which means reading/writing special # memory addresses. For example, writing a 1 byte to $d0_0011 will enable # the Jamdac synthesizer, or reading from $d0_0300 can tell you the current # background color. This file defines all the MMIO locations. # ----------------------------------------------------------------------------- class io_theme # size 64 # Maps a theme color index to a system palette index inset color_map: byte[size 64] end class # ----------------------------------------------------------------------------- class io_sprite # size 16 # Screen coordinates of upper-left corner of sprite var x: pair, y: pair # Possible values: 8, 16, 32, 48 var width: byte, height: byte # Layer: # 0 = in front of all tilemaps # 1 = behind tilemap A, in front of B # 2 = behind tilemap B, in front of C # 3 = behind all tilemaps # # Transform: # +4 = flip X (horizontal) # +8 = flip Y (vertical) # +16 = rotate clockwise 90 degrees, only if sprite is square var flags: byte # The theme is an index (0..31) into io::themes var theme: byte # The address of the pixel buffer. To hide the sprite, set pixels_address=0 var pixels_address: int var reserved: int end class # ----------------------------------------------------------------------------- class io_tilemap # size 16 # Screen coordinates of upper-left corner of tilemap var x: pair, y: pair # Possible values: 16, 32, 64, 128, 256 var col_count: pair, row_count: pair # A pointer to a grid of tile codes arranged in left-to-right rows, # starting from the top row. the array index is row*col_count+col. # Each tile code is a 16-bit pair: # bits 0..9 encode the tile index (0..1023) from the tileset # bits 10..14 encode the theme index (0..31) into io::themes # bit 15 if set, io::matte_color replaces any clear pixels in this tile var tile_codes_address: int # 0 = wrap: the map wraps around, repeating infinitely in every direction # 1 = clip: cells outside the map bounds are transparent var edge_mode: byte # ------------------ # Tileset properties # 0 = tiles are 8 x 8 pixels # 1 = tiles are 16 x 16 pixels var jumbo: byte # Normally tileset_X_addresses entries point to contiguous tiles where: # tile_pixel(x,y) = address + y*tile_width + x # ...with tile_width being 8 for regular tiles or 16 for jumbo tiles. # If tile_pitch is in the range 1..512, then this formula changes to: # tile_pixel(x,y) = address + y*tile_pitch + x # With tile_pitch>tile_width, the gaps enable a block of tilemap cells to # be mapped to a contiguous linear frame buffer for advanced rasterizers. var tile_pitch: pair # 0 = default behavior (use tile_width) end class # ----------------------------------------------------------------------------- class io_input_event # size 4 # 0 = key down # 1 = key repeat # 2 = key up # 3 = resync; all keys up var kind: byte # Identifies the keyboard button, or 0 for alternate input sources var keyid: byte # Identifies the translated HASCII, or 0 for keys that don't type anything. # Right now the high byte is always 0, but in the future it will be used # for UTF-16 characters. var hascii: pair end class # ----------------------------------------------------------------------------- class io_hand_controller # size 8 # 0 = disabled # 1 = gamepad # 16 = mouse (x and y are rolling counters) var kind: byte # +1 = a / mouse main button # +2 = b / mouse middle button # +4 = c / mouse secondary button # +8 = d var buttons: byte # Gamepad: (left) -1023 .. +1023 (right) var x: pair # Gamepad: (up) -1023 .. +1023 (down) var y: pair var reserved: pair end class # ============================================================================= # IO MODULE # ============================================================================= # MMIO location mappings for all hardware interfaces. # ----------------------------------------------------------------------------- module io # Memory region layout var ram_end_address: int located at $d0_0000 # 001x_xxxx var rom_end_address: int located at $d0_0004 # 00cx_xxxx var io_end_address: int located at $d0_0008 # 00dx_xxxx # Used with irq_wake_mask below. Whenever the specified event occurs, # the hardware sets the corresponding bit. The program can clear a bit # by setting it (the "write 1 to clear" or "W1C" convention). # +1 = whenever Palix paints a video frame (buffered or unbuffered) # +2 = whenever Jamdac advances an io_audio_queue.read_address var irq_pending: byte located at $d0_0010 # To save electricity, the CPU can be temporarily paused using the "sleep" # instruction. The CPU will wake up again when irq_pending matches any of # the irq_wake_mask bits (level triggering). var irq_wake_mask: byte located at $d0_0012 # Custom handling for "fail", "trace", "request" and other CPU events var cpu_event_flags: byte located at $d0_0013 var cpu_event_ip: int located at $d0_0014 var cpu_event_fp: int located at $d0_0018 var cpu_event_value: int located at $d0_001c var cpu_fail_handler: int located at $d0_0020 var cpu_trace_handler: int located at $d0_0024 var cpu_request_handler: int located at $d0_0028 # Real time clock var clock_year: pair located at $d0_0030 var clock_month: byte located at $d0_0032 var clock_day: byte located at $d0_0033 var clock_day_of_week: byte located at $d0_0034 var clock_hours: byte located at $d0_0035 var clock_minutes: byte located at $d0_0036 var clock_seconds: byte located at $d0_0037 # Starts from 0, incrementing every millisecond. Your program can modify it. var clock_ms: int located at $d0_0038 # +1 = floating point add-on var product_features: byte located at $d0_003c # 0 = disabled; 1 = stack fault when sp > gp var stack_guard: byte located at $d0_003d # 0 = disable Jamdac; 1 = enable Jamdac, rebooting its CPU var jamdac_enable: byte located at $d0_003e # (input_queue_end_index is at $d0_003f) # Serial port output # # (see status codes below) var transmit_status: byte located at $d0_0040 # 0 = cancel transmit (can be set before a final status is received) # 1 = start transmit var transmit_command: byte located at $d0_0041 var transmit_channel: byte located at $d0_0042 var transmit_next_write_index: byte located at $d0_0043 var transmit_next_read_index: byte located at $d0_0044 # Serial port input # # (see status codes below) var receive_status: byte located at $d0_0048 # 0 = cancel listening (can be set before a final status is received) # 1 = start listening var receive_command: byte located at $d0_0049 var receive_channel: byte located at $d0_004a var receive_next_write_index: byte located at $d0_004b var receive_next_read_index: byte located at $d0_004c # Storage controller # # (see status codes below) var drive_status: byte located at $d0_0050 # 1 = get info # 2 = read sector # 3 = write sector var drive_command: byte located at $d0_0051 # 0 = flash chip var drive_volume: byte located at $d0_0052 # 1 = flash chip, sector size is 1024, 4 sectors total per cartridge var drive_info_type: byte located at $d0_0053 var drive_info_total_sectors: int located at $d0_0054 var drive_sector_num: int located at $d0_0058 var drive_dma_address: int located at $d0_005c # Status codes reported by: transmit_status, receive_status, drive_status # # 0 = done # 8 = error: device not present # 9 = error: device not present, reached end # 10 = error: invalid command # 11 = error: invalid sector number # 12 = error: memory fault # # 128 = start command - written by program to start an operation # 129 = busy - will change to a "ready code" (< 128) after the work is done inset gamepad_0: io_hand_controller located at $d0_0060 inset gamepad_1: io_hand_controller located at $d0_0068 inset gamepads: io_hand_controller[inset 2] located at $d0_0060 inset mouse: io_hand_controller located at $d0_0078 # $d0_0080 - $d0_00bf is regular RAM designated as the kernel scratchpad. # In the framework kernel implementation, the following variables are # placed within this region: var kernel_heap_start_address: int located at $d0_0080 # Conventionally, the heap end is the stack start var kernel_stack_start_address: int located at $d0_0084 var kernel_stack_end_address: int located at $d0_0088 # The kernel::scratch_bytes array occupies $d0_00a0 - $d0_00bf # Keyboard var input_queue_end_index: byte located at $d0_003f inset input_queue: io_input_event[inset 16] located at $d0_00c0 # ..$d0_00ff # Serial buffers inset transmit_buffer: byte[size 256] located at $d0_0100 inset receive_buffer: byte[size 256] located at $d0_0200 # Palix video system var background_color: byte located at $d0_0300 var matte_color: byte located at $d0_0301 # Increments for each video frame, even if painting is skipped. # The refresh rate is 30 frames/sec = 33.333 ms/frame var frame_counter: pair located at $d0_0302 # 0 = unbuffered # 1 = buffered # 3 = buffered, paint requested; afterwards the value reverts to 1 var paint_mode: byte located at $d0_0304 inset tilemap_a: io_tilemap located at $d0_0310 # ..$d0_031f inset tilemap_b: io_tilemap located at $d0_0320 # ..$d0_032f inset tilemap_c: io_tilemap located at $d0_0330 # ..$d0_033f inset sprites: io_sprite[inset 64] located at $d0_0400 # ..$d0_07ff inset themes: io_theme[inset 32] located at $d0_0800 # ..$d0_0fff inset tileset_a_addresses: int[size 1024] located at $d0_1000 # ..$d0_1fff inset tileset_b_addresses: int[size 1024] located at $d0_2000 # ..$d0_2fff inset tileset_c_addresses: int[size 1024] located at $d0_3000 # ..$d0_3fff # Jamdac inset audio_queues: io_audio_queue[inset 6] located at $d0_4000 # ..$d0_4bff end module