; ARM/Thumb instructions.  -*- Scheme -*-
; Copyright (C) 2000 Red Hat, Inc.
; This file is part of CGEN.
; See file COPYING.CGEN for details.
;
; This file is included by arm.cpu.

; Hardware elements.

(define-hardware 
  (name h-gr-t)
  (comment "Thumb's general purpose registers")
  (attrs (ISA thumb) VIRTUAL) ; ??? CACHE-ADDR should be doable
  (type register WI (8))
  (indices keyword ""
	   ((r0 0) (r1 1) (r2 2) (r3 3) (r4 4) (r5 5) (r6 6) (r7 7)))
  (get (regno) (reg h-gr regno))
  (set (regno newval) (set (reg h-gr regno) newval))
)

(define-hardware
  (name h-lr-t)
  (comment "Thumb's access to the LR register")
  (attrs (ISA thumb) VIRTUAL)
  (type register WI)
  (get () (reg h-gr 14))
  (set (newval) (set (reg h-gr 14) newval))
)

(define-hardware
  (name h-sp-t)
  (comment "Thumb's access to the SP register")
  (attrs (ISA thumb) VIRTUAL)
  (type register WI)
  (get () (reg h-gr 13))
  (set (newval) (set (reg h-gr 13) newval))
)

; Instruction fields.

; define-normal-thumb-field
(define-pmacro (dntf name comment attrs start length)
  (dnf name comment (.splice (.unsplice attrs) (ISA thumb)) start length)
)

; Main opcode fields.
(dntf f-op3 "First 3 bits of opcode"   () 15 3)
(dntf f-op4 "First 4 bits of opcode"   () 15 4)
(dntf f-op5 "First 5 bits of opcode"   () 15 5)
(dntf f-op6 "First 6 bits of opcode"   () 15 6)
(dntf f-op8 "First 8 bits of opcode"   () 15 8)

; Other opcode like fields with special names.
(dntf f-h1 "h1 field in hireg insns" () 7 1)
(dntf f-h2 "h2 field in hireg insns" () 6 1)
(dntf f-l  "load/store indicator" () 11 1)
(dntf f-b  "byte/word indicator" () 10 1)
(dntf f-h  "byte/halfword indicator" () 11 1)

; Misc. remaining opcode fields (constant values but unnamed).
(dntf f-bit9 "bit 9" () 9 1)

; Data fields.
(dntf f-offset5  "5 bit unsigned immediate" () 10 5)
(dntf f-rs       "Rs (source reg)" () 5 3)
(dntf f-rd       "Rd (dest reg)" () 2 3)
(dntf f-rn       "Rn (2nd source reg in add/sub insns" () 8 3)
(dntf f-offset3  "3 bit unsigned immediate in add/sub insns" () 8 3)
(dntf f-bit10-rd "Rd (dest reg) at bit 10" () 10 3)
(dntf f-offset8  "8 bit unsigned immediate" () 7 8)
(dntf f-ro       "Ro (offset register)" () 8 3)
(dntf f-rb       "Rb (base register)" () 5 3)

; Instruction operands.

; define-normal-thumb-operand
(define-pmacro (dntop name comment attrs hw indx)
  (dnop name comment (.splice (.unsplice attrs) (ISA thumb)) hw indx)
)

(dntop sp "stack pointer" () h-sp-t f-nil)
(dntop lr "link register" () h-lr-t f-nil)

(dntop rd      "destination register"      () h-gr-t f-rd)
(dntop rs      "source register"           () h-gr-t f-rs)
(dntop offset5 "5 bit unsigned immediate"  () h-uint f-offset5)
(dntop rn      "2nd source register"       () h-gr-t f-rn)
(dntop offset3 "3 bit unsigned immediate"  () h-uint f-offset3)
(dntop offset8 "8 bit unsigned immediate"  () h-uint f-offset8)

(dntop bit10-rd "rd in bits 10,9,8"        () h-gr-t f-bit10-rd)

