xbasic64 1.0.0

A BASIC-to-x86_64 native code compiler targeting 1980s-era BASIC dialects
# ==============================================================================
# BASIC Runtime: Input Functions
# ==============================================================================
#
# Keyboard input functions for the BASIC INPUT statement. These read from stdin
# using libc scanf.
#
# Buffer and format strings (from data_defs.s):
#   _input_buf      = 1024 bytes for string input
#   _fmt_input      = "%lf"           - read double
#   _fmt_input_str  = "%1023[^\n]"    - read up to 1023 chars, stop at newline
#
# Note: scanf with %[^\n] reads until newline but does NOT consume the newline.
# We call getchar() after scanf to consume the trailing newline, preventing it
# from being read by the next INPUT statement.
#
# String Return Convention:
#   Strings are returned as (pointer, length) pairs:
#   - rax = pointer to string data
#   - rdx = length in bytes
# ==============================================================================

# ------------------------------------------------------------------------------
# _rt_input_string - Read a line of text from stdin
# ------------------------------------------------------------------------------
# Reads characters until newline (which is not included in result).
# Uses a static buffer, so the returned pointer is only valid until the next
# call to _rt_input_string.
#
# Arguments: none
#
# Returns:
#   rax = pointer to string data (in _input_buf)
#   rdx = length of string
#
# Implementation:
#   1. Clear buffer (set first byte to 0 for empty input case)
#   2. scanf("%1023[^\n]", buffer) - read up to 1023 chars
#   3. getchar() - consume the trailing newline
#   4. Calculate string length by scanning for null terminator
# ------------------------------------------------------------------------------
.globl _rt_input_string
_rt_input_string:
    push rbp
    mov rbp, rsp
    sub rsp, 16                     # Stack alignment
    # Clear buffer in case of empty input
    lea rdi, [rip + _input_buf]
    mov BYTE PTR [rdi], 0           # Empty string if scanf reads nothing
    # Read string: scanf("%1023[^\n]", buffer)
    lea rsi, [rip + _input_buf]     # destination buffer (2nd arg)
    lea rdi, [rip + _fmt_input_str] # format string (1st arg)
    xor eax, eax                    # no vector args
    call {libc}scanf
    # Consume trailing newline that scanf left behind
    call {libc}getchar
    # Calculate string length (scan for null terminator)
    lea rax, [rip + _input_buf]     # rax = start of string
    xor rdx, rdx                    # rdx = length counter
.Linput_len:
    cmp BYTE PTR [rax + rdx], 0     # check for null terminator
    je .Linput_done
    inc rdx                         # length++
    jmp .Linput_len
.Linput_done:
    # Return: rax = pointer (already set), rdx = length
    leave
    ret

# ------------------------------------------------------------------------------
# _rt_input_number - Read a numeric value from stdin
# ------------------------------------------------------------------------------
# Reads a double-precision floating point number. BASIC's INPUT statement
# converts this to the appropriate type (Integer, Long, Single, Double).
#
# Arguments: none
#
# Returns:
#   xmm0 = the number read (double)
#
# Implementation:
#   1. scanf("%lf", &local_var) - read double into stack
#   2. getchar() - consume trailing newline
#   3. Load result into xmm0
# ------------------------------------------------------------------------------
.globl _rt_input_number
_rt_input_number:
    push rbp
    mov rbp, rsp
    sub rsp, 16                     # Space for local double + alignment
    # Read double: scanf("%lf", &result)
    lea rsi, [rbp - 8]              # address of local variable (2nd arg)
    lea rdi, [rip + _fmt_input]     # format string "%lf" (1st arg)
    xor eax, eax                    # no vector args
    call {libc}scanf
    # Consume trailing newline
    call {libc}getchar
    # Load result into xmm0
    movsd xmm0, QWORD PTR [rbp - 8]
    leave
    ret