#!/usr/bin/env scheme-script
;; -*- mode: scheme; coding: utf-8 -*- !#
;; Demo for (machine-code x86 assembler), makes an EFI binary
;; Copyright © 2025 Vadym Kochan <vadim4j@gmail.com>
;; SPDX-License-Identifier: MIT
#!r6rs

;; This program assembles an EFI 64-bit application
;; that prints a hello message to the screen.

(import
  (rnrs (6))
  (machine-code assembler x86)
  (machine-code assembler pe)
  (machine-code format pe))

(define IMAGE-BASE 0)

(define (pe64-header entry)
  `((%label pe-start)
    ,@(pe-64-assembler (make-pe-image
                         PE-MACHINE-AMD64 1 #x2e00
			 '(- code-end text) 0
                         `(- ,entry ,IMAGE-BASE) `(- text ,IMAGE-BASE)
			 #f IMAGE-BASE '(- pe-end pe-start)
			 PE-SUBSYSTEM-EFI-APPLICATION
                         ;; data directories
			 '()
			 ))
    ,@(pe-64-assembler (make-pe-section
                         ".text" '(- code-end text) `(- text ,IMAGE-BASE)
                         (fxior PE-SECTION-CODE PE-SECTION-MEM-READ PE-SECTION-MEM-EXECUTE)))
    ))

(define EFI-SYSTEM-TABLE.ConsoleOutHandle 56) ;; EFI_HANDLE
(define EFI-SYSTEM-TABLE.ConOut           64) ;; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL

(define EFI-SIMPLE-TEXT-OUTPUT-PROTOCOL.Reset              0)
(define EFI-SIMPLE-TEXT-OUTPUT-PROTOCOL.OutputString       8)

(define (STR s)
  (append
    (map (lambda (c) `(%u16 ,(char->integer c)))
         (string->list s))
    '((%u16 0))))

(define demo-text
  `((%mode 64)
    (%origin ,IMAGE-BASE)
    ,@(pe64-header 'code-start)
    (%align 4096 0)
    (%section text)
    (%label text)
    (%label code-start code-end global func)

    (mov rsi rdx)

    ;; Reset the screen
    (mov rax (mem64+ rsi ,EFI-SYSTEM-TABLE.ConOut))
    (mov rbx (mem64+ rax ,EFI-SIMPLE-TEXT-OUTPUT-PROTOCOL.Reset))

    (mov rcx rax)
    (mov rdx 0)
    (call rbx)

    ;; Print the string
    (mov rax (mem64+ rsi ,EFI-SYSTEM-TABLE.ConOut))
    (mov rbx (mem64+ rax ,EFI-SIMPLE-TEXT-OUTPUT-PROTOCOL.OutputString))

    (mov rcx rax)
    (lea rdx (mem64+ rip hello-string))
    (call rbx)

    ;; Run run run run run ...
    (%label run)
    (hlt)
    (jmp run)

    (%label hello-string)
    ,@(STR "Hello from EFI world!!!")

    (%align #x200 0)
    (%label code-end)

    (%label pe-end)))

(define fn "x86-efi64-demo.efi")

(call-with-port (open-file-output-port fn (file-options no-fail))
  (lambda (p)
    (let-values (((machine-code symbol-table)
                  (assemble (append demo-text))))
      (put-bytevector p machine-code)
      (close-port p)
      (let-values (((syms addrs) (hashtable-entries symbol-table)))
        (display "Symbol table:\n")
        (vector-for-each
         (lambda (addr sym)
           (display (number->string addr 16))
           (display #\space)
           (display sym)
           (newline))
         addrs syms)
        (newline)))))

(display "Wrote ")
(display fn)
(newline)
(flush-output-port (current-output-port))
