Initial commit.
commit
b09b9283cd
@ -0,0 +1,49 @@
|
||||
.PHONY: all rebuild clean spotless newimage
|
||||
|
||||
all: tos.sys
|
||||
|
||||
rebuild: clean all
|
||||
|
||||
run:
|
||||
qemu-system-x86_64 -vga std -smp 8 -m 256 -drive id=disk,file=bmfs.image,format=raw,if=none -device ahci,id=ahci -device ide-drive,drive=disk,bus=ahci.0 -name "LLL"
|
||||
|
||||
tos.sys: vm.bin pure64.sys
|
||||
dd if=/dev/zero of=kernel.bin bs=1M count=1
|
||||
dd if=vm.bin of=kernel.bin bs=512 conv=notrunc
|
||||
dd if=heap.bin of=kernel.bin seek=256 bs=512 conv=notrunc
|
||||
./bmfs bmfs.image write kernel.bin
|
||||
cat pure64.sys kernel64.sys > tos.sys
|
||||
dd if=tos.sys of=bmfs.image bs=512 seek=16 conv=notrunc
|
||||
|
||||
vm.bin: vm.o vm-loader.o heap.bin
|
||||
gcc -T vm.ld -o vm.bin -ffreestanding -O3 -nostdlib vm-loader.o vm.o -lgcc -mno-red-zone
|
||||
|
||||
vm.o: vm.c prescheme.h
|
||||
gcc -O3 -o vm.o -c vm.c -nostdlib -nostartfiles -nodefaultlibs -mno-red-zone -std=gnu99 -ffreestanding
|
||||
|
||||
vm.c: ev.scm ps-macros.scm packages.scm terminal.scm
|
||||
scheme48 -i ps-vm-compiler.image < compile-vm.scm
|
||||
|
||||
vm-loader.o: vm-loader.asm
|
||||
nasm -f elf64 vm-loader.asm -o vm-loader.o
|
||||
|
||||
heap.bin: heap-writer.scm ev.scm ev-runtime.scm ev-environment.scm
|
||||
csi -s heap-writer.scm
|
||||
|
||||
heap-debug.bin: heap-writer.scm ev.scm ev-runtime.scm ev-environment.scm
|
||||
csi -D debug -s heap-writer.scm
|
||||
|
||||
newimage:
|
||||
dd if=/dev/zero of=bmfs.image bs=1M count=8
|
||||
./bmfs bmfs.image format /force
|
||||
dd if=bmfs_mbr.sys of=bmfs.image bs=512 conv=notrunc
|
||||
./bmfs bmfs.image create kernel.bin 1
|
||||
make clean
|
||||
|
||||
clean:
|
||||
rm -f *.o *.so *.bin tos.sys heap-writer vm.c
|
||||
|
||||
spotless:
|
||||
rm -f bmfs.image
|
||||
make clean
|
||||
|
@ -0,0 +1,17 @@
|
||||
`(((quote set! type value eq?
|
||||
nil env
|
||||
+ -
|
||||
cons car cdr
|
||||
eval record vau wrap
|
||||
byte-set!
|
||||
type-eq?
|
||||
xyz)
|
||||
|
||||
,(make-quote) ,(make-assign) ,(make-type) ,(make-value) ,(make-eq?)
|
||||
,(make-nil) ,(make-env)
|
||||
,(make-add) ,(make-sub)
|
||||
,(make-cons) ,(make-car) ,(make-cdr)
|
||||
,(make-eval) ,(make-record) ,(make-vau) ,(make-wrap)
|
||||
,(make-byte-set!)
|
||||
,(make-type-eq?)
|
||||
10))
|
@ -0,0 +1,114 @@
|
||||
(
|
||||
(set! quote (vau ((x)) x))
|
||||
|
||||
(set! list (wrap (vau (elements) elements)))
|
||||
|
||||
(set! lambda
|
||||
(vau ((formals body) env)
|
||||
(wrap (eval (list (quote vau) (list formals) body) env))))
|
||||
|
||||
(set! *vga-mem* 753664)
|
||||
|
||||
(set! $eq?
|
||||
(vau ((a b conseq alt) env)
|
||||
(eval (eq? (eval a env) (eval b env)
|
||||
conseq
|
||||
alt)
|
||||
env)))
|
||||
|
||||
(set! $type-eq?
|
||||
(vau ((a b conseq alt) env)
|
||||
(eval (type-eq? (eval a env) (eval b env)
|
||||
conseq
|
||||
alt)
|
||||
env)))
|
||||
|
||||
(set! *nil-record* (nil))
|
||||
|
||||
(set! $null?
|
||||
(vau ((x conseq alt) env)
|
||||
(eval
|
||||
($eq? (eval x env) *nil-record* conseq alt)
|
||||
env)))
|
||||
|
||||
;; BUG not finding the variable bindings in a VAU
|
||||
(lambda () 1)
|
||||
|
||||
;; (set! map
|
||||
;; (lambda (proc lis)
|
||||
;; ($null? lis
|
||||
;; lis
|
||||
;; (cons (proc (car lis)) (map proc (cdr lis))))))
|
||||
|
||||
;; ;; ((vau ((x y))
|
||||
;; ;; body)
|
||||
;; ;; (1 2))
|
||||
;; (set! let
|
||||
;; (vau ((vars body) env)
|
||||
;; ((eval (list (quote vau) (list (map car vars))
|
||||
;; body)
|
||||
;; env))))
|
||||
|
||||
;; (set! cseq
|
||||
;; (lambda (n op)
|
||||
;; ($eq? n 5
|
||||
;; 0
|
||||
;; (cseq (+ n 1) (write-char (+ n 80))))))
|
||||
|
||||
;; (set! *a-pair* (cons 98 99))
|
||||
;; (set! $pair?
|
||||
;; (vau ((obj conseq alt) env)
|
||||
;; (eval ($type-eq? (eval obj env) *a-pair* conseq alt) env)))
|
||||
|
||||
;; (set! char-rec-num 100)
|
||||
;; (set! char-rec (record char-rec-num))
|
||||
;; (set! char (lambda (n) (cons char-rec n)))
|
||||
;; (set! char-val (lambda (c) (cdr c)))
|
||||
|
||||
;; (set! $char?
|
||||
;; (vau ((obj conseq alt) env)
|
||||
;; ((lambda (res)
|
||||
;; (eval ($pair? res
|
||||
;; (type-eq? (car res) char-rec conseq alt)
|
||||
;; alt)
|
||||
;; env))
|
||||
;; obj)))
|
||||
|
||||
;; (set! write-char
|
||||
;; ((lambda (pos)
|
||||
;; (lambda (c)
|
||||
;; (list (byte-set! pos (char-val c)) (set! pos (+ pos 2)))))
|
||||
;; (+ *vga-mem* 8)))
|
||||
|
||||
;; (set! write-val
|
||||
;; ((lambda (pos)
|
||||
;; (lambda (v)
|
||||
;; (list (byte-set! pos v) (set! pos (+ pos 2)))))
|
||||
;; (+ *vga-mem* 8)))
|
||||
|
||||
;; ;; (map write-char
|
||||
;; ;; (map char
|
||||
;; ;; (list 72 101 108 108 111 44 32 119 111 114 108 100 33)))
|
||||
|
||||
;; (set! print
|
||||
;; (lambda (obj)
|
||||
;; ($char? obj
|
||||
;; (map write-char (list (char 35) (char 92) obj))
|
||||
;; ($pair? obj
|
||||
;; (map print (list (char 40) (map print obj) (char 41)))
|
||||
;; (write-char (char 33))
|
||||
;; ))))
|
||||
|
||||
;; (set! print2
|
||||
;; (lambda (obj)
|
||||
;; ($char? obj
|
||||
;; (write-char (char 72))
|
||||
;; (write-char (char 73)))))
|
||||
|
||||
|
||||
|
||||
;; ;; BUG? type-eq? is evaluating its arguments? Isn't it supposed to
|
||||
;; ;; though since it's a primitive? Yes, it is.
|
||||
;; (write-val (type-eq? (car (char 80)) (car (char 100)) 89 78))
|
||||
|
||||
)
|
@ -0,0 +1,740 @@
|
||||
;; Expands to variables prepended with enum name and a period (.). See
|
||||
;; enum 'err' for an example.
|
||||
(define-syntax enum
|
||||
(lambda (x r c)
|
||||
(let ((name (symbol->string (cadr x))))
|
||||
`(,(r 'begin)
|
||||
,@(map (lambda (var/val)
|
||||
`(,(r 'define) ,(string->symbol
|
||||
(string-append name
|
||||
"."
|
||||
(symbol->string (car var/val))))
|
||||
,(cadr var/val)))
|
||||
(caddr x))))))
|
||||
|
||||
(define-syntax define*
|
||||
(syntax-rules ()
|
||||
((_ x)
|
||||
(syntax-error "invalid define* form " x))
|
||||
((_ var val)
|
||||
(define var val))
|
||||
((_ var val rest ...)
|
||||
(begin (define var val)
|
||||
(define* rest ...)))))
|
||||
|
||||
;; nan: not a number
|
||||
;; nap: not a pair
|
||||
;; nar: not a record
|
||||
;; example: err.nan-add
|
||||
(enum err
|
||||
((nan-add 0)
|
||||
(nan-sub 1)
|
||||
(nan-mult 2)
|
||||
(nan-quotient 3)
|
||||
(nan-remainder 4)
|
||||
(nap-car 5)
|
||||
(nap-cdr 6)
|
||||
(nap-set-car! 7)
|
||||
(nap-set-cdr! 8)
|
||||
(unknown-inst 9)
|
||||
(bad-arg-count-apply 10)
|
||||
(ran-out-of-fuel 11)
|
||||
(nar-record-type 12)
|
||||
(nar-record-value 13)
|
||||
(nan-untag-number 14)
|
||||
(primitive-not-found 15)
|
||||
(operation-not-found 16)
|
||||
(variable-not-found 17)
|
||||
(type-not-found 18)
|
||||
(oom 19)
|
||||
(nap-record-type 20) ; for record objs
|
||||
))
|
||||
|
||||
(define* pair-mask #b1111
|
||||
pair-spec #b0000
|
||||
number-mask #b1
|
||||
number-spec #b1 ; also in tag-number define-records macro
|
||||
record-mask #b10
|
||||
record-spec #b10)
|
||||
|
||||
;; NOTE! if word-size is not half of block-size update the algorithm
|
||||
;; in (cons).
|
||||
(define* *word-size* 8 ; bytes
|
||||
*block-size* 16) ; bytes
|
||||
|
||||
;;;;;;; PreScheme ;;;;;;;
|
||||
(cond-expand
|
||||
(prescheme
|
||||
(define* halt (external "PS_HALT" (=> () null))
|
||||
vm-init (external "vm_init" (=> () null)))
|
||||
|
||||
; also in tag-number define-records macro
|
||||
(define (twos-complement obj)
|
||||
(if (>= obj 0)
|
||||
(shift-left obj 1)
|
||||
(shift-left (+ (bitwise-not (abs obj)) 1) 1)))
|
||||
|
||||
; also in tag-number define-records macro
|
||||
(define* (tag-number obj) (bitwise-xor (twos-complement obj) number-spec)
|
||||
(tag-record obj) (bitwise-xor (shift-left obj 2) record-spec)
|
||||
(tag-nil) (tag-record (tag-number 0)) ; TODO check if prescheme inlines this
|
||||
(nil) (tag-nil))
|
||||
|
||||
(define (matches-spec? obj mask spec) (= (bitwise-and obj mask) spec))
|
||||
|
||||
(define* (pair? obj) (matches-spec? obj pair-mask pair-spec)
|
||||
(number? obj) (matches-spec? obj number-mask number-spec)
|
||||
(record? obj) (matches-spec? obj record-mask record-spec)
|
||||
(nil? obj) (= obj (tag-nil))) ; TODO make sure prescheme inlines
|
||||
|
||||
(define (untag-number obj)
|
||||
(if (>= obj 0)
|
||||
(arithmetic-shift-right obj 1)
|
||||
(- 0 (+ (arithmetic-shift-right (bitwise-not obj) 1) 1))))
|
||||
(define (untag-record obj) (arithmetic-shift-right obj 2))
|
||||
|
||||
(define *error* 0)
|
||||
(define *error-obj* 999)
|
||||
|
||||
(define-syntax assert
|
||||
(syntax-rules ()
|
||||
((_ test good err-num print-obj err-val)
|
||||
(if test good (begin (set! *error* err-num) (set! *error-obj* print-obj) err-val)))))
|
||||
|
||||
(define (untag-number/chk n)
|
||||
(assert (number? n) (untag-number n) err.nan-untag-number n 0))
|
||||
;; TODO check for overflows
|
||||
;; TODO this doesn't work for negative numbers!
|
||||
(define (twos-complement-add n n2)
|
||||
(assert (and (number? n) (number? n2)) (+ n n2 -1) err.nan-add (list n n2) 0))
|
||||
|
||||
(define (twos-complement-sub n n2)
|
||||
(assert (and (number? n) (number? n2)) (+ (- n n2) 1) err.nan-sub (list n n2) 0))
|
||||
|
||||
(define (mult n1 n2)
|
||||
(assert (and (number? n1) (number? n2))
|
||||
(tag-number (* (untag-number n1) (untag-number n1)))
|
||||
err.nan-mult
|
||||
(list n1 n2)
|
||||
0))
|
||||
|
||||
(define (quotient/chk n1 n2)
|
||||
(assert (and (number? n1) (number? n2))
|
||||
(tag-number (quotient (untag-number n1) (untag-number n2)))
|
||||
err.nan-quotient
|
||||
(list n1 n2)
|
||||
0))
|
||||
(define (remainder/chk n1 n2)
|
||||
(assert (and (number? n1) (number? n2))
|
||||
(tag-number (remainder (untag-number n1) (untag-number n2)))
|
||||
err.nan-remainder
|
||||
(list n1 n2)
|
||||
0))
|
||||
|
||||
;; (define (display-error n)
|
||||
;; (unsigned-byte-set! (integer->address 753664) 69)
|
||||
;; (unsigned-byte-set! (integer->address (+ 753664 2)) (+ n 48)))
|
||||
|
||||
(define (display-error n obj)
|
||||
(terminal:put-char #\E)
|
||||
(terminal:put-number n)
|
||||
(terminal:put-char #\<)
|
||||
(print* obj)
|
||||
(terminal:put-char #\>)
|
||||
(terminal:put-char #\space))
|
||||
|
||||
(define *alloc-address* null-address)
|
||||
(define *end-of-memory* 0)
|
||||
(define *oom-pair* 1) ; ??? 0 test as a "tagged" pair, so we aren't going with that
|
||||
|
||||
;; cons overshoots *end-of-memory* by block size
|
||||
(define (cons a b)
|
||||
(let ((start (address->integer *alloc-address*)))
|
||||
(assert (< start *end-of-memory*)
|
||||
(begin
|
||||
(word-set! *alloc-address* a)
|
||||
(word-set! (address+ *alloc-address* *word-size*) b)
|
||||
(set! *alloc-address* (address+ *alloc-address* *block-size*))
|
||||
start)
|
||||
err.oom (tag-number -1) *oom-pair*)))
|
||||
|
||||
(define (car pair)
|
||||
(assert (pair? pair) (word-ref (integer->address pair)) err.nap-car pair (tag-nil)))
|
||||
(define (cdr pair)
|
||||
(assert (pair? pair) (word-ref (address+ (integer->address pair) *word-size*))
|
||||
err.nap-cdr pair (tag-nil)))
|
||||
|
||||
(define (car/chk pair)
|
||||
(assert (pair? pair) (word-ref (integer->address pair)) err.nap-car pair (tag-nil)))
|
||||
(define (cdr/chk pair)
|
||||
(assert (pair? pair) (word-ref (address+ (integer->address pair) *word-size*))
|
||||
err.nap-cdr pair (tag-nil)))
|
||||
|
||||
(define (set-car! pair val)
|
||||
(assert (pair? pair) (word-set! (integer->address pair) val)
|
||||
;; TODO find a better way to get prescheme to have the
|
||||
;; correct return value for the error condition.
|
||||
err.nap-set-car! (list pair val) (word-set! (integer->address #xffff00) 0)))
|
||||
(define (set-cdr! pair val)
|
||||
(assert (pair? pair) (word-set! (address+ (integer->address pair) *word-size*) val)
|
||||
err.nap-set-cdr! (list pair val) (word-set! (integer->address #xffff00) 0)))
|
||||
|
||||
(define (alloc bytes)
|
||||
(let ((beginning-of-block (address->integer *alloc-address*)))
|
||||
(set! *alloc-address*
|
||||
(address+ *alloc-address*
|
||||
(if (<= bytes *block-size*)
|
||||
*block-size*
|
||||
(+ bytes (- *block-size* (remainder bytes *block-size*))))))
|
||||
beginning-of-block))
|
||||
|
||||
(define (record-obj type val)
|
||||
(cons (tag-record type) val))
|
||||
|
||||
(define (record-obj? obj)
|
||||
(and (pair? obj) (record? (car obj))))
|
||||
|
||||
;; If OBJ is a pair that points to a record return its type.
|
||||
(define (record-obj-type obj)
|
||||
(assert (pair? obj)
|
||||
(let ((rec (car obj)))
|
||||
(assert (record? rec) rec
|
||||
err.nar-record-type rec 0))
|
||||
err.nap-record-type obj 0))
|
||||
|
||||
;; If OBJ is a pair that points to a record return its value.
|
||||
(define (record-obj-value obj)
|
||||
(assert (pair? obj)
|
||||
(assert (record? (car obj)) (cdr obj)
|
||||
err.nar-record-value obj 0)
|
||||
err.nap-record-type obj 0))
|
||||
|
||||
(define null? nil?)
|
||||
|
||||
(define-syntax list
|
||||
(syntax-rules ()
|
||||
((_) (tag-nil))
|
||||
((_ element)
|
||||
(cons element (tag-nil)))
|
||||
((_ element r)
|
||||
(cons element (list r)))
|
||||
((_ element r ...)
|
||||
(cons element (list r ...)))))
|
||||
|
||||
(define (list-tail x k)
|
||||
(if (= k 0)
|
||||
x
|
||||
(list-tail (cdr x) (- k 1))))
|
||||
|
||||
(define (list-ref lis k)
|
||||
(car (list-tail lis k)))
|
||||
|
||||
(define (reverse lis)
|
||||
(if (null? lis)
|
||||
lis
|
||||
(let loop ((remaining lis) (new-head (tag-nil)))
|
||||
(if (pair? (cdr remaining))
|
||||
(loop (cdr remaining)
|
||||
(cons (car remaining) new-head))
|
||||
(cons (car remaining) new-head)))))
|
||||
|
||||
(define (cadr x) (car (cdr x))) (define (caddr x) (car (cdr (cdr x))))
|
||||
(define (cadddr x) (car (cdr (cdr (cdr x)))))
|
||||
(define (cddr x) (cdr (cdr x))) (define (cdddr x) (cdr (cdr (cdr x))))
|
||||
) ; end cond-expand prescheme
|
||||
|
||||
|
||||
(else ; cond-expand else
|
||||
|
||||
(define (twos-complement obj)
|
||||
(if (>= obj 0)
|
||||
obj
|
||||
(+ (bitwise-not (abs obj)) 1)))
|
||||
(define (tag-number obj)
|
||||
(bitwise-xor (arithmetic-shift (twos-complement obj) 1) number-spec))
|
||||
(define (untag-number obj)
|
||||
(if (>= obj 0)
|
||||
(arithmetic-shift obj -1)
|
||||
(- 0 (+ (arithmetic-shift (bitwise-not obj) -1) 1))))
|
||||
(define untag-number/chk untag-number)
|
||||
(define (twos-complement-add n n2)
|
||||
(+ n n2 -1))
|
||||
(define (twos-complement-sub n n2)
|
||||
(+ (- n n2) 1))
|
||||
|
||||
(define quotient/chk quotient)
|
||||
(define remainder/chk remainder)
|
||||
|
||||
;;; If you want easier debugging.
|
||||
;; (define (twos-complement-add n n2) (+ n n2))
|
||||
;; (define (twos-complement-sub n n2) (- n n2))
|
||||
;; (define (tag-number n) n)
|
||||
;; (define (untag-number n) n)
|
||||
(define (tag-nil) '())
|
||||
(define (nil) (tag-nil))
|
||||
(define nil? null?)
|
||||
(define-record-type record (tag-record value) record?
|
||||
(value untag-record))
|
||||
|
||||
(define-record-printer record
|
||||
(lambda (obj port)
|
||||
(print "#<record " (untag-record obj) ">")))
|
||||
|
||||
(define (record-obj type val)
|
||||
(cons (tag-record type) val))
|
||||
|
||||
(define (record-obj? obj)
|
||||
(and (pair? obj) (record? (car obj))))
|
||||
|
||||
;; If OBJ is a pair that points to a record return its type.
|
||||
(define (record-obj-type obj)
|
||||
(if (record-obj? obj)
|
||||
(car obj)
|
||||
(error "not a record obj")))
|
||||
|
||||
;; If OBJ is a pair that points to a record return its value.
|
||||
(define (record-obj-value obj)
|
||||
(if (record-obj? obj)
|
||||
(cdr obj)
|
||||
(error "not a record obj")))
|
||||
|
||||
(define (word-set! addr val) (void))
|
||||
(define (unsigned-byte-set! addr val)
|
||||
(display val) (void))
|
||||
(define (integer->address n) n)
|
||||
|
||||
(define *error* 0)
|
||||
(define *error-obj* 999)
|
||||
(define (put-number n) (display n))
|
||||
(define (put-char c) (display c))
|
||||
|
||||
(define (halt) '())
|
||||
(define (initialize) '())
|
||||
|
||||
(define-syntax goto
|
||||
(syntax-rules ()
|
||||
((_ body ...)
|
||||
(begin (body ...)))))
|
||||
|
||||
(define (display-error n obj)
|
||||
(error (conc "VM error: " n "<" obj ">")))
|
||||
|
||||
(define (mult n1 n2) (tag-number (* (untag-number n1) (untag-number n2))))
|
||||
|
||||
)) ; end cond-expand
|
||||
|
||||
(define-syntax define-records
|
||||
(lambda (x r c)
|
||||
;; TODO move iota and conc in to another pre-scheme module and
|
||||
;; import for syntax
|
||||
(define (iota k . start)
|
||||
(define (iota* k i)
|
||||
(if (< i k)
|
||||
(cons i (iota* k (+ i 1)))
|
||||
'()))
|
||||
(let ((start (if (null? start) 0 (car start))))
|
||||
(iota* (+ k start) start)))
|
||||
(define (conc . r)
|
||||
(apply string-append (map (lambda (x) (if (symbol? x) (symbol->string x) x)) r)))
|
||||
(define (tag-number n)
|
||||
(bitwise-xor (twos-complement n) #b1))
|
||||
(define (twos-complement obj)
|
||||
(if (>= obj 0)
|
||||
(arithmetic-shift obj 1)
|
||||
(arithmetic-shift (+ (bitwise-not (abs obj)) 1) 1)))
|
||||
`(begin
|
||||
,@(map
|
||||
(lambda (inst n)
|
||||
(let* ((has-args (list? inst))
|
||||
(name (if has-args (car inst) inst))
|
||||
(args (if has-args (cdr inst) '())))
|
||||
`(begin (,(r 'define) (,(string->symbol (conc 'make- name)) ,@args)
|
||||
,(if has-args
|
||||
`(record-obj ,n (list ,@args))
|
||||
`(tag-record ,n)))
|
||||
,(if has-args
|
||||
`(,(r 'define) (,(string->symbol (string-append (symbol->string name) "-type?")) ,(r 'obj))
|
||||
(,(r 'and) (record-obj? ,(r 'obj))
|
||||
(,(r '=) (untag-record (record-obj-type ,(r 'obj))) ,n)))
|
||||
`(,(r 'define) (,(string->symbol (string-append (symbol->string name) "-type?")) ,(r 'obj))
|
||||
(,(r 'and) (record? ,(r 'obj))
|
||||
(,(r '=) (untag-record ,(r 'obj)) ,n))))
|
||||
,@(if has-args
|
||||
(map (lambda (arg slot)
|
||||
`(,(r 'define) (,(string->symbol
|
||||
(conc name '- arg))
|
||||
,(r 'obj))
|
||||
(list-ref (record-obj-value ,(r 'obj)) ,slot)))
|
||||
args (iota (length args)))
|
||||
'()))))
|
||||
(cdr x) (map tag-number (iota (length (cdr x)) 1)))))) ; start at 1, 0 is nil
|
||||
|
||||
(define-records
|
||||
variable ;; TODO not used?
|
||||
quote
|
||||
assign
|
||||
call/cc
|
||||
(symbol char-list)
|
||||
type
|
||||
value
|
||||
tag
|
||||
untag
|
||||
eq?
|
||||
nil
|
||||
env
|
||||
word-set!
|
||||
word-ref
|
||||
byte-set!
|
||||
byte-ref
|
||||
alloc
|
||||
add
|
||||
sub
|
||||
mult
|
||||
quotient
|
||||
remainder
|
||||
abs
|
||||
cons
|
||||
car
|
||||
cdr
|
||||
eval
|
||||
;; (macro proc)
|
||||
(operative vars dyn-var has-dyn-var? proc static-env)
|
||||
(applicative obj)
|
||||
wrap
|
||||
unwrap
|
||||
set-car!
|
||||
set-cdr!
|
||||
primitive
|
||||
apply
|
||||
record
|
||||
vau
|
||||
type-eq?
|
||||
)
|
||||
|
||||
(cond-expand (prescheme 0) (else (define symbol-type? symbol?)))
|
||||
|
||||
(define (list-of-values exps env)
|
||||
(if (null? exps)
|
||||
(nil)
|
||||
(cons (ev (car exps) env)
|
||||
(list-of-values (cdr exps) env))))
|
||||
|
||||
(define (apply-primitive-proc proc args env)
|
||||
;; TODO check num args
|
||||
;; In process of converting from all primitives having all args
|
||||
;; being evaled to just some of them.
|
||||
(cond ((add-type? proc) (twos-complement-add (ev (car args) env)
|
||||
(ev (car (cdr args)) env)))
|
||||
((sub-type? proc) (twos-complement-sub (ev (car args) env)
|
||||
(ev (car (cdr args)) env)))
|
||||
((car-type? proc) (car/chk (ev (car args) env)))
|
||||
((cdr-type? proc) (cdr/chk (ev (car args) env)))
|
||||
((env-type? proc) env)
|
||||
((eval-type? proc) (ev (car args) (ev (car (cdr args)) env)))
|
||||
;; ((list-of-values-type? proc))
|
||||
((cons-type? proc) (cons (ev (car args) env) (ev (car (cdr args)) env)))
|
||||
((nil-type? proc) (nil))
|
||||
((wrap-type? proc) (make-applicative (ev (car args) env)))
|
||||
;; TODO this doesn't even make sense. Someone could
|
||||
;; (tag-record -1) and then this would say it is a number!
|
||||
;; ((type-type? proc) (cond ((number? (car args)) (tag-number -1))
|
||||
;; ((record? (car args))
|
||||
;; (untag-record (car args)))
|
||||
;; ((nil? (car args)) (tag-number -2))
|
||||
;; ((pair? (car args)) (tag-number -3)) ; TODO possible?
|
||||
;; (else
|
||||
;; (assert #f -3 err.type-not-found (tag-number -99)))))
|
||||
((value-type? proc)
|
||||
(let ((args (list-of-values args env)))
|
||||
(cond ((record? (car args)) (untag-record (car args)))
|
||||
((or (number? (car args)) (pair? args))
|
||||
(car args))
|
||||
(else
|
||||
(assert #f -3 err.type-not-found (car args) (tag-number -98))))))
|
||||
((record-type? proc) (tag-record (ev (car args) env)))
|
||||
;; ((tag-type? proc) (record (car args) (car (cdr args))))
|
||||
;; ((untag-type? proc) (cond ((number? (car args)) (car args))
|
||||
;; ((record? (car args)) (record-value (car args)))))
|
||||
;; ((set-car!-type? proc) (set-car! (car args) (car (cdr args))))
|
||||
((byte-set!-type? proc)
|
||||
(unsigned-byte-set!
|
||||
(integer->address (untag-number/chk (ev (car args) env)))
|
||||
(untag-number/chk (ev (car (cdr args)) env)))
|
||||
(tag-number 0))
|
||||
((eq?-type? proc)
|
||||
(if (= (ev (car args) env) (ev (car (cdr args)) env))
|
||||
(car (cdr (cdr args)))
|
||||
(car (cdr (cdr (cdr args))))))
|
||||
((type-eq?-type? proc)
|
||||
(let ((a (ev (car args) env))
|
||||
(b (ev (cadr args) env)))
|
||||
(cond ((and (pair? a) (pair? b))
|
||||
(car (cdr (cdr args))))
|
||||
((and (record? a) (record? b)
|
||||
(= (untag-record a) (untag-record b)))
|
||||
(car (cdr (cdr args))))
|
||||
((= a b)
|
||||
(car (cdr (cdr args))))
|
||||
(else (car (cdr (cdr (cdr args))))))))
|
||||
(else
|
||||
args
|
||||
;; (assert #f #f err.primitive-not-found (tag-number 0))
|
||||
))
|
||||
)
|
||||
|
||||
(define (ev-sequence exps env)
|
||||
(if (null? (cdr exps))
|
||||
(ev (car exps) env)
|
||||
(begin (ev (car exps) env)
|
||||
(ev-sequence (cdr exps) env))))
|
||||
|
||||
;; (define (ev-apply proc args env)
|
||||
;; (if (pair? proc)
|
||||
;; (ev-sequence (cdr (cdr proc)) (extend-environment (car (cdr proc)) args (car proc)))
|
||||
;; (apply-primitive-proc proc args env)))
|
||||
|
||||
(define (ev-args args env)
|
||||
(let loop ((args args) (res (nil)))
|
||||
(if (null? args)
|
||||
(reverse res) ;; TODO store applicative and/or operative args
|
||||
;; in reverse so we don't have to do a reverse here
|
||||
(loop (cdr args) (cons (ev (car args) env) res)))))
|
||||
|
||||
(define (empty-list seed)
|
||||
(let loop ((acc (nil)) (seed seed))
|
||||
(if (null? seed) acc (loop (cons (nil) acc) (cdr seed)))))
|
||||
|
||||
(define (make-frame variables values)
|
||||
(cons variables values))
|
||||
(define (frame-variables frame) (car frame))
|
||||
(define (frame-values frame) (cdr frame))
|
||||
(define (add-binding-to-frame! var val frame)
|
||||
(set-car! frame (cons var (car frame)))
|
||||
(set-cdr! frame (cons val (cdr frame))))
|
||||
|
||||
(define (assignment? op) (assign-type? op))
|
||||
(define (assignment-variable exp) (car (cdr exp)))
|
||||
(define (assignment-value exp) (car (cdr (cdr exp))))
|
||||
|
||||
(define (self-evaluating? exp)
|
||||
(or (number? exp) (nil? exp) (operative-type? exp) (applicative-type? exp)
|
||||
(and (not (symbol-type? exp)) (record? exp))))
|
||||
|
||||
(define (extend-environment vars vals base-env)
|
||||
(cons (make-frame vars vals) base-env))
|
||||
|
||||
(define (find-variable var env)
|
||||
(define (env-loop env)
|
||||
(define (scan vars vals)
|
||||
(cond ((null? vars)
|
||||
(env-loop (cdr env)))
|
||||
((eq? var (car vars))
|
||||
(values vals #t))
|
||||
(else (scan (cdr vars) (cdr vals)))))
|
||||
(if (null? env)
|
||||
(values var #f)
|
||||
(let ((frame (car env)))
|
||||
(scan (frame-variables frame)
|
||||
(frame-values frame)))))
|
||||
(env-loop env))
|
||||
|
||||
(define (lookup-variable-value var env)
|
||||
(receive (vals found) (find-variable var env)
|
||||
(if found
|
||||
(car vals)
|
||||
(begin
|
||||
;; (display-error var)
|
||||
(display-error err.variable-not-found var)
|
||||
err.variable-not-found))))
|
||||
|
||||
(define (set-variable-value! var val env)
|
||||
(receive (vals found) (find-variable var env)
|
||||
(if found
|
||||
(set-car! vals val)
|
||||
(add-binding-to-frame! var val (car env)))
|
||||
(nil)))
|
||||
|
||||
(define-syntax guard
|
||||
(syntax-rules (*error*)
|
||||
((_ body ...)
|
||||
(if (= *error* 0)
|
||||
(begin body ...)
|
||||
(begin
|
||||
(display-error *error* *error-obj*)
|
||||
(tag-number 999))))))
|
||||
|
||||
(define (extend-operative-environment op x e)
|
||||
(let* ((not-list-arg (pair? (car (operative-vars op))))
|
||||
(operative-var-list
|
||||
(if not-list-arg
|
||||
(operative-vars op)
|
||||
(list (operative-vars op))))
|
||||
(vals (if not-list-arg (cdr x) (list (cdr x)))))
|
||||
(if (= (operative-has-dyn-var? op) 1)
|
||||
(extend-environment (cons (operative-dyn-var op)
|
||||
operative-var-list)
|
||||
(cons e vals)
|
||||
(operative-static-env op))
|
||||
(extend-environment operative-var-list
|
||||
vals
|
||||
(operative-static-env op)))))
|
||||
|
||||
(define (construct-operative x e)
|
||||
(if (nil? (cdr (car x)))
|
||||
(make-operative (car (car x)) (nil) 0 (cdr x) e)
|
||||
;; Include dynamic environment.
|
||||
(make-operative (car (car x)) (car (cdr (car x))) 1 (cdr x) e)))
|
||||
|
||||
(define (ev x e)
|
||||
(guard
|
||||
(cond ((self-evaluating? x) x)
|
||||
((symbol-type? x) (lookup-variable-value x e))
|
||||
(else
|
||||
(let ((op (ev (car x) e)))
|
||||
(guard
|
||||
(cond ((assignment? op)
|
||||
(set-variable-value! (car (cdr x)) (ev (car (cdr (cdr x))) e) e))
|
||||
((operative-type? op)
|
||||
(ev-sequence
|
||||
(operative-proc op)
|
||||
(extend-operative-environment op x e)))
|
||||
((applicative-type? op)
|
||||
(ev (cons (applicative-obj op) (ev-args (cdr x) e)) e))
|
||||
((vau-type? op)
|
||||
(construct-operative (cdr x) e))
|
||||
(else (apply-primitive-proc op (cdr x) e)))))))))
|
||||
|
||||
;; (define *prim-env*
|
||||
;; (extend-environment
|
||||
;; '(+ ev vau eval wrap set! nil cons env)
|
||||
;; `(,(make-add)
|
||||
;; ,(make-eval)
|
||||
;; ,(make-vau)
|
||||
;; ,(make-eval)
|
||||
;; ,(make-wrap)
|
||||
;; ,(make-assign)
|
||||
;; ,(make-nil)
|
||||
;; ,(make-cons)
|
||||
;; ,(make-env))
|
||||
;; (nil)))
|
||||
|
||||
;; (ev '((vau (elements) elements) 1 2 3)
|
||||
;; *prim-env*)
|
||||
|
||||
;; ;; (ev '(set! quote* (vau ((x)) x))
|
||||
;; ;; *prim-env*)
|
||||
;; ;; (ev '(quote* xyz)
|
||||
;; ;; *prim-env*)
|
||||
|
||||
(cond-expand (prescheme 0)
|
||||
(else
|
||||
(define (precompile-sexp x)
|
||||
(cond ((number? x) (tag-number x))
|
||||
((symbol? x)
|
||||
(make-symbol
|
||||
(map (compose tag-number char->integer)
|
||||
(string->list (symbol->string x)))))))
|
||||
(precompile-sexp 'set!)))
|
||||
|
||||
(define *env* #x220000)
|
||||
;; (define *oblist* #x220010)
|
||||
(define *exps* #x220008)
|
||||
|
||||
(define s-add 0)
|
||||
(define s-set! 0)
|
||||
(define s-x 0)
|
||||
|
||||
(define (print-debug-table start words)
|
||||
(let loop ((i 0))
|
||||
(if (< i words)
|
||||
(begin (terminal:put-number (+ start (* i 8)))
|
||||
(terminal:put-char #\:)
|
||||
(terminal:put-char #\space)
|
||||
(terminal:put-number
|
||||
(word-ref (integer->address (+ start (* i 8)))))
|
||||
(terminal:put-char #\newline)
|
||||
(loop (+ i 1))))))
|
||||
|
||||
(define (print* obj)
|
||||
(cond ((number? obj)
|
||||
(terminal:put-number (untag-number obj)))
|
||||
((symbol-type? obj)
|
||||
(let loop ((o (symbol-char-list obj)))
|
||||
(terminal:put-char (ascii->char (untag-number (car/chk o))))
|
||||
(if (not (nil? (cdr o)))
|
||||
(loop (cdr/chk o)))))
|
||||
((record? obj)
|
||||
(terminal:put-char #\#)
|
||||
(terminal:put-char #\<)
|
||||
(terminal:put-number (untag-number (untag-record obj)))
|
||||
(terminal:put-char #\>))
|
||||
((pair? obj)
|
||||
;; must not be in a function or prescheme could make it in to
|
||||
;; a dependency loop
|
||||
(terminal:put-char #\()
|
||||
(let loop ((o obj))
|
||||
(print* (car o))
|
||||
(if (not (nil? (cdr o)))
|
||||
(begin (terminal:put-char #\space)
|
||||
(if (pair? (cdr o))
|
||||
(loop (cdr o))
|
||||
(begin (terminal:put-char #\.)
|
||||
(terminal:put-char #\space)
|
||||
(print* (cdr o)))))))
|
||||
(terminal:put-char #\)))
|
||||
((nil? obj)
|
||||
(terminal:put-char #\()
|
||||
(terminal:put-char #\)))
|
||||
(else (terminal:put-char #\E))))
|
||||
|
||||
(define (run-ev)
|
||||
(set! *alloc-address* (integer->address #x300000))
|
||||
;; a single pair will be consed after this location
|
||||
(set! *end-of-memory* #x1000000) ; 32 MiB
|
||||
(set! *oom-pair* (cons (make-nil) (make-nil)))
|
||||
|
||||
|
||||
(set! *env* (word-ref (integer->address *env*)))
|
||||
(set! *exps* (word-ref (integer->address *exps*)))
|
||||
|
||||
(let loop ((exps (car *exps*)))
|
||||
(if (null? (cdr exps))
|
||||
(ev (car exps) *env*)
|
||||
(begin (ev (car exps) *env*)
|
||||
(loop (cdr exps)))))
|
||||
|
||||
)
|
||||
|
||||
(define (keyboard-handler key)
|
||||
(+ key 0))
|
||||
|
||||
(define (kmain)
|
||||
;; see note in prescheme.h
|
||||
(vm-init)
|
||||
;; (init)
|
||||
|
||||
(terminal:initialize)
|
||||
|
||||
|
||||
(run-ev)
|
||||
(let loop ()
|
||||
(halt)
|
||||
(loop))
|
||||
)
|
||||
|
||||
;; NOTE!
|
||||
;; dynamic variable binding can be accomplished by looking up a
|
||||
;; variable in the dynamic environment instead of the lexical
|
||||
;; environment that was stored when the procedure was created.
|
||||
;; if one wants a variable to be dynamic scoped for calls later on it
|
||||
;; can bind a variable in the static environment to the one in the
|
||||
;; dynamic environment.
|
||||
|
||||
;; (define bar `(,(env) (x) (baz x)))
|
||||
;; (bar (lookup-variable x (current-env))) ; x is dynamic for the
|
||||
;; ; duration of the execution of
|
||||
;; ; bar's body
|
||||
|
||||
;; NOTE! is it possible to do tail-recursion with the current
|
||||
;; evaluator if the code in the tail position is always evaluated in
|
||||
;; its own loop?
|
@ -0,0 +1,93 @@
|
||||
(import chicken scheme ports data-structures)
|
||||
(use srfi-1 extras posix)
|
||||
|
||||
(include "ev.scm")
|
||||
|
||||
|
||||
(define (symbol->num-list sym)
|
||||
(map char->integer (string->list (symbol->string sym))))
|
||||
|
||||
(define (write-val v)
|
||||
(let loop ((v v) (acc '()))
|
||||
(if (= v 0)
|
||||
(for-each write-byte
|
||||
(reverse
|
||||
;; NOTE assumes little endian
|
||||
(append (make-list (- *word-size* (length acc)) 0) acc)))
|
||||
(loop (quotient v 256) (cons (remainder v 256) acc)))))
|
||||
|
||||
|
||||
(define a (if (feature? 'debug)
|
||||
0
|
||||
#x220000))
|
||||
(if (= a 0)
|
||||
(begin (newline) (print "!!!!!! heap-writer DEBUG MODE !!!!!") (newline)))
|
||||
|
||||
(define (write-cons obj1 obj2)
|
||||
(let ((start a))
|
||||
(write-val obj1)
|
||||
(write-val obj2)
|
||||
(set! a (+ a 16))
|
||||
start))
|
||||
|
||||
(define *oblist* '())
|
||||
(define (make-heap obj)
|
||||
(define (tag-record obj) (bitwise-xor (arithmetic-shift obj 2) record-spec))
|
||||
(define symbol-tag (tag-record (untag-record (record-obj-type (make-symbol '())))))
|
||||
(define (tag-nil) (tag-record (tag-number 0)))
|
||||
(define (has-symbol? sym)
|
||||
(alist-ref sym *oblist*))
|
||||
(define (push-oblist-symbol sym loc)
|
||||
(set! *oblist* (cons (cons sym loc) *oblist*)))
|
||||
(define (oblist-symbol-record sym)
|
||||
(alist-ref sym *oblist*))
|
||||
;; (define (tag-number n) n)
|
||||
(define (loop obj)
|
||||
(cond ((number? obj)
|
||||
(tag-number obj))
|
||||
((nil? obj)
|
||||
(tag-nil))
|
||||
((record? obj)
|
||||
(tag-record (untag-record obj)))
|
||||
((record-obj? obj)
|
||||
(write-cons (tag-record (untag-record (record-obj-type obj)))
|
||||
(loop (record-obj-value obj))))
|
||||
((symbol? obj)
|
||||
(if (has-symbol? obj)
|
||||
(oblist-symbol-record obj)
|
||||
(let ((rec (write-cons symbol-tag
|
||||
(write-cons (loop (symbol->num-list obj))
|
||||
(tag-nil)))))
|
||||
(push-oblist-symbol obj rec)
|
||||
rec)))
|
||||
((pair? obj)
|
||||
(write-cons (loop (car obj)) (loop (cdr obj))))
|
||||
(else (error "unknown"))))
|
||||
(loop obj))
|
||||
|
||||
(with-output-to-file (if (feature? 'debug) "heap-debug.bin" "heap.bin")
|
||||
(lambda ()
|
||||
;; (set! a 0)
|
||||
;; (set! *oblist* '())
|
||||
;; lets call the stuff at the beginning "the header"
|
||||
;; could also include the tip of the oblist (and make the oblist
|
||||
;; an actual list of its own) Also, make the header real big for
|
||||
;; future binary compatibility so more things can be added to it
|
||||
;; without causing old images to become invalid
|
||||
;; also make the values in the headers offsets and have ev.scm
|
||||
;; calculate the actual location that way the heap can be moved to
|
||||
;; places in memory without having to recompile
|
||||
;; ALSO add to header the end of the heap so that *alloc-address*
|
||||
;; can be set to it when run-ev starts
|
||||
(write-val 0) ; written over for env start address
|
||||
(write-val 0) ; written over for exps start address
|
||||
(set! a (+ a 16))
|
||||
(make-heap (eval (car (read-file "ev-environment.scm"))))
|
||||
(let ((fp (file-position (current-output-port))))
|
||||
(set-file-position! (current-output-port) 0)
|
||||
(write-val (- a 16))
|
||||
(set-file-position! (current-output-port) fp))
|
||||
(make-heap (read-file "ev-runtime.scm"))
|
||||
(set-file-position! (current-output-port) 8)
|
||||
(write-val (- a 16))
|
||||
))
|
@ -0,0 +1,18 @@
|
||||
(define-structure vm
|
||||
(export kmain keyboard-handler
|
||||
)
|
||||
(open prescheme ps-memory ps-receive ps-macros (modify terminal (prefix terminal:)))
|
||||
(for-syntax (open scheme bitwise))
|
||||
(files ev))
|
||||
|
||||
(define-structure ps-macros
|
||||
(export cond-expand)
|
||||
(open prescheme)
|
||||
(files ps-macros))
|
||||
|
||||
(define-structure terminal
|
||||
(export initialize put-number put-char)
|
||||
(open prescheme ps-memory)
|
||||
(files terminal))
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
(define-syntax cond-expand
|
||||
(syntax-rules (prescheme)
|
||||
((_ (prescheme body ...) rest ...)
|
||||
(begin body ...))))
|
@ -0,0 +1,120 @@
|
||||
(define-syntax enum
|
||||
(lambda (x r c)
|
||||
(let ((name (symbol->string (cadr x))))
|
||||
`(,(r 'begin)
|
||||
,@(map (lambda (var/val)
|
||||
`(,(r 'define) ,(string->symbol
|
||||
(string-append name
|
||||
"."
|
||||
(symbol->string (car var/val))))
|
||||
,(cadr var/val)))
|
||||
(caddr x))))))
|
||||
|
||||
(define (word-set-at! addr index val)
|
||||
(word-set! (address+ addr index) val))
|
||||
|
||||
(define-syntax set++!
|
||||
(syntax-rules ()
|
||||
((_ v)
|
||||
(begin (set! v (+ v 1)) v))))
|
||||
|
||||
(define-syntax set--!
|
||||
(syntax-rules ()
|
||||
((_ v)
|
||||
(begin (set! v (- v 1)) v))))
|
||||
|
||||
|
||||
(define (ipow base exp)
|
||||
(let loop ((r 1) (exp exp) (base base))
|
||||
(if (> exp 0)
|
||||
(let ((nr
|
||||
(if (> (bitwise-and exp 1) 0)
|
||||
(* r base)
|
||||
r)))
|
||||
(loop nr (arithmetic-shift-right exp 1) (* base base)))
|
||||
r)))
|
||||
|
||||
(enum vga-color
|
||||
((black 0)
|
||||
(blue 1)
|
||||
(green 2)
|
||||
(cyan 2)
|
||||
(red 4)
|
||||
(magenta 5)
|
||||
(brown 6)
|
||||
(light-grey 7)
|
||||
(dark-grey 8)
|
||||
(light-blue 9)
|
||||
(light-green 10)
|
||||
(light-cyan 11)
|
||||
(light-red 12)
|
||||
(light-magenta 13)
|
||||
(light-brown 14)
|
||||
(white 15)))
|
||||
|
||||
(define (make-color fg bg)
|
||||
(bitwise-ior fg (shift-left bg 4)))
|
||||
|
||||
(define (make-vga-entry c color)
|
||||
(bitwise-ior c (shift-left color 8)))
|
||||
|
||||
(define *terminal-index*)
|
||||
(define *terminal-color*)
|
||||
(define *terminal-buffer*)
|
||||
|
||||
(define (initialize)
|
||||
(set! *terminal-index* 0)
|
||||
(set! *terminal-color* (make-color vga-color.green vga-color.black))
|
||||
(set! *terminal-buffer* (integer->address #xB8000))
|
||||
(let loop ((y 0))
|
||||
(if (< y 4000)
|
||||
(begin
|
||||
(word-set! (address+ *terminal-buffer* y)
|
||||
(make-vga-entry (char->ascii #\space)
|
||||
*terminal-color*))
|
||||
(loop (+ y 2)))))
|
||||
)
|
||||
|
||||
(define (set-color color)
|
||||
(set! *terminal-color* color))
|
||||
|
||||
(define (put-entry-at c color index)
|
||||
(word-set-at! *terminal-buffer* index
|
||||
(make-vga-entry (char->ascii c) color)))
|
||||
|
||||
(define (put-char c)
|
||||
(if (char=? c #\newline)
|
||||
(begin
|
||||
(set! *terminal-index*
|
||||
(+ *terminal-index* (- 160 (remainder *terminal-index* 160)))))
|
||||
(begin
|
||||
(put-entry-at c *terminal-color* *terminal-index*)
|
||||
(set! *terminal-index* (+ *terminal-index* 2)))))
|
||||
|
||||
(define *ascii-numeric-start* 48)
|
||||
(define (put-number num)
|
||||
;; Can handle up to a uint32
|
||||
(define (num-digits num n)
|
||||
(cond ((>= num 100000000)
|
||||
(num-digits (quotient num 100000000) (+ n 8)))
|
||||
((>= num 10000)
|
||||
(num-digits (quotient num 10000) (+ n 4)))
|
||||
((>= num 100)
|
||||
(num-digits (quotient num 100) (+ n 2)))
|
||||
((>= num 10)
|
||||
(+ n 1))
|
||||
(else n)))
|
||||
|
||||
(if (< num 0)
|
||||
(put-char #\-))
|
||||
(let ((ndigits (num-digits (abs num) 1)))
|
||||
(let loop ((digits-remaining (- ndigits 1))
|
||||
(mult (ipow 10 (- ndigits 1)))
|
||||
(num (abs num)))
|
||||
(put-char
|
||||
(ascii->char (+ *ascii-numeric-start* (quotient num mult))))
|
||||
(if (> digits-remaining 0)
|
||||
(let ((remains (- digits-remaining 1)))
|
||||
(loop remains (ipow 10 remains) (remainder num mult)))))))
|
||||
|
||||
|
Loading…
Reference in New Issue