# ==============================================================================
# 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