(dntop ro      "offset register"           () h-gr-t f-ro)
(dntop rb      "base register"             () h-gr-t f-rb)

; Instruction definitions.

; Cover macro to dni to indicate these are all Thumb insns.
; dnti: define-normal-thumb-insn

(define-pmacro (dnti xname xcomment xattrs xsyntax xformat xsemantics)
  (define-insn
    (name xname)
    (comment xcomment)
    (.splice attrs (.unsplice xattrs) (ISA thumb))
    (syntax xsyntax)
    (format xformat)
    (semantics xsemantics)
    )
)

; Move shifted register insns.

(dntf f-shift-op "Move shifted register opcode" () 12 2)

(dnti lsl "logical shift left"
      ()
      "lsl $rd,$rs,#$offset5"
      (+ (f-op3 0) (f-shift-op 0) offset5 rs rd)
      (sequence ((BI carry-out))
		(set carry-out
		     (c-call BI "compute_carry_out_immshift" rs
			     SHIFT-TYPE-lsl offset5 cbit))
		(set rd (sll rs offset5))
		(set-logical-cc rd carry-out))
)
(dnti lsr "logical shift right"
      ()
      "lsr $rd,$rs,#$offset5"
      (+ (f-op3 0) (f-shift-op 1) offset5 rs rd)
      (sequence ((BI carry-out))
		(set carry-out
		     (c-call BI "compute_carry_out_immshift" rs
			     SHIFT-TYPE-lsr offset5 cbit))
		(set rd (srl rs offset5))
		(set-logical-cc rd carry-out))
)
(dnti asr "arithmetic shift right"
      ()
      "asr $rd,$rs,#$offset5"
      (+ (f-op3 0) (f-shift-op 2) offset5 rs rd)
      (sequence ((BI carry-out))
		(set carry-out
		     (c-call BI "compute_carry_out_immshift" rs
			     SHIFT-TYPE-asr offset5 cbit))
		(set rd (sra rs offset5))
		(set-logical-cc rd carry-out))
)

; Add/subtract insns.

(dntf f-i "immediate indicator in add/sub insns" () 10 1)

(dntf f-addsub-op "Add/subtract opcode" () 9 1)

(dnti add "add reg+reg"
      ()
      "add $rd,$rs,$rn"
      (+ (f-op5 3) (f-i 0) (f-addsub-op 0) rn rs rd)
      (sequence ()
		(set-add-flags rs rn 0)
		(set rd (add rs rn)))
)
(dnti addi "add reg+imm"
      ()
      "add $rd,$rs,#$offset3"
      (+ (f-op5 3) (f-i 1) (f-addsub-op 0) offset3 rs rd)
      (sequence ()
		(set-add-flags rs offset3 0)
		(set rd (add rs offset3)))
)
(dnti sub "sub reg+reg"
      ()
      "sub $rd,$rs,$rn"
      (+ (f-op5 3) (f-i 0) (f-addsub-op 1) rn rs rd)
      (sequence ()
		(set-sub-flags rs rn 1)
		(set rd (sub rs rn)))
)
(dnti subi "sub reg+imm"
      ()
      "sub $rd,$rs,#$offset3"
      (+ (f-op5 3) (f-i 1) (f-addsub-op 1) offset3 rs rd)
      (sequence ()
		(set-sub-flags rs offset3 1)
		(set rd (sub rs offset3)))
)

; Move/compare/add/subtract immediate insns.

(dntf f-mcasi-op "Move/compare/add/subtract immediate opcode" () 12 2)

