xbasic64 1.0.0

A BASIC-to-x86_64 native code compiler targeting 1980s-era BASIC dialects
# ==============================================================================
# BASIC Runtime: DATA/READ/RESTORE Support
# ==============================================================================
#
# Functions implementing BASIC's DATA/READ/RESTORE statements.
#
# DATA Statement Overview:
#   DATA statements define constant values that can be read sequentially by
#   READ statements. The compiler collects all DATA values and emits them
#   into _data_table in the .data section.
#
# Data Table Format:
#   Each entry is 16 bytes (for alignment):
#
#   Offset  Size  Content
#   ------  ----  -------
#   0       8     Type tag: 0=integer, 1=float, 2=string
#   8       8     Value: integer, double bits, or string pointer
#
#   Example table for DATA 42, 3.14, "hello":
#     Entry 0: [type=0] [value=42]           (integer)
#     Entry 1: [type=1] [value=0x40091EB...]  (double bits for 3.14)
#     Entry 2: [type=2] [value=ptr to "hello"] (string pointer)
#
# Global State:
#   _data_table = Array of 16-byte entries (generated by compiler)
#   _data_count = Number of entries in table
#   _data_ptr   = Current read position (0-based index)
#
# Note: The compiler generates _data_table with entries for all DATA statements
# in order of appearance in the source code.
# ==============================================================================

# ------------------------------------------------------------------------------
# _rt_read_number - Read next DATA value as a number
# ------------------------------------------------------------------------------
# Reads the next value from the DATA table and returns it as a double.
# Type conversion is performed automatically:
#   - Integer (type 0): Convert to double
#   - Float (type 1): Return as-is
#   - String (type 2): Parse with strtod (like VAL function)
#
# Arguments: none
#
# Returns:
#   xmm0 = value as double
#
# Side effect: Advances _data_ptr to next entry
#
# Error handling: No bounds checking - reading past end is undefined behavior.
# BASIC programs are expected to READ only as many values as they DATA'd.
# ------------------------------------------------------------------------------
.globl _rt_read_number
_rt_read_number:
    push rbp
    mov rbp, rsp
    # Calculate address: &_data_table[_data_ptr]
    # Each entry is 16 bytes, so offset = _data_ptr * 16 = _data_ptr << 4
    mov rax, QWORD PTR [rip + _data_ptr]
    shl rax, 4                          # offset = index * 16
    lea rcx, [rip + _data_table]
    add rcx, rax                        # rcx = entry address
    # Load type tag
    mov rax, QWORD PTR [rcx]            # rax = type (0=int, 1=float, 2=string)
    cmp rax, 2
    je .Lread_str_as_num                # string needs special handling
    # Load value (works for both int and float - float bits in memory)
    movsd xmm0, QWORD PTR [rcx + 8]
    cmp rax, 0
    jne .Lread_num_done                 # if float, we're done
    # Integer: convert to double
    mov rax, QWORD PTR [rcx + 8]        # load as integer
    cvtsi2sd xmm0, rax                  # convert to double
.Lread_num_done:
    inc QWORD PTR [rip + _data_ptr]     # advance to next entry
    leave
    ret
.Lread_str_as_num:
    # String: parse with strtod
    mov rdi, QWORD PTR [rcx + 8]        # string pointer
    xor rsi, rsi                        # endptr = NULL
    call {libc}strtod                   # returns double in xmm0
    inc QWORD PTR [rip + _data_ptr]     # advance to next entry
    leave
    ret

# ------------------------------------------------------------------------------
# _rt_read_string - Read next DATA value as a string
# ------------------------------------------------------------------------------
# Reads the next value from the DATA table and returns it as a string.
# Currently only handles string-typed DATA entries; numeric entries
# should not be READ into string variables (BASIC would convert, we don't).
#
# Arguments: none
#
# Returns:
#   rax = pointer to string data
#   rdx = string length
#
# Side effect: Advances _data_ptr to next entry
# ------------------------------------------------------------------------------
.globl _rt_read_string
_rt_read_string:
    push rbp
    mov rbp, rsp
    # Calculate entry address
    mov rax, QWORD PTR [rip + _data_ptr]
    shl rax, 4                          # offset = index * 16
    lea rcx, [rip + _data_table]
    add rcx, rax                        # rcx = entry address
    # Load string pointer (assumes type is string)
    mov rax, QWORD PTR [rcx + 8]        # rax = string pointer
    # Calculate length using strlen (DATA strings are null-terminated)
    mov rdi, rax                        # string pointer for strlen
    call {libc}strlen                   # returns length in rax
    mov rdx, rax                        # length → rdx
    # Reload string pointer (strlen clobbered rax)
    mov rax, QWORD PTR [rip + _data_ptr]
    shl rax, 4
    lea rcx, [rip + _data_table]
    add rcx, rax
    mov rax, QWORD PTR [rcx + 8]        # rax = string pointer
    # Advance to next entry
    inc QWORD PTR [rip + _data_ptr]
    leave
    ret

# ------------------------------------------------------------------------------
# _rt_restore - Reset DATA pointer (RESTORE statement)
# ------------------------------------------------------------------------------
# Resets the DATA read position, allowing DATA to be re-read from the beginning
# or from a specific position.
#
# Arguments:
#   rdi = new position (0-based index into data table)
#         RESTORE with no argument passes 0
#         RESTORE n would pass the index of DATA following line n (not implemented)
#
# Returns: nothing
# ------------------------------------------------------------------------------
.globl _rt_restore
_rt_restore:
    mov QWORD PTR [rip + _data_ptr], rdi
    ret