ulisp/lisp/r5asm.lsp

396 lines
10 KiB
Common Lisp

; RISC-V Assembler - Version 4 - 18th October 2024
; see http://www.ulisp.com/show?310Z
;
; Extract register number
(defun regno (sym)
(case sym (zero 0) (ra 1) (sp 2) (gp 3) (tp 4) ((s0 fp) 8) (s1 9)
(t (let* ((s (string sym))
(c (char s 0))
(n (read-from-string (subseq s 1))))
(case c (#\x n) (#\a (+ n 10)) (#\s (+ n 16)) (#\t (if (<= n 2) (+ n 5) (+ n 25))))))))
; Short 3-bit register s0, s1, a0 to a5
(defun cregp (rd) (<= 8 (regno rd) 15))
(defun cregno (sym) (logand (regno sym) #x7))
; Pack arguments into bit fields
(defun emit (bits &rest args)
(let ((word 0))
(mapc #'(lambda (width value)
(unless (zerop (ash value (- width))) (error* "Won't fit"))
(setq word (logior (ash word width) value)))
bits args)
word))
; 32-bit emit
(defun emit32 (bits &rest args)
(let ((word (apply #'emit bits args)))
(list (logand word #xffff) (logand (ash word -16) #xffff))))
; Errors
(defun error* (txt) (format t "(pc=#x~x) ~a~%" *pc* txt))
; Test range of immediate signed values
(defun immp (x b)
(<= (- (ash 1 (1- b))) x (1- (ash 1 (1- b)))))
; Extract bitfield
(defun bits (x a &optional b)
(if b (logand (ash x (- b)) (1- (ash 1 (- a b -1))))
(logand (ash x (- a)) 1)))
(defun offset (label) (- label *pc*))
; Instruction formats
(defun reg (funct7 rs2 rs1 funct3 rd op)
(emit32 '(7 5 5 3 5 7) funct7 (regno rs2) (regno rs1) funct3 (regno rd) op))
(defun creg (op3 op1 op2 rd op2b rs2)
(cond
((and (cregp rd) (cregp rs2))
(emit '(3 1 2 3 2 3 2) op3 op1 op2 (cregno rd) op2b (cregno rs2) 1))
(t (error* "C won't fit"))))
(defun immed (imm12 rs1 funct3 rd op)
(cond
((immp imm12 12)
(emit32 '(12 5 3 5 7) (logand imm12 #xfff) (regno rs1) funct3 (regno rd) op))
(t
(error* "Immediate value out of range"))))
(defun cimmed (imm12 rs1 funct3 rd op)
(emit32 '(12 5 3 5 7) imm12 (regno rs1) funct3 (regno rd) op))
(defun branch (imm12 rs2 rs1 funct3 funct7)
(let ((off (offset imm12)))
(emit32 '(1 6 5 5 3 4 1 7)
(bits off 12) (bits off 10 5) (regno rs2)
(regno rs1) funct3 (bits off 4 1) (bits off 11) funct7)))
(defun jump (imm20 imm10-1 imm11 imm19-12 rd op)
(emit32 '(1 10 1 8 5 7) imm20 imm10-1 imm11 imm19-12 rd op))
(defun muldiv (rs2 rs1 funct3 rd funct7)
(emit32 '(7 5 5 3 5 7) 1 (regno rs2) (regno rs1) funct3 (regno rd) funct7))
(defun store (imm src base op)
(emit32 '(7 5 5 3 5 7) (bits imm 11 5) (regno src) (regno base) op (bits imm 4 0) #x23))
(defun cimm6 (rd imm op1 op2)
(emit '(3 1 5 5 2) op1 (bits imm 5) (regno rd) (bits imm 4 0) op2))
(defun cimm6* (rd imm op1 op2 op3)
(emit '(3 1 2 3 5 2) op1 (bits imm 5) op2 (cregno rd) (bits imm 4 0) op3))
;
; Alphabetical list of mnemonics
;
(defun $add (rd rs1 rs2)
(cond
((eq rd rs1)
(emit '(3 1 5 5 2) 4 1 (regno rd) (regno rs2) 2))
(t (reg 0 rs2 rs1 0 rd #x33))))
(defun $addi (rd rs1 imm)
(cond
((and (eq rd rs1) (immp imm 6))
(cimm6 rd imm 0 1))
((and (= (regno rd) 2) (= (regno rs1) 2) (immp imm 10))
(emit '(3 1 5 1 1 2 1 2) 3 (bits imm 9) 2 (bits imm 4) (bits imm 6) (bits imm 8 7) (bits imm 5) 1))
(t (immed imm rs1 0 rd #x13))))
(defun $and (rd rs1 rs2)
(cond
((and (eq rd rs1) (cregp rd) (cregp rs2))
(creg 4 0 3 rd 3 rs2))
(t (reg 0 rs2 rs1 7 rd #x33))))
(defun $andi (rd rs1 imm)
(cond
((and (eq rd rs1) (cregp rd) (immp imm 5))
(cimm6* rd imm 4 2 1))
(t (immed imm rs1 7 rd #x13))))
(defun $auipc (rd imm)
(cond
((zerop (logand imm #xfff))
(emit32 '(20 5 7) (bits imm 31 12) (regno rd) #x17))
(t (error* "auipc no good"))))
(defun $beq (rs1 rs2 imm12)
(branch imm12 rs2 rs1 0 #x63))
(defun $beqz (rs imm)
(let ((off (offset imm)))
(cond
((and (immp off 8) (cregp rs))
(emit '(3 1 2 3 2 2 1 2) 6 (bits off 8) (bits off 4 3)
(cregno rs) (bits off 7 6) (bits off 2 1) (bits off 5) 1))
(t ($beq rs 'x0 imm)))))
(defun $bge (rs1 rs2 imm12)
(branch imm12 rs2 rs1 5 #x63))
(defun $bgeu (rs1 rs2 imm12)
(branch imm12 rs2 rs1 7 #x63))
(defun $bgez (rs1 imm12)
($bge rs1 'x0 imm12))
(defun $bgt (rs1 rs2 imm12)
($blt rs2 rs1 imm12))
(defun $bgtu (rs1 rs2 imm12)
($bltu rs2 rs1 imm12))
(defun $bgtz (rs1 imm12)
($blt 'x0 rs1 imm12))
(defun $ble (rs1 rs2 imm12)
($bge rs2 rs1 imm12))
(defun $bleu (rs1 rs2 imm12)
($bgeu rs2 rs1 imm12))
(defun $blez (rs2 imm12)
($bge 'x0 rs2 imm12))
(defun $blt (rs1 rs2 imm12)
(branch imm12 rs2 rs1 4 #x63))
(defun $bltu (rs1 rs2 imm12)
(branch imm12 rs2 rs1 6 #x63))
(defun $bltz (rs1 imm12)
($blt rs1 'x0 imm12))
(defun $bne (rs1 rs2 imm12)
(branch imm12 rs2 rs1 1 #x63))
(defun $bnez (rs imm)
(let ((off (offset imm)))
(cond
((and (immp off 8) (cregp rs))
(emit '(3 1 2 3 2 2 1 2) 7 (bits off 8) (bits off 4 3)
(cregno rs) (bits off 7 6) (bits off 2 1) (bits off 5) 1))
(t ($bne rs 'x0 imm)))))
(defun $div (rd rs1 rs2)
(muldiv rs2 rs1 4 rd #x33))
(defun $divu (rd rs1 rs2)
(muldiv rs2 rs1 5 rd #x33))
(defun $divw (rd rs1 rs2)
(muldiv rs2 rs1 4 rd #x3b))
(defun $divuw (rd rs1 rs2)
(muldiv rs2 rs1 5 rd #x3b))
(defun $fence () (emit32 '(16 16) #x0ff0 #x000f))
(defun $j (label)
(let ((off (offset label)))
(emit '(3 1 1 2 1 1 1 3 1 2) 5 (bits off 11) (bits off 4) (bits off 9 8)
(bits off 10) (bits off 6) (bits off 7) (bits off 3 1) (bits off 5) 1)))
; C.JAL is RV32 only
(defun $jal (rd &optional label)
(when (null label) (setq label rd rd 'ra))
(let ((off (offset label)))
(emit32 '(1 10 1 8 5 7) (bits off 20) (bits off 10 1) (bits off 11) (bits off 19 12) (regno rd) #x6f)))
(defun $jalr (label lst)
(let ((off (+ (offset label) 4)))
(emit32 '(12 5 3 5 7) (bits off 11 0) (regno (car lst)) 0 (regno (car lst)) #x67)))
(defun $jr (rs1)
(emit '(3 1 5 5 2) 4 0 (regno rs1) 0 2))
; In next four, imm can be omitted and defaults to 0
(defun $lb (rd imm &optional lst)
(unless lst (setq lst imm imm 0))
(immed imm (car lst) 0 rd 3))
(defun $lbu (rd imm &optional lst)
(unless lst (setq lst imm imm 0))
(immed imm (car lst) 4 rd 3))
(defun $lh (rd imm &optional lst)
(unless lst (setq lst imm imm 0))
(immed imm (car lst) 1 rd 3))
(defun $lhu (rd imm &optional lst)
(unless lst (setq lst imm imm 0))
(immed imm (car lst) 5 rd 3))
; li pseudoinstruction - will load 32-bit immediates
(defun $li (rd imm)
(cond
((immp imm 6) ; 16 bit
(cimm6 rd imm 2 1))
((immp imm 12) ; 32 bit
($addi rd 'x0 imm))
(t (let ((imm12 (logand imm #x00000fff)) ; 64 bit
(imm20 (logand (ash imm -12) #xfffff)))
(append
($lui rd (if (= (logand imm12 #x800) #x800) (+ imm20 #x1000) imm20))
; $addi
(emit32 '(12 5 3 5 7) imm12 (regno rd) 0 (regno rd) #x13))))))
(defun $lui (rd imm)
(cond
((and (immp imm 6) (/= imm 0) (/= (regno rd) 0) (/= (regno rd) 2)) ; 16 bit
(cimm6 rd imm 3 1))
(t
(emit32 '(20 5 7) imm (regno rd) #x37))
(t (error* "lui no good"))))
(defun $lw (rd imm lst)
(cond
((listp lst)
(let ((base (car lst)))
(cond
; rs1 = sp
((and (= (regno base) 2))
(emit '(3 1 5 3 2 2) 2 (bits imm 5) (regno rd) (bits imm 4 2) (bits imm 7 6) 2))
; rs1 = general
((and (cregp rd) (cregp base))
(emit '(3 3 3 1 1 3 2) 2 (bits imm 5 3) (cregno base) (bits imm 2) (bits imm 6) (cregno rd) 0))
(t (immed imm base 2 rd 3)))))
(t (error* "Illegal 3rd arg"))))
(defun $mul (rd rs1 rs2)
(muldiv rs2 rs1 0 rd #x33))
(defun $mulh (rd rs1 rs2)
(muldiv rs2 rs1 1 rd #x33))
(defun $mulhsu (rd rs1 rs2)
(muldiv rs2 rs1 2 rd #x33))
(defun $mulhu (rd rs1 rs2)
(muldiv rs2 rs1 3 rd #x33))
(defun $mv (rd rs1)
(emit '(3 1 5 5 2) 4 0 (regno rd) (regno rs1) 2))
(defun $neg (rd rs2)
($sub rd 'x0 rs2))
(defun $nop ()
($addi 'x0 'x0 0))
(defun $not (rd rs1)
($xori rd rs1 -1))
(defun $or (rd rs1 rs2)
(cond
((and (eq rd rs1) (cregp rd) (cregp rs2))
(creg 4 0 3 rd 2 rs2))
(t (reg 0 rs2 rs1 6 rd #x33))))
(defun $ori (rd rs1 imm)
(immed imm rs1 6 rd #x13))
(defun $rem (rd rs1 rs2)
(muldiv rs2 rs1 6 rd #x33))
(defun $remu (rd rs1 rs2)
(muldiv rs2 rs1 7 rd #x33))
(defun $ret ()
($jr 'ra))
; In $sb, $sh, and $sw, imm can be omitted and defaults to 0
(defun $sb (src imm &optional lst)
(unless lst (setq lst imm imm 0))
(store imm src (car lst) 0))
(defun $seqz (rd rs1)
($sltiu rd rs1 1))
(defun $sgtz (rd rs2)
($slt rd 'x0 rs2))
(defun $sh (src imm &optional lst)
(unless lst (setq lst imm imm 0))
(store imm src (car lst) 1))
(defun $sll (rd rs1 rs2)
(reg 0 rs2 rs1 1 rd #x33))
(defun $slli (rd rs1 imm)
(cond
((and (eq rd rs1))
(cimm6 rd imm 0 2))
(t (emit32 '(6 6 5 3 5 7) 0 imm (regno rs1) 1 (regno rd) #x13))))
(defun $slt (rd rs1 rs2)
(reg 0 rs2 rs1 2 rd #x33))
(defun $slti (rd rs1 imm)
(immed imm rs1 2 rd #x13))
(defun $sltiu (rd rs1 imm)
(immed imm rs1 3 rd #x13))
(defun $sltu (rd rs1 rs2)
(reg 0 rs2 rs1 3 rd #x33))
(defun $sltz (rd rs1)
($slt rd rs1 'x0))
(defun $snez (rd rs2)
($sltu rd 'x0 rs2))
(defun $sra (rd rs1 rs2)
(reg #x20 rs2 rs1 2 rd #x33))
(defun $srai (rd rs1 imm)
(cond
((and (eq rd rs1) (cregp rd))
(cimm6* rd imm 4 1 1))
(t (emit32 '(6 6 5 3 5 7) #x10 imm (regno rs1) 5 (regno rd) #x13))))
(defun $srl (rd rs1 rs2)
(reg 0 rs2 rs1 5 rd #x33))
(defun $srli (rd rs1 imm)
(cond
((and (eq rd rs1) (cregp rd))
(cimm6* rd imm 4 0 1))
(t (emit32 '(6 6 5 3 5 7) 0 imm (regno rs1) 5 (regno rd) #x13))))
(defun $sub (rd rs1 rs2)
(cond
((and (eq rd rs1) (cregp rd) (cregp rs2))
(creg 4 0 3 rd 0 rs2))
(t (reg #x20 rs2 rs1 0 rd #x33))))
(defun $sw (src imm &optional lst)
(unless lst (setq lst imm imm 0))
(let ((base (car lst)))
(cond
; base = sp
((and (= (regno base) 2))
(emit '(3 4 2 5 2) 6 (bits imm 5 2) (bits imm 7 6) (regno src) 2))
; base = general
((and (cregp src) (cregp base))
(emit '(3 3 3 1 1 3 2) 6 (bits imm 5 3) (cregno base) (bits imm 2) (bits imm 6) (cregno src) 0))
(t (store imm src base 2)))))
(defun $xor (rd rs1 rs2)
(cond
((and (eq rd rs1) (cregp rd) (cregp rs2))
(creg 4 0 3 rd 1 rs2))
(t (reg 0 rs2 rs1 4 rd #x33))))
(defun $xori (rd rs1 imm)
(immed imm rs1 4 rd #x13))