(dnti mov "move imm->reg"
      ()
      "mov ${bit10-rd},#$offset8"
      (+ (f-op3 1) (f-mcasi-op 0) bit10-rd offset8)
      (sequence ()
		(set bit10-rd offset8)
		(set-zn-flags bit10-rd))
)
(dnti cmp "cmp reg,imm"
      ()
      "cmp ${bit10-rd},#$offset8"
      (+ (f-op3 1) (f-mcasi-op 1) bit10-rd offset8)
      (set-sub-flags bit10-rd offset8 1)
)
(dnti addi8 "add 8 bit immediate"
      ()
      "add ${bit10-rd},#$offset8"
      (+ (f-op3 1) (f-mcasi-op 2) bit10-rd offset8)
      (sequence ()
		(set-add-flags bit10-rd offset8 0)
		(set bit10-rd (add bit10-rd offset8)))
)
(dnti subi8 "sub 8 bit immediate"
      ()
      "sub ${bit10-rd},#$offset8"
      (+ (f-op3 1) (f-mcasi-op 3) bit10-rd offset8)
      (sequence ()
		(set-sub-flags bit10-rd offset8 1)
		(set bit10-rd (sub bit10-rd offset8)))
)

; ALU operations.

(dntf f-alu-op "ALU opcode" () 9 4)

