diff --git a/src/arch/arm64/Syscall.go b/src/arch/arm64/Syscall.go new file mode 100644 index 0000000..37e0b9e --- /dev/null +++ b/src/arch/arm64/Syscall.go @@ -0,0 +1,17 @@ +package register + +import "git.akyoto.dev/cli/q/src/register" + +const ( + SyscallNumber = register.R8 + SyscallReturn = register.R0 +) + +var SyscallArgs = []register.ID{ + register.R0, + register.R1, + register.R2, + register.R3, + register.R4, + register.R5, +} diff --git a/src/x64/Call.go b/src/arch/x64/Call.go similarity index 100% rename from src/x64/Call.go rename to src/arch/x64/Call.go diff --git a/src/x64/Move.go b/src/arch/x64/Move.go similarity index 100% rename from src/x64/Move.go rename to src/arch/x64/Move.go diff --git a/src/x64/Return.go b/src/arch/x64/Return.go similarity index 100% rename from src/x64/Return.go rename to src/arch/x64/Return.go diff --git a/src/arch/x64/Syscall.go b/src/arch/x64/Syscall.go new file mode 100644 index 0000000..132e560 --- /dev/null +++ b/src/arch/x64/Syscall.go @@ -0,0 +1,22 @@ +package x64 + +import "git.akyoto.dev/cli/q/src/register" + +const ( + SyscallNumber = register.R0 // rax + SyscallReturn = register.R0 // rax +) + +var SyscallArgs = []register.ID{ + register.R7, // rdi + register.R6, // rsi + register.R2, // rdx + register.R10, + register.R8, + register.R9, +} + +// Syscall is the primary way to communicate with the OS kernel. +func Syscall(code []byte) []byte { + return append(code, 0x0f, 0x05) +} diff --git a/src/arch/x64/x64_test.go b/src/arch/x64/x64_test.go new file mode 100644 index 0000000..8fb7c21 --- /dev/null +++ b/src/arch/x64/x64_test.go @@ -0,0 +1,16 @@ +package x64_test + +import ( + "testing" + + "git.akyoto.dev/cli/q/src/arch/x64" + "git.akyoto.dev/go/assert" +) + +func TestX64(t *testing.T) { + assert.DeepEqual(t, x64.Call([]byte{}, 1), []byte{0xe8, 0x01, 0x00, 0x00, 0x00}) + assert.DeepEqual(t, x64.MoveRegNum32([]byte{}, 0, 1), []byte{0xb8, 0x01, 0x00, 0x00, 0x00}) + assert.DeepEqual(t, x64.MoveRegNum32([]byte{}, 1, 1), []byte{0xb9, 0x01, 0x00, 0x00, 0x00}) + assert.DeepEqual(t, x64.Return([]byte{}), []byte{0xc3}) + assert.DeepEqual(t, x64.Syscall([]byte{}), []byte{0x0f, 0x05}) +} diff --git a/src/asm/Assembler.go b/src/asm/Assembler.go index 470d310..5de2ff9 100644 --- a/src/asm/Assembler.go +++ b/src/asm/Assembler.go @@ -3,10 +3,10 @@ package asm import ( "encoding/binary" + "git.akyoto.dev/cli/q/src/arch/x64" "git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/log" "git.akyoto.dev/cli/q/src/register" - "git.akyoto.dev/cli/q/src/x64" ) // Assembler contains a list of instructions. diff --git a/src/asm/Assembler_test.go b/src/asm/Assembler_test.go index 581da9b..ed47f32 100644 --- a/src/asm/Assembler_test.go +++ b/src/asm/Assembler_test.go @@ -3,9 +3,9 @@ package asm_test import ( "testing" + "git.akyoto.dev/cli/q/src/arch/x64" "git.akyoto.dev/cli/q/src/asm" - "git.akyoto.dev/cli/q/src/linux" - "git.akyoto.dev/cli/q/src/register" + "git.akyoto.dev/cli/q/src/os/linux" "git.akyoto.dev/go/assert" ) @@ -13,14 +13,14 @@ func TestHello(t *testing.T) { a := asm.New() hello := []byte("Hello\n") - a.MoveRegisterNumber(register.Syscall0, linux.Write) - a.MoveRegisterNumber(register.Syscall1, 1) - a.MoveRegisterData(register.Syscall2, hello) - a.MoveRegisterNumber(register.Syscall3, uint64(len(hello))) + a.MoveRegisterNumber(x64.SyscallNumber, linux.Write) + a.MoveRegisterNumber(x64.SyscallArgs[0], 1) + a.MoveRegisterData(x64.SyscallArgs[1], hello) + a.MoveRegisterNumber(x64.SyscallArgs[2], uint64(len(hello))) a.Syscall() - a.MoveRegisterNumber(register.Syscall0, linux.Exit) - a.MoveRegisterNumber(register.Syscall1, 0) + a.MoveRegisterNumber(x64.SyscallNumber, linux.Exit) + a.MoveRegisterNumber(x64.SyscallArgs[0], 0) a.Syscall() code, data := a.Finalize() diff --git a/src/compiler/Finalize.go b/src/compiler/Finalize.go index ccae1b3..56095f4 100644 --- a/src/compiler/Finalize.go +++ b/src/compiler/Finalize.go @@ -1,9 +1,9 @@ package compiler import ( + "git.akyoto.dev/cli/q/src/arch/x64" "git.akyoto.dev/cli/q/src/asm" - "git.akyoto.dev/cli/q/src/linux" - "git.akyoto.dev/cli/q/src/register" + "git.akyoto.dev/cli/q/src/os/linux" ) // Finalize generates the final machine code. @@ -14,8 +14,8 @@ func Finalize(functions map[string]*Function) ([]byte, []byte) { a.Merge(&f.Assembler) } - a.MoveRegisterNumber(register.Syscall0, linux.Exit) - a.MoveRegisterNumber(register.Syscall1, 0) + a.MoveRegisterNumber(x64.SyscallNumber, linux.Exit) + a.MoveRegisterNumber(x64.SyscallArgs[0], 0) a.Syscall() code, data := a.Finalize() diff --git a/src/compiler/Function.go b/src/compiler/Function.go index 5c4742f..1b168d1 100644 --- a/src/compiler/Function.go +++ b/src/compiler/Function.go @@ -1,9 +1,9 @@ package compiler import ( + "git.akyoto.dev/cli/q/src/arch/x64" "git.akyoto.dev/cli/q/src/asm" - "git.akyoto.dev/cli/q/src/linux" - "git.akyoto.dev/cli/q/src/register" + "git.akyoto.dev/cli/q/src/os/linux" "git.akyoto.dev/cli/q/src/token" ) @@ -20,10 +20,10 @@ func (f *Function) Compile() { for i, t := range f.Body { if t.Kind == token.Identifier && t.String() == "print" { message := f.Body[i+2].Bytes - f.Assembler.MoveRegisterNumber(register.Syscall0, linux.Write) - f.Assembler.MoveRegisterNumber(register.Syscall1, 1) - f.Assembler.MoveRegisterData(register.Syscall2, message) - f.Assembler.MoveRegisterNumber(register.Syscall3, uint64(len(message))) + f.Assembler.MoveRegisterNumber(x64.SyscallNumber, linux.Write) + f.Assembler.MoveRegisterNumber(x64.SyscallArgs[0], 1) + f.Assembler.MoveRegisterData(x64.SyscallArgs[1], message) + f.Assembler.MoveRegisterNumber(x64.SyscallArgs[2], uint64(len(message))) f.Assembler.Syscall() } } diff --git a/src/keywords/All.go b/src/keywords/All.go deleted file mode 100644 index 5df0a39..0000000 --- a/src/keywords/All.go +++ /dev/null @@ -1,6 +0,0 @@ -package keywords - -// All defines the keywords used in the language. -var All = map[string]bool{ - "return": true, -} diff --git a/src/linux/syscall_linux.go b/src/linux/syscall_linux.go deleted file mode 100644 index c535786..0000000 --- a/src/linux/syscall_linux.go +++ /dev/null @@ -1,333 +0,0 @@ -package linux - -const ( - Read = iota - Write - Open - Close - Stat - Fstat - Lstat - Poll - Lseek - Mmap - Mprotect - Munmap - Brk - Rt_sigaction - Rt_sigprocmask - Rt_sigreturn - Ioctl - Pread64 - Pwrite64 - Readv - Writev - Access - Pipe - Select - Sched_yield - Mremap - Msync - Mincore - Madvise - Shmget - Shmat - Shmctl - Dup - Dup2 - Pause - Nanosleep - Getitimer - Alarm - Setitimer - Getpid - Sendfile - Socket - Connect - Accept - Sendto - Recvfrom - Sendmsg - Recvmsg - Shutdown - Bind - Listen - Getsockname - Getpeername - Socketpair - Setsockopt - Getsockopt - Clone - Fork - Vfork - Execve - Exit - Wait4 - Kill - Uname - Semget - Semop - Semctl - Shmdt - Msgget - Msgsnd - Msgrcv - Msgctl - Fcntl - Flock - Fsync - Fdatasync - Truncate - Ftruncate - Getdents - Getcwd - Chdir - Fchdir - Rename - Mkdir - Rmdir - Creat - Link - Unlink - Symlink - Readlink - Chmod - Fchmod - Chown - Fchown - Lchown - Umask - Gettimeofday - Getrlimit - Getrusage - Sysinfo - Times - Ptrace - Getuid - Syslog - Getgid - Setuid - Setgid - Geteuid - Getegid - Setpgid - Getppid - Getpgrp - Setsid - Setreuid - Setregid - Getgroups - Setgroups - Setresuid - Getresuid - Setresgid - Getresgid - Getpgid - Setfsuid - Setfsgid - Getsid - Capget - Capset - Rt_sigpending - Rt_sigtimedwait - Rt_sigqueueinfo - Rt_sigsuspend - Sigaltstack - Utime - Mknod - Uselib - Personality - Ustat - Statfs - Fstatfs - Sysfs - Getpriority - Setpriority - Sched_setparam - Sched_getparam - Sched_setscheduler - Sched_getscheduler - Sched_get_priority_max - Sched_get_priority_min - Sched_rr_get_interval - Mlock - Munlock - Mlockall - Munlockall - Vhangup - Modify_ldt - Pivot_root - Sysctl - Prctl - Arch_prctl - Adjtimex - Setrlimit - Chroot - Sync - Acct - Settimeofday - Mount - Umount2 - Swapon - Swapoff - Reboot - Sethostname - Setdomainname - Iopl - Ioperm - Create_module - Init_module - Delete_module - Get_kernel_syms - Query_module - Quotactl - Nfsservctl - Getpmsg - Putpmsg - Afs_syscall - Tuxcall - Security - Gettid - Readahead - Setxattr - Lsetxattr - Fsetxattr - Getxattr - Lgetxattr - Fgetxattr - Listxattr - Llistxattr - Flistxattr - Removexattr - Lremovexattr - Fremovexattr - Tkill - Time - Futex - Sched_setaffinity - Sched_getaffinity - Set_thread_area - Io_setup - Io_destroy - Io_getevents - Io_submit - Io_cancel - Get_thread_area - Lookup_dcookie - Epoll_create - Epoll_ctl_old - Epoll_wait_old - Remap_file_pages - Getdents64 - Set_tid_address - Restart_syscall - Semtimedop - Fadvise64 - Timer_create - Timer_settime - Timer_gettime - Timer_getoverrun - Timer_delete - Clock_settime - Clock_gettime - Clock_getres - Clock_nanosleep - Exit_group - Epoll_wait - Epoll_ctl - Tgkill - Utimes - Vserver - Mbind - Set_mempolicy - Get_mempolicy - Mq_open - Mq_unlink - Mq_timedsend - Mq_timedreceive - Mq_notify - Mq_getsetattr - Kexec_load - Waitid - Add_key - Request_key - Keyctl - Ioprio_set - Ioprio_get - Inotify_init - Inotify_add_watch - Inotify_rm_watch - Migrate_pages - Openat - Mkdirat - Mknodat - Fchownat - Futimesat - Newfstatat - Unlinkat - Renameat - Linkat - Symlinkat - Readlinkat - Fchmodat - Faccessat - Pselect6 - Ppoll - Unshare - Set_robust_list - Get_robust_list - Splice - Tee - Sync_file_range - Vmsplice - Move_pages - Utimensat - Epoll_pwait - Signalfd - Timerfd_create - Eventfd - Fallocate - Timerfd_settime - Timerfd_gettime - Accept4 - Signalfd4 - Eventfd2 - Epoll_create1 - Dup3 - Pipe2 - Inotify_init1 - Preadv - Pwritev - Rt_tgsigqueueinfo - Perf_event_open - Recvmmsg - Fanotify_init - Fanotify_mark - Prlimit64 - Name_to_handle_at - Open_by_handle_at - Clock_adjtime - Syncfs - Sendmmsg - Setns - Getcpu - Process_vm_readv - Process_vm_writev - Kcmp - Finit_module - Sched_setattr - Sched_getattr - Renameat2 - Seccomp - Getrandom - Memfd_create - Kexec_file_load - Bpf - Stub_execveat - Userfaultfd - Membarrier - Mlock2 - Copy_file_range - Preadv2 - Pwritev2 -) diff --git a/src/os/linux/Syscall.go b/src/os/linux/Syscall.go new file mode 100644 index 0000000..7c94f6a --- /dev/null +++ b/src/os/linux/Syscall.go @@ -0,0 +1,10 @@ +package linux + +// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/entry/syscalls/syscall_64.tbl +const ( + Read = 0 + Write = 1 + Open = 2 + Close = 3 + Exit = 60 +) diff --git a/src/register/ID.go b/src/register/ID.go index 08ac82b..bc2fe1c 100644 --- a/src/register/ID.go +++ b/src/register/ID.go @@ -22,6 +22,21 @@ const ( R13 R14 R15 + R16 + R17 + R18 + R19 + R20 + R21 + R22 + R23 + R24 + R25 + R26 + R27 + R28 + R29 + R30 ) func (r ID) String() string { diff --git a/src/register/Syscall_amd64.go b/src/register/Syscall_amd64.go deleted file mode 100644 index 62cc6e5..0000000 --- a/src/register/Syscall_amd64.go +++ /dev/null @@ -1,11 +0,0 @@ -package register - -const ( - Syscall0 = R0 - Syscall1 = R7 - Syscall2 = R6 - Syscall3 = R2 - Syscall4 = R10 - Syscall5 = R8 - Syscall6 = R9 -) diff --git a/src/token/Keywords.go b/src/token/Keywords.go new file mode 100644 index 0000000..adb8de7 --- /dev/null +++ b/src/token/Keywords.go @@ -0,0 +1,6 @@ +package token + +// Keywords defines the keywords used in the language. +var Keywords = map[string]bool{ + "return": true, +} diff --git a/src/token/Tokenize.go b/src/token/Tokenize.go index 5e336d8..9229d6c 100644 --- a/src/token/Tokenize.go +++ b/src/token/Tokenize.go @@ -1,7 +1,5 @@ package token -import "git.akyoto.dev/cli/q/src/keywords" - // Pre-allocate these byte buffers so we can re-use them // instead of allocating a new buffer every time. var ( @@ -87,7 +85,7 @@ func Tokenize(buffer []byte) List { buffer[position:i], } - if keywords.All[string(token.Bytes)] { + if Keywords[string(token.Bytes)] { token.Kind = Keyword } diff --git a/src/x64/Syscall.go b/src/x64/Syscall.go deleted file mode 100644 index 94b07d2..0000000 --- a/src/x64/Syscall.go +++ /dev/null @@ -1,6 +0,0 @@ -package x64 - -// Syscall is the primary way to communicate with the OS kernel. -func Syscall(code []byte) []byte { - return append(code, 0x0f, 0x05) -}