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

;; This program assembles a statically linked Windows PE32+ image
;; that prints a familiar greeting.

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

(define (pe64-header entry)
  `((%label pe-start)
    ,@(pe-64-assembler (make-pe-image
                         PE-MACHINE-AMD64 3 #x022F
			 '(- code-end text) '(- idata-end data)
                         `(- ,entry #x400000) '(- text #x400000)'
			 #f #x400000 '(- pe-end pe-start)
			 PE-SUBSYSTEM-WINDOWS-CUI
                         ;; data directories
			 `((,PE-IMPORT-TABLE (- import-table/start #x400000)
                                             (- import-table/end import-table/start)))
			 ))
    ,@(pe-64-assembler (make-pe-section
                         ".text" '(- code-end text) '(- text #x400000)
                         (fxior PE-SECTION-CODE PE-SECTION-MEM-READ PE-SECTION-MEM-EXECUTE)))
    ,@(pe-64-assembler (make-pe-section
                         ".data" '(- data-end data) '(- data #x400000)
                         (fxior PE-SECTION-INITIALIZED-DATA PE-SECTION-MEM-READ PE-SECTION-MEM-WRITE)))
    ,@(pe-64-assembler (make-pe-section
                         ".idata" '(- idata-end idata) '(- idata #x400000)
                         (fxior PE-SECTION-INITIALIZED-DATA PE-SECTION-MEM-READ PE-SECTION-MEM-WRITE)))
    ))

(define hello-world "Hello, world!\r\n")

(define demo-text
  `((%mode 64)
    (%origin #x400000)
    ,@(pe64-header 'code-start)
    (%align 4096 0)
    (%section text)
    (%label text)
    (%label code-start code-end global func)
    (push rbp)
    (mov  rbp rsp)
    (sub  rsp 32)

    (mov rcx -11)
    (call (mem64+ GetStdHandle))
    (mov rcx rax)
    (mov rdx hello-string)
    (mov r8 (- hello-string-end hello-string))
    (mov r9 ret)
    (call (mem64+ WriteFile))
    (mov rcx 0)
    (call (mem64+ ExitProcess))

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

(define demo-data
  `((%align 4096 0)
    (%section data)
    (%label data)
    (%label ret) (%u64 0)
    (%label hello-string)
    (%utf8z ,hello-world)
    (%label hello-string-end)
    (%align #x200 0)
    (%label data-end)

    (%align 4096 0)
    (%section idata)
    (%label idata)
    ,@(pe-64-assembler (make-import-table #x400000
                         `(,(make-import-module "kernel32.dll"
                                                `(,(make-import-function "ExitProcess")
                                                  ,(make-import-function "GetStdHandle")
                                                  ,(make-import-function "WriteFile")))) ))
    (%align #x200 0)
    (%label idata-end)
    (%align 4096 0)
    (%label pe-end)
    ))

(define fn "x86-win64-demo.exe")

(call-with-port (open-file-output-port fn (file-options no-fail))
  (lambda (p)
    (let-values (((machine-code symbol-table)
                  (assemble (append demo-text demo-data))))
      (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))