(define-pmacro (alu-logical-op mnemonic comment alu-opcode sem-fn)
  (dnti (.sym alu- mnemonic) comment
	()
	(.str mnemonic " $rd,$rs")
	(+ (f-op6 #x10) (f-alu-op alu-opcode) rs rd)
	(sequence ()
		  (set rd (sem-fn rd rs))
		  (set-zn-flags rd))
	)
)

(define-pmacro (alu-arith-op mnemonic comment alu-opcode sem-fn set-flags)
  (dnti (.sym alu- mnemonic) comment
	()
	(.str mnemonic " $rd,$rs")
	(+ (f-op6 #x10) (f-alu-op alu-opcode) rs rd)
	(sequence ((SI result))
		  (set result (sem-fn rd rs cbit))
		  (set-flags rd rs cbit)
		  (set rd result))
	)
)

(define-pmacro (alu-shift-op mnemonic comment alu-opcode sem-fn shift-type)
  (dnti (.sym alu- mnemonic) comment
	()
	(.str mnemonic " $rd,$rs")
	(+ (f-op6 #x10) (f-alu-op alu-opcode) rs rd)
	(sequence ((BI carry-out) (SI result))
		  (set carry-out
		       (c-call BI "compute_carry_out_regshift"
			       rd shift-type rs cbit))
		  (set result (sem-fn rd rs))
		  (set rd result)
		  (set-logical-cc result carry-out))
	)
)

(alu-logical-op and "and" 0 and)
(alu-logical-op eor "xor" 1 xor)

(alu-shift-op lsl "logical shift left" 2 sll SHIFT-TYPE-lsl)
(alu-shift-op lsr "logical shift right" 3 srl SHIFT-TYPE-lsr)
(alu-shift-op asr "arithmetic shift right" 4 sra SHIFT-TYPE-asr)
(alu-shift-op ror "rotate right" 7 ror SHIFT-TYPE-ror)

(alu-arith-op adc "add with carry" 5
	(.pmacro (rd rs cbit) (addc rd rs cbit))
	(.pmacro (rd rs cbit) (set-add-flags rd rs cbit)))
(alu-arith-op sbc "subtract with carry (borrow)" 6
	(.pmacro (rd rs cbit) (subc rd rs (not cbit)))
	(.pmacro (rd rs cbit) (set-sub-flags rd rs cbit)))

(dnti alu-tst "test"
      ()
      "tst $rd,$rs"
      (+ (f-op6 #x10) (f-alu-op 8) rs rd)
      (sequence ((SI x))
		(set x (and rd rs))
		(set-zn-flags x))
)

(alu-arith-op neg "negate" 9
	      (.pmacro (rd rs cbit) (neg rs))
	      (.pmacro (rd rs cbit) (set-sub-flags 0 rs 1)))

(dnti alu-cmp "compare"
      ()
      "cmp $rd,$rs"
      (+ (f-op6 #x10) (f-alu-op 10) rs rd)
      (set-sub-flags rd rs 1)
)
(dnti alu-cmn "compare negative"
      ()
      "cmn $rd,$rs"
      (+ (f-op6 #x10) (f-alu-op 11) rs rd)
      (set-add-flags rd rs 0)
)

(alu-logical-op orr "or" 12 or)

; use alu-logical-op 'cus it sets the condition codes the way we want
(alu-logical-op mul "multiply" 13 mul)

(alu-logical-op bic "bit clear" 14 (.pmacro (rd rs) (and rd (inv rs))))

(alu-logical-op mvn "invert" 15 (.pmacro (rd rs) (inv rs)))

; Hi register operations.
;
; R15 and PC are treated as two distinct registers.  It is assumed that the
; execution environment ensures R15 = PC+4.  All reads are taken from R15.
; All writes are written to PC.

(define-hardware
  (name h-hiregs)
  (comment "High registers (R8-R15)")
  (attrs (ISA thumb) VIRTUAL)
  (type register WI (8))
  (indices keyword ""
	   ((r8 0) (r9 1) (r10 2) (r11 3) (r12 4) (r13 5) (r14 6) (r15 7)))
  ; ??? Accesses won't be as efficient as possible as +8 calculation will
  ; get done at exec time (could be defered to extract phase), but that's an
  ; optimization that can be generally useful in the extract phase.
  (get (regno) (reg h-gr (add regno (const 8))))
  (set (regno newval) (set (reg h-gr (add regno (const 8))) newval))
)


(dntf f-hireg-op  "Hi register opcode" () 9 2)

(dntop hs "high source register"      () h-hiregs f-rs)
(dntop hd "high destination register" () h-hiregs f-rd)

(define-pmacro (hireg-op mnemonic
			 lo-op-hi-comment
			 hi-op-lo-comment
			 hi1-op-hi2-comment
			 opcode
			 lo-dest-sem-fn
			 hi-dest-sem-fn)
  (begin
    (dnti (.sym mnemonic -rd-hs)
	  lo-op-hi-comment
	  ()
	  (.str mnemonic " $rd,$hs")
	  (+ (f-op6 #x11) (f-hireg-op opcode) (f-h1 0) (f-h2 1) hs rd)
	  (lo-dest-sem-fn rd hs)
	  )
    (dnti (.sym mnemonic -hd-rs)
	  hi-op-lo-comment
	  ()
	  (.str mnemonic " $hd,$rs")
	  (+ (f-op6 #x11) (f-hireg-op opcode) (f-h1 1) (f-h2 0) hd rs)
	  (hi-dest-sem-fn hd rs)
	  )
    (dnti (.sym mnemonic -hd-hs)
	  hi1-op-hi2-comment
	  ()
	  (.str mnemonic " $hd,$hs")
	  (+ (f-op6 #x11) (f-hireg-op opcode) (f-h1 1) (f-h2 1) hd hs)
	  (hi-dest-sem-fn hd hs)
	  )
    )
)

(hireg-op add "lo = lo + hi" "hi = hi + lo" "hi = hi + hi2" 0
	  (.pmacro (src1-dest src2) (set src1-dest (add src1-dest src2)))
	  (.pmacro (src1-dest src2)
		   (if (eq (regno src1-dest) 7)
		       (set pc (add src1-dest src2))
		       (set src1-dest (add src1-dest src2))))
)

(hireg-op cmp "compare lo,hi" "compare hi,lo" "compare hi1,hi2" 1
	  (.pmacro (src1 src2) (set-sub-flags src1 src2 1))
	  (.pmacro (src1 src2) (set-sub-flags src1 src2 1))
)

(hireg-op mov "lo = hi" "hi = lo" "hi1 = hi2" 2
	  (.pmacro (dest src) (set dest src))
	  (.pmacro (dest src)
		   (if (eq (regno dest) 7)
		       (set pc src)
		       (set dest src)))
)

(dnti bx-rs "bx on lo reg"
      ()
      "bx $rs"
      (+ (f-op6 #x11) (f-hireg-op 3) (f-h1 0) (f-h2 0) (f-rd 0) rs)
      (sequence ()
		(set pc rs)
		(if (not (and rs 1))
		    (set (reg h-tbit) 0)))
)
(dnti bx-hs "bx on hi reg"
      ()
      "bx $hs"
      (+ (f-op6 #x11) (f-hireg-op 3) (f-h1 0) (f-h2 1) (f-rd 0) hs)
      (sequence ()
		(set pc hs)
		(if (not (and hs 1))
		    (set (reg h-tbit) 0)))
)

; PC relative load.

(df f-word8 "10 bit unsigned offset, right shifted by 2"
    ((ISA thumb))
    7 8 UINT
    ((value pc) (srl WI value (const 2)))
    ((value pc) (sll WI value (const 2)))
)

(dntop word8 "10 bit unsigned immediate" () h-uint f-word8)

(dnti ldr-pc "pc relative load"
      ()
      "ldr ${bit10-rd},[pc,#$word8]"
      (+ (f-op5 9) bit10-rd word8)
      (set bit10-rd
	   (mem WI (add (and (add pc (const 4)) (const WI -4)) word8)))
)

; Load/store with register offset.

(dnti str "store word"
      ()
      "str $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-l 0) (f-b 0) (f-bit9 0) ro rb rd)
      (set (mem WI (add rb ro)) rd)
)
(dnti strb "store byte"
      ()
      "strb $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-l 0) (f-b 1) (f-bit9 0) ro rb rd)
      (set (mem QI (add rb ro)) rd)
)
(dnti ldr "load word"
      ()
      "ldr $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-l 1) (f-b 0) (f-bit9 0) ro rb rd)
      (set rd (mem WI (add rb ro)))
)
(dnti ldrb "load zero extended byte"
      ()
      "ldrb $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-l 1) (f-b 1) (f-bit9 0) ro rb rd)
      (set rd (zext SI (mem QI (add rb ro))))
)

; Load/store sign-extended byte/halfword.

(dntf f-s  "signed/unsigned indicator" () 10 1)

(dnti strh "store halfword"
      ()
      "strh $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-h 0) (f-s 0) (f-bit9 1) ro rb rd)
      (set (mem HI (add rb ro)) rd)
)
(dnti ldrh "load zero extended halfword"
      ()
      "ldrh $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-h 1) (f-s 0) (f-bit9 1) ro rb rd)
      (set rd (zext SI (mem HI (add rb ro))))
)
(dnti ldsb "load sign extended byte"
      ()
      "ldsb $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-h 0) (f-s 1) (f-bit9 1) ro rb rd)
      (set rd (ext SI (mem QI (add rb ro))))
)
(dnti ldsh "load sign extended halfword"
      ()
      "ldsh $rd,[$rb,$ro]"
      (+ (f-op4 5) (f-h 1) (f-s 1) (f-bit9 1) ro rb rd)
      (set rd (ext SI (mem HI (add rb ro))))
)

; Load/store with immediate offset.

(dntf f-b-imm "byte/word indicator in load/store with immediate offset insns" () 12 1)

(df f-offset5-7 "offset5 field as 7 bit unsigned immediate"
    ((ISA thumb))
    10 5 UINT
    ((value pc) (srl WI value (const 2)))
    ((value pc) (sll WI value (const 2)))
)

(dntop offset5-7 "offset5 as 7 bit unsigned immediate" () h-uint f-offset5-7)

(dnti str-imm "store word with immediate offset"
      ()
      "str $rd,[$rb,#${offset5-7}]"
      (+ (f-op3 3) (f-b-imm 0) (f-l 0) offset5-7 rb rd)
      (set (mem WI (add rb offset5-7)) rd)
)
(dnti ldr-imm "load word with immediate offset"
      ()
      "ldr $rd,[$rb,#${offset5-7}]"
      (+ (f-op3 3) (f-b-imm 0) (f-l 1) offset5-7 rb rd)
      (set rd (mem WI (add rb offset5-7)))
)
(dnti strb-imm "store byte with immediate offset"
      ()
      "strb $rd,[$rb,#$offset5]"
      (+ (f-op3 3) (f-b-imm 1) (f-l 0) offset5 rb rd)
      (set (mem QI (add rb offset5)) rd)
)
(dnti ldrb-imm "load zero extended byte with immediate offset"
      ()
      "ldrb $rd,[$rb,#$offset5]"
      (+ (f-op3 3) (f-b-imm 1) (f-l 1) offset5 rb rd)
      (set rd (zext SI (mem QI (add rb offset5))))
)

; Load/store halfword with immediate offset.

(df f-offset5-6 "offset5 field as 6 bit unsigned immediate"
    ((ISA thumb))
    10 5 UINT
    ((value pc) (srl WI value (const 1)))
    ((value pc) (sll WI value (const 1)))
)

(dntop offset5-6 "offset5 as 7 bit unsigned immediate" () h-uint f-offset5-6)

(dnti strh-imm "store halfword with immediate offset"
      ()
      "strh $rd,[$rb,#${offset5-6}]"
      (+ (f-op4 8) (f-l 0) offset5-6 rb rd)
      (set (mem HI (add rb offset5-6)) rd)
)
(dnti ldrh-imm "load zero extended halfword with immediate offset"
      ()
      "ldrh $rd,[$rb,#${offset5-6}]"
      (+ (f-op4 8) (f-l 1) offset5-6 rb rd)
      (set rd (zext WI (mem HI (add rb offset5-6))))
)

; SP-relative load/store

(dnti str-sprel "store word, sp-relative"
      ()
      "str ${bit10-rd},[sp,#$word8]"
      (+ (f-op4 9) (f-l 0) bit10-rd word8)
      (set (mem WI (add sp word8)) bit10-rd)
)
(dnti ldr-sprel "load word, sp-relative"
      ()
      "ldr ${bit10-rd},[sp,#$word8]"
      (+ (f-op4 9) (f-l 1) bit10-rd word8)
      (set bit10-rd (mem WI (add sp word8)))
)

; Load address

(dntf f-sp "sp/pc indicator" () 11 1)

(dnti lda-pc "load address from pc"
      ()
      "add ${bit10-rd},pc,$word8"
      (+ (f-op4 10) (f-sp 0) bit10-rd word8)
      (set bit10-rd (add (and (add pc (const 4)) (const WI -4)) word8))
)
(dnti lda-sp "load address from sp"
      ()
      "add ${bit10-rd},sp,$word8"
      (+ (f-op4 10) (f-sp 1) bit10-rd word8)
      (set bit10-rd (add sp word8))
)

; Add offset to stack pointer.
; FIXME: Handling of sign+magnitude needs revisiting.
; If expressions are allowed here we can't assume "-" follows "#".

(dntf f-addoff-s "s bit in add offset to sp insns" () 7 1)

(df f-sword7 "7 bit magnitude, accompanies sign bit"
    ((ISA thumb))
    6 7 UINT
    ((value pc) (srl WI value (const 2)))
    ((value pc) (sll WI value (const 2)))
)

(dntop sword7 "7 bit magnitude, accompanies sign bit" () h-uint f-sword7)

(dnti add-sp "add offset to sp"
      ()
      "add sp,#$sword7"
      (+ (f-op8 #xb0) (f-addoff-s 0) sword7)
      (set sp (add sp sword7))
)
(dnti sub-sp "subtract offset from sp"
      ()
      "add sp,#-$sword7"
      (+ (f-op8 #xb0) (f-addoff-s 1) sword7)
      (set sp (sub sp sword7))
)

; Push/pop registers.

; FIXME: Might be better to use sequence temp as address reg.

(define-pmacro (push-reg regno)
  (if (and rlist (sll 1 regno))
      (sequence ()
		(set sp (sub sp 4))
		(set (mem WI sp) (reg WI h-gr-t regno))
		))
)
(define-pmacro (pop-reg regno)
  (if (and rlist (sll 1 regno))
      (sequence ()
		(set (reg WI h-gr-t regno) (mem WI sp))
		(set sp (add sp 4))
		))
)

(dntf f-pushpop-op "opcode bits 10,9 in push/pop insns" () 10 2)

(dntf f-r "register indicator in push/pop insns" () 8 1)

(dntf f-rlist "register list" () 7 8)

; ??? Print/parse handler specs missing.  Later.
(dntop rlist "register list" () h-uint f-rlist)
(dntop rlist-lr "register list with lr" () h-uint f-rlist)
(dntop rlist-pc "register list with pc" () h-uint f-rlist)

(dnti push "push registers"
      ()
      "push {$rlist}"
      (+ (f-op4 11) (f-l 0) (f-pushpop-op 2) (f-r 0) rlist)
      (.splice sequence () (.unsplice (.map push-reg (.iota 8 7 -1))))
)
(dnti push-lr "push registers and lr"
      ()
      "push {${rlist-lr}}"
      (+ (f-op4 11) (f-l 0) (f-pushpop-op 2) (f-r 1) rlist)
      (.splice sequence ()
	       (set sp (sub sp 4))
	       (set (mem WI sp) lr)
	       (.unsplice (.map push-reg (.iota 8 7 -1)))
	       )
)

(dnti pop "pop registers"
      ()
      "pop {$rlist}"
      (+ (f-op4 11) (f-l 1) (f-pushpop-op 2) (f-r 0) rlist)
      (.splice sequence () (.unsplice (.map pop-reg (.iota 8))))
)
(dnti pop-pc "pop registers and pc"
      ()
      "pop {${rlist-pc}}"
      (+ (f-op4 11) (f-l 1) (f-pushpop-op 2) (f-r 1) rlist)
      (.splice sequence ()
	       (.unsplice (.map pop-reg (.iota 8)))
	       (set pc (mem WI sp))
	       (set sp (add sp 4))
	       )
)

; Multiple load/store.

; FIXME: Might be better to use sequence temp as address reg.

(dntf f-bit10-rb "Rb at bit 10" () 10 3)

(dntop bit10-rb "base reg at bit 10" () h-gr-t f-bit10-rb)

(define-pmacro (save-reg-inc regno)
  (if (and rlist (sll 1 regno))
      (sequence ()
		(set (mem WI bit10-rb) (reg WI h-gr-t regno))
		(set bit10-rb (add bit10-rb 4))
		))
)
(define-pmacro (load-reg-inc regno)
  (if (and rlist (sll 1 regno))
      (sequence ()
		(set (reg WI h-gr-t regno) (mem WI bit10-rb))
		(set bit10-rb (add bit10-rb 4))
		))
)

(dnti stmia "store multiple"
      ()
      "stmia $rb!,{$rlist}"
      (+ (f-op4 12) (f-l 0) bit10-rb rlist)
      (.splice sequence () (.unsplice (.map save-reg-inc (.iota 8))))
)
(dnti ldmia "load multiple"
      ()
      "ldmia $rb!,{$rlist}"
      (+ (f-op4 12) (f-l 1) bit10-rb rlist)
      (.splice sequence () (.unsplice (.map load-reg-inc (.iota 8))))
)

; Conditional branches.

(dntf f-cond "condition code spec" () 11 4)

; The standard condition code tests.

(define-normal-insn-enum cc-tests
  "condition code tests"
  ((ISA thumb)) "" f-cond
  (
   (CC_EQ 0)   ; equal
   (CC_NE 1)   ; not equal
   (CC_CS 2)   ; carry set (unsigned greater or equal)
   (CC_CC 3)   ; carry clear (unsigned less than)
   (CC_MI 4)   ; minus (negative)
   (CC_PL 5)   ; positive or zero
   (CC_VS 6)   ; overflow set
   (CC_VC 7)   ; overflow clear
   (CC_HI 8)   ; higher (unsigned greater)
   (CC_LS 9)   ; less or same (unsigned less or equal)
   (CC_GE 10)  ; greater or equal
   (CC_LT 11)  ; less
   (CC_GT 12)  ; greater
   (CC_LE 13)  ; less or equal
   )
)

(df  f-soffset8 "8 bit pc relative branch address"
     (PCREL-ADDR (ISA thumb))
     7 8 INT
     ((value pc) (sra WI (sub WI value (add WI pc (const 4))) (const 1)))
     ((value pc) (add WI (sll WI value (const 1)) (add WI pc (const 4))))
)

(dntop soffset8 "8 bit pc relative branch address" () h-iaddr f-soffset8)

(define-pmacro (cbranch bname comment cond test)
  (dnti bname (.str "branch if " comment)
	()
	(.str bname " $soffset8")
	(+ (f-op4 13) cond soffset8)
	(if (test)
	    (set pc soffset8))
	)
)
(cbranch beq  "eq"                    CC_EQ test-eq)
(cbranch bne  "ne"                    CC_NE test-ne)
(cbranch bcs  "cs (ltu)"              CC_CS test-cs)
(cbranch bcc  "cc (geu)"              CC_CC test-cc)
(cbranch bmi  "mi (negative)"         CC_MI test-mi)
(cbranch bpl  "pl (positive or zero)" CC_PL test-pl)
(cbranch bvs  "vs (overflow set)"     CC_VS test-vs)
(cbranch bvc  "vc (overflow clear)"   CC_VC test-vc)
(cbranch bhi  "hi (gtu)"              CC_HI test-hi)
(cbranch bls  "ls (leu)"              CC_LS test-ls)
(cbranch bge  "ge"                    CC_GE test-ge)
(cbranch blt  "lt"                    CC_LT test-lt)
(cbranch bgt  "gt"                    CC_GT test-gt)
(cbranch ble  "le"                    CC_LE test-le)

; Software interrupt.

(dntf f-value8 "8 bit value for swi" () 7 8)

(dntop value8 "8 bit value for swi" () h-uint f-value8)

(dnti swi "software interrupt"
      ()
      "swi $value8"
      (+ (f-op8 #xdf) value8)
      ; FIXME: for now
      (set pc (c-call WI "thumb_swi" pc value8))
)

; Unconditional branch.

(df f-offset11 "11 bit pc relative branch address"
    (PCREL-ADDR (ISA thumb))
    10 11 INT
    ((value pc) (sra WI (sub value (add pc (const 4))) (const 1)))
    ((value pc) (add WI (sll value (const 1)) (add pc (const 4))))
)

(dntop offset11 "11 bit pc relative branch address" () h-iaddr f-offset11)

(dnti b "unconditional branch"
      ()
      "b $offset11"
      (+ (f-op5 #x1c) offset11)
      (set pc offset11)
)

; Long branch with link.
; Two instructions that make up a subroutine call.
; FIXME: Assembler access is via one insn - macro-insn?
; Left for later, as is all assembly considerations.

(dntf f-lbwl-h "long branch with link `h' field"   () 11 1)

; This one is signed.
(define-ifield
  (name f-lbwl-hi)
  (comment "long branch with link offset, high part")
  (attrs (ISA thumb))
  (mode INT)
  (start 10)
  (length 11)
)
(dntop lbwl-hi "long branch with link offset, high part" ()
       h-sint f-lbwl-hi)

; This one is unsigned.
(dntf f-lbwl-lo "long branch with link offset, low part" () 10 11)
(dntop lbwl-lo "long branch with link offset, low part" ()
       h-uint f-lbwl-lo)

(dnti bl-hi "branch link, high offset"
      ()
      "bl-hi ${lbwl-hi}"
      (+ (f-op4 15) (f-lbwl-h 0) lbwl-hi)
      (set lr (add (add pc 4) (sll lbwl-hi 12)))
)

(dnti bl-lo "branch link, low offset"
      ()
      "bl-lo ${lbwl-lo}"
      (+ (f-op4 15) (f-lbwl-h 1) lbwl-lo)
      (sequence ((WI cur-pc))
		(set cur-pc pc)
		(set pc (add lr (sll lbwl-lo 1)))
		(set lr (or (add cur-pc 2) 1)))
